Pycxx Mod

http://mathpad.wikidot.com/pycxx-handlers
http://mathpad.wikidot.com/pycxx-support
These two belong together, as support is a subsection.
Over 500 lines of repetitive handler code in cxx_extensions.cxx
PythonType contains method-tables:

// PythonType.hxx
:
namespace Py
{
    class PythonType
    {
    public:
        :
        PythonType &supportSequenceType( void );
        PythonType &supportMappingType( void ); // <-- CHASE THIS ONE!
        PythonType &supportNumberType( void );
        PythonType &supportBufferType( void );
        :
        PyTypeObject            *table;
        PySequenceMethods       *sequence_table;
        PyMappingMethods        *mapping_table;
        PyNumberMethods         *number_table;
        PyBufferProcs           *buffer_table;

All of those types are Python-types, defined in /include/Python/object.h or wherever Python's object.h is.

Chasing supportMappingType(), we encounter this singleton:

// cxx_extentions.cxx
:
// Mapping
PythonType &PythonType::supportMappingType()
{
    if( !mapping_table )
    {
        mapping_table = new PyMappingMethods;
        memset( mapping_table, 0, sizeof( PyMappingMethods ) );   // ensure new fields are 0
        table->tp_as_mapping            = mapping_table;

        mapping_table->mp_length        = mapping_length_handler; // <-- CHASE THIS ONE!
        mapping_table->mp_subscript     = mapping_subscript_handler;
        mapping_table->mp_ass_subscript = mapping_ass_subscript_handler;    // BAS setup seperately?
    }
    return *this;
}

Searching for mapping_length_handler yields:

// ... still cxx_extentions.cxx
:
extern "C"
{
    static Py_ssize_t mapping_length_handler( PyObject * );
}
:
extern "C" Py_ssize_t mapping_length_handler( PyObject *self )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return p->mapping_length();
    }
    catch( Py::Exception & )
    {
        return -1;    // indicate error
    }
}

The first parameter to every handler is 'PyObject* self'; PyObject is from Python.h

By converting it to a PythonExtensionBase *, this suggests (if I understand this correctly) that PythonExtensionBase is attempting to mirror exactly the structure of PyObject. i.e. This is the bridge at the lowest level. in Python, everything is a PyObject.

I can't see exactly what purpose these handlers achieve. So they typecast a PyObject* into its corresponding PythonExtensionBase*, and then call a particular one of its methods.

For the example I gave, it is mapping_length()

Searching for this,

// ExtentionTypeBase.hxx
:
class PythonExtensionBase : public PyObject
    {
    public:
        :
        // Mapping
        virtual int     mapping_length();
        virtual Object  mapping_subscript(      const Object & );
        virtual int     mapping_ass_subscript(  const Object &, const Object & );

// cxx_extentions.cxx
:
// Mapping
int PythonExtensionBase::mapping_length()
{
    missing_method( mapping_length );
    return -1;
}
:
#define missing_method( method ) \
    throw RuntimeError( "Extension object missing implement of " #method );

// Exception.hxx
    :
    class Exception
    {
    public:
        Exception( ExtensionExceptionType &exception, const std::string &reason );
        Exception( ExtensionExceptionType &exception, Object &reason );

        explicit Exception ()
        { }

        Exception (const std::string &reason)
        {
            PyErr_SetString( Py::_Exc_RuntimeError(), reason.c_str() );
        }

        Exception( PyObject *exception, const std::string &reason )
        {
            PyErr_SetString( exception, reason.c_str() );
        }

        Exception( PyObject *exception, Object &reason );        

        void clear() // clear the error
        // technically but not philosophically const
        {
            PyErr_Clear();
        }
    };
    :
    // Abstract
    class StandardError: public Exception
    {
    protected: 
        explicit StandardError()
        { }
    };
    :
    class RuntimeError: public StandardError
    {
    public:
        RuntimeError (const std::string& reason)
        : StandardError()
        {
            PyErr_SetString( Py::_Exc_RuntimeError(), reason.c_str() );
        }
    };

Ok so the deal is that the default implementation of PythonExtensionBase::mapping_length() is going to throw an error that whoever is deriving from PythonExtensionBase has failed to implement this particular function.

So this kind of implies that every PythonExtension class will have to provide its own implementation of mapping_length().
But only if 'supportMappingType( void );' gets run.

That would crank up the above singleton. But the Singleton only assigns the function pointer for the handler.

It won't be until something asks for mapping_table->mp_length()
which calls mapping_length()
Then missing_method( mapping_length )
Finally PyErr_SetString( Py::_Exc_RuntimeError(), reason.c_str() );

So this triggers an exception within Python.

Which will probably in term trigger an exception in our own C++ code, which is why we need this error handler. The error handler basically ignores the error and returns -1.


http://mathpad.wikidot.com/pycxx-variadic-templates
http://mathpad.wikidot.com/pycxx-extention-base

http://mathpad.wikidot.com/pycxx-pyinvoke

http://mathpad.wikidot.com/pycxx-operators

http://mathpad.wikidot.com/pycxx-debugmacro

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License