Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the purpose of a struct template without definition?

Below is a snippet from Boost.Python's source code:

template <class T> struct null_ok; // how's it working?

template <class T>
inline null_ok<T>* allow_null(T* p)
{
    return (null_ok<T>*)p;
}

It's wired that there's no definition for forward declared struct null_ok, and null_ok has nothing to do with the template argument T.

In Python wiki, some hints given that:

handle<> y(null_ok(x)) allows y to become NULL

handle<> y(x), where x is not the result of null_ok, never results in a NULL y. An exception will be thrown if x is NULL

I can't figure out how the declaration (without definition) of a struct template, null_ok, could achieve the purpose as stated above?

like image 500
nn0p Avatar asked Jan 29 '15 07:01

nn0p


2 Answers

The point is to encode "it's ok for this pointer to be null" along with the original type in the type of the pointer itself.

Then, the function templates accepting the pointer can be overloaded to recognize null_ok<T>* pointers, and not error out on null pointers (while converting it back to a T*).

You do not need a definition for null_ok, since you can have pointers to incomplete types, and it prevents people from accidentally writing something like null_ok<int> a;.

like image 63
T.C. Avatar answered Nov 15 '22 01:11

T.C.


Exactly with the code in handle.hpp below your snippet (comments mine):

// this is a borrowed handle which can be null,
// so increment refcount if p is not null
// (xincref just does nothing in this case)
template <class T>
inline T* manage_ptr(detail::borrowed<null_ok<T> >* p, int)
{
  return python::xincref((T*)p);
}

// the same - as stated in the doc, null_ok and borrowed are commutative
template <class T>
inline T* manage_ptr(null_ok<detail::borrowed<T> >* p, int)
{
  return python::xincref((T*)p);
}


// this is a borrowed handle which cannot be null, so make sure p is not null
// and increment refcount.
template <class T>
inline T* manage_ptr(detail::borrowed<T>* p, long)
{
  return python::incref(expect_non_null((T*)p));
}

// p is not borrowed and can be null - do nothing.
template <class T>
inline T* manage_ptr(null_ok<T>* p, long)
{
  return (T*)p;
}

// p is not borrowed and cannot be null - make sure it isn't null indeed.
template <class T>
inline T* manage_ptr(T* p, ...)
{
  return expect_non_null(p);
}

null_ok<T> doesn't have to be complete for this code to compile, so it's just declared, not defined. Making it complete would just add extra work for compiler.

like image 44
Anton Savin Avatar answered Nov 14 '22 23:11

Anton Savin