|
Re: Curiously Recurring Generic Pattern
|
Posted: Nov 6, 2005 11:25 AM
|
|
This method allows me do do a very neat trick. I don't remember all the specifics, but this was the only way I managed to do this thing.
I have a Python application and I've extended it with about 10 types implemented in C++. As you probably know, the code to implement a Python type in C contains a lot of boilerplate. I managed to eliminate a lot of it so that a very simple class can now look like the one below:
class CriticalSection : public PyUtil::Type<CriticalSection> { public: static bool TypeInit();
CriticalSection(); ~CriticalSection();
private: PyObject* Acquire(); PyObject* Release();
private: CRITICAL_SECTION m_CriticalSection; };
PYUTIL_TYPE_IMPLEMENT(_px, CriticalSection)
bool CriticalSection::TypeInit() { PYUTIL_TYPE_METHOD_NOARGS(Acquire) PYUTIL_TYPE_METHOD_NOARGS(Release) return true; }
CriticalSection::CriticalSection() { InitializeCriticalSection(&m_CriticalSection); }
CriticalSection::~Critica lSection() { DeleteCriticalSection(&m_CriticalSection); }
PyObject* CriticalSection::Acquire() { PYUTIL_THREAD_SAVE; EnterCriticalSection(&m_CriticalSection); PYUTIL_THREAD_LOAD;
Py_RETURN_NONE; }
PyObject* CriticalSection::Release() { PYUTIL_THREAD_SAVE; LeaveCriticalSection(&m_CriticalSection); PYUTIL_THREAD_LOAD;
Py_RETURN_NONE; } PyUtil::Type is declared like this:
namespace PyUtil {
template <typename T> class Type { ... {
} CRTP is used in this class to decide which methods to override. This is a bit tricky to explain without showing the code. Basically I'm doing this: PyUtil::Type implements a lot of methods corresponding to the Python C/API handlers: init, clear, traverse, richCompare, sequenceLength, sequenceItem, sequenceSlice. But the actual type I'm implementing might not support these (like the CriticalSection object from above, which is not a sequence). To decide if to set the corresponding field from the PyType object, I do this:
if (&_init != &T::_init) ms_Type.tp_init = _init; In derived classes which implement _init, this test will succeed and we will add an entry for this method.
I tried doing this without CRTP, but I didn't succeded (with or without virtual methods). I don't remember exactly why, but there were big problems (VS .NET 2003)
At that time I had no idea this pattern was called CRTP. It seemed natural to me to parametrize the base class using the actual final class.
PS: The CriticalSection type shown above doesn't actually need this, as it doesn't implement any special method. All other types I have do, but they are too long to show here. Also, PyUtil::Type's implementation has about 600 lines of complicated code (including a lot of macros to generate the default handlers), but it's worth it. On average, a Python type converted to use this method of implementation was about half the size of the original.
|
|