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?
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;
.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With