Pycxx Handlers

In Src/Python3/cxx_extensions.cxx we have ~520 lines of error handling code:

//--------------------------------------------------------------------------------
//
//    Handlers
//
//--------------------------------------------------------------------------------
PythonExtensionBase *getPythonExtensionBase( PyObject *self )
{
    if( self->ob_type->tp_flags&Py_TPFLAGS_BASETYPE )
    {
        PythonClassInstance *instance = reinterpret_cast<PythonClassInstance *>( self );
        return instance->m_pycxx_object;
    }
    else
    {
        return static_cast<PythonExtensionBase *>( self );
    }
}

#ifdef PYCXX_PYTHON_2TO3
extern "C" int print_handler( PyObject *self, FILE *fp, int flags )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return p->print( fp, flags );
    }
    catch( Py::Exception & )
    {
        return -1;    // indicate error
    }
}
#endif

extern "C" PyObject *getattr_handler( PyObject *self, char *name )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->getattr( name ) );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" int setattr_handler( PyObject *self, char *name, PyObject *value )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return p->setattr( name, Py::Object( value ) );
    }
    catch( Py::Exception & )
    {
        return -1;    // indicate error
    }
}

extern "C" PyObject *getattro_handler( PyObject *self, PyObject *name )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->getattro( Py::String( name ) ) );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" int setattro_handler( PyObject *self, PyObject *name, PyObject *value )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return p->setattro( Py::String( name ), Py::Object( value ) );
    }
    catch( Py::Exception & )
    {
        return -1;    // indicate error
    }
}

extern "C" PyObject *rich_compare_handler( PyObject *self, PyObject *other, int op )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->rich_compare( Py::Object( other ), op ) );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" PyObject *repr_handler( PyObject *self )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->repr() );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" PyObject *str_handler( PyObject *self )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->str() );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" Py_hash_t hash_handler( PyObject *self )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return p->hash();
    }
    catch( Py::Exception & )
    {
        return -1;    // indicate error
    }
}

extern "C" PyObject *call_handler( PyObject *self, PyObject *args, PyObject *kw )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        if( kw != NULL )
            return new_reference_to( p->call( Py::Object( args ), Py::Object( kw ) ) );
        else
            return new_reference_to( p->call( Py::Object( args ), Py::Object() ) );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" PyObject *iter_handler( PyObject *self )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->iter() );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" PyObject *iternext_handler( PyObject *self )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return p->iternext();  // might be a NULL ptr on end of iteration
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

// Sequence methods
extern "C" Py_ssize_t sequence_length_handler( PyObject *self )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return p->sequence_length();
    }
    catch( Py::Exception & )
    {
        return -1;    // indicate error
    }
}

extern "C" PyObject *sequence_concat_handler( PyObject *self, PyObject *other )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->sequence_concat( Py::Object( other ) ) );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" PyObject *sequence_repeat_handler( PyObject *self, Py_ssize_t count )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->sequence_repeat( count ) );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" PyObject *sequence_item_handler( PyObject *self, Py_ssize_t index )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->sequence_item( index ) );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" int sequence_ass_item_handler( PyObject *self, Py_ssize_t index, PyObject *value )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return p->sequence_ass_item( index, Py::Object( value ) );
    }
    catch( Py::Exception & )
    {
        return -1;    // indicate error
    }
}

// Mapping
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
    }
}

extern "C" PyObject *mapping_subscript_handler( PyObject *self, PyObject *key )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->mapping_subscript( Py::Object( key ) ) );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" int mapping_ass_subscript_handler( PyObject *self, PyObject *key, PyObject *value )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return p->mapping_ass_subscript( Py::Object( key ), Py::Object( value ) );
    }
    catch( Py::Exception & )
    {
        return -1;    // indicate error
    }
}

// Number
extern "C" PyObject *number_negative_handler( PyObject *self )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->number_negative() );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" PyObject *number_positive_handler( PyObject *self )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->number_positive() );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" PyObject *number_absolute_handler( PyObject *self )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->number_absolute() );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" PyObject *number_invert_handler( PyObject *self )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->number_invert() );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" PyObject *number_int_handler( PyObject *self )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->number_int() );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" PyObject *number_float_handler( PyObject *self )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->number_float() );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" PyObject *number_add_handler( PyObject *self, PyObject *other )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->number_add( Py::Object( other ) ) );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" PyObject *number_subtract_handler( PyObject *self, PyObject *other )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->number_subtract( Py::Object( other ) ) );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" PyObject *number_multiply_handler( PyObject *self, PyObject *other )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->number_multiply( Py::Object( other ) ) );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" PyObject *number_remainder_handler( PyObject *self, PyObject *other )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->number_remainder( Py::Object( other ) ) );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" PyObject *number_divmod_handler( PyObject *self, PyObject *other )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->number_divmod( Py::Object( other ) ) );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" PyObject *number_lshift_handler( PyObject *self, PyObject *other )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->number_lshift( Py::Object( other ) ) );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" PyObject *number_rshift_handler( PyObject *self, PyObject *other )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->number_rshift( Py::Object( other ) ) );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" PyObject *number_and_handler( PyObject *self, PyObject *other )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->number_and( Py::Object( other ) ) );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" PyObject *number_xor_handler( PyObject *self, PyObject *other )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->number_xor( Py::Object( other ) ) );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" PyObject *number_or_handler( PyObject *self, PyObject *other )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->number_or( Py::Object( other ) ) );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

extern "C" PyObject *number_power_handler( PyObject *self, PyObject *x1, PyObject *x2 )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return new_reference_to( p->number_power( Py::Object( x1 ), Py::Object( x2 ) ) );
    }
    catch( Py::Exception & )
    {
        return NULL;    // indicate error
    }
}

// Buffer
extern "C" int buffer_get_handler( PyObject *self, Py_buffer *buf, int flags )
{
    try
    {
        PythonExtensionBase *p = getPythonExtensionBase( self );
        return p->buffer_get( buf, flags );
    }
    catch( Py::Exception & )
    {
        return -1;    // indicate error
    }
}

extern "C" void buffer_release_handler( PyObject *self, Py_buffer *buf )
{
    PythonExtensionBase *p = getPythonExtensionBase( self );
    p->buffer_release( buf );
    // NOTE: No way to indicate error to Python
}

As each function follows the same form, we can use a macro to dramatically lower the line count and improve readability:

PythonExtensionBase *getPythonExtensionBase( PyObject *self )
{
    if( self->ob_type->tp_flags & Py_TPFLAGS_BASETYPE )
    {
        PythonClassInstance *instance = reinterpret_cast<PythonClassInstance *>( self );
        return instance->m_pycxx_object;
    }
    else
    {
        return static_cast<PythonExtensionBase *>( self );
    }
}

#define ADD_HANDLER( foo, ret, err ) \
    extern "C" foo \
    { \
        try \
        { \
            PythonExtensionBase *p = getPythonExtensionBase( self ); \
            return ret; \
        } \
        catch( Py::Exception & ) /* indicate error */ \
        { \
            return err;  \
        } \
    }

#define REF(x) new_reference_to( p -> x )

#ifdef PYCXX_PYTHON_2TO3
ADD_HANDLER(
            int print_handler( PyObject *self, FILE *fp, int flags ),
            p->print( fp, flags ),
            -1
            )
#endif

ADD_HANDLER(
            PyObject *getattr_handler( PyObject *self, char *name ),
            REF( getattr( name ) ),
            nullptr
            )

ADD_HANDLER(
            int setattr_handler( PyObject *self, char *name, PyObject *value ),
            p->setattr( name, Py::Object( value ) ),
            -1
            )
ADD_HANDLER(
            PyObject *getattro_handler( PyObject *self, PyObject *name ),
            REF( getattro( Py::String( name ) ) ),
            nullptr
            )

ADD_HANDLER(
            int setattro_handler( PyObject *self, PyObject *name, PyObject *value ),
            p->setattro( Py::String( name ), Py::Object( value ) ),
            -1
            )

ADD_HANDLER(
            PyObject *rich_compare_handler( PyObject *self, PyObject *other, int op ),
            REF( rich_compare( Py::Object( other ), op ) ),
            nullptr
            )

ADD_HANDLER(
            PyObject *repr_handler( PyObject *self ),
            REF( repr() ),
            nullptr
            )

ADD_HANDLER(
            PyObject *str_handler( PyObject *self ),
            REF( str() ),
            nullptr
            )

ADD_HANDLER(
            Py_hash_t hash_handler( PyObject *self ),
            p->hash(),
            -1
            )

ADD_HANDLER(
            PyObject *call_handler( PyObject *self, PyObject *args, PyObject *kw ),
            new_reference_to(
                             p->call(
                                     Py::Object( args ),
                                     kw ? Py::Object( kw ) : Py::Object()
                                     )
                             ),
            nullptr
            )

ADD_HANDLER(
            PyObject *iter_handler( PyObject *self ),
            REF( iter() ),
            nullptr
            )

ADD_HANDLER(
            PyObject *iternext_handler( PyObject *self ),
            p->iternext(), // might be a NULL ptr on end of iteration
            nullptr
            )

// Sequence methods
ADD_HANDLER(
            Py_ssize_t sequence_length_handler( PyObject *self ),
            p->sequence_length(),
            -1
            )

ADD_HANDLER(
            PyObject *sequence_concat_handler( PyObject *self, PyObject *other ),
            REF( sequence_concat( Py::Object( other ) ) ),
            nullptr
            )

ADD_HANDLER(
            PyObject *sequence_repeat_handler( PyObject *self, Py_ssize_t count ),
            REF( sequence_repeat( count ) ),
            nullptr
            )

ADD_HANDLER(
            PyObject *sequence_item_handler( PyObject *self, Py_ssize_t index ),
            REF( sequence_item( index ) ),
            nullptr
            )

ADD_HANDLER(
            int sequence_ass_item_handler( PyObject *self, Py_ssize_t index, PyObject *value ),
            p->sequence_ass_item( index, Py::Object( value ) ),
            -1
            )

// Mapping
ADD_HANDLER(
            Py_ssize_t mapping_length_handler( PyObject *self ),
            p->mapping_length(),
            -1
            )

ADD_HANDLER(
            PyObject *mapping_subscript_handler( PyObject *self, PyObject *key ),
            REF( mapping_subscript( Py::Object( key ) ) ),
            nullptr
            )

ADD_HANDLER(
            int mapping_ass_subscript_handler( PyObject *self, PyObject *key, PyObject *value ),
            p->mapping_ass_subscript( Py::Object( key ), Py::Object( value ) ),
            -1
            )

// Number
ADD_HANDLER(
            PyObject *number_negative_handler( PyObject *self ),
            REF( number_negative() ),
            nullptr
            )

ADD_HANDLER(
            PyObject *number_positive_handler( PyObject *self ),
            REF( number_positive() ),
            nullptr
            )

ADD_HANDLER(
            PyObject *number_absolute_handler( PyObject *self ),
            REF( number_absolute() ),
            nullptr
            )

ADD_HANDLER(
            PyObject *number_invert_handler( PyObject *self ),
            REF( number_invert() ),
            nullptr
            )

ADD_HANDLER(
            PyObject *number_int_handler( PyObject *self ),
            REF( number_int() ),
            nullptr
            )

ADD_HANDLER(
            PyObject *number_float_handler( PyObject *self ),
            REF( number_float() ),
            nullptr
            )

ADD_HANDLER(
            PyObject *number_add_handler( PyObject *self, PyObject *other ),
            REF( number_add( Py::Object( other ) ) ),
            nullptr
            )

ADD_HANDLER(
            PyObject *number_subtract_handler( PyObject *self, PyObject *other ),
            REF( number_subtract( Py::Object( other ) ) ),
            nullptr
            )

ADD_HANDLER(
            PyObject *number_multiply_handler( PyObject *self, PyObject *other ),
            REF( number_multiply( Py::Object( other ) ) ),
            nullptr
            )

ADD_HANDLER(
            PyObject *number_remainder_handler( PyObject *self, PyObject *other ),
            REF( number_remainder( Py::Object( other ) ) ),
            nullptr
            )

ADD_HANDLER(
            PyObject *number_divmod_handler( PyObject *self, PyObject *other ),
            REF( number_divmod( Py::Object( other ) ) ),
            nullptr
            )

ADD_HANDLER(
            PyObject *number_lshift_handler( PyObject *self, PyObject *other ),
            REF( number_lshift( Py::Object( other ) ) ),
            nullptr
            )

ADD_HANDLER(
            PyObject *number_rshift_handler( PyObject *self, PyObject *other ),
            REF( number_rshift( Py::Object( other ) ) ),
            nullptr
            )

ADD_HANDLER(
            PyObject *number_and_handler( PyObject *self, PyObject *other ),
            REF( number_and( Py::Object( other ) ) ),
            nullptr
            )

ADD_HANDLER(
            PyObject *number_xor_handler( PyObject *self, PyObject *other ),
            REF( number_xor( Py::Object( other ) ) ),
            nullptr
            )

ADD_HANDLER(
            PyObject *number_or_handler( PyObject *self, PyObject *other ),
            REF( number_or( Py::Object( other ) ) ),
            nullptr
            )

ADD_HANDLER(
            PyObject *number_power_handler( PyObject *self, PyObject *x1, PyObject *x2 ),
            REF( number_power( Py::Object( x1 ), Py::Object( x2 ) ) ),
            nullptr
            )

// Buffer
ADD_HANDLER(
            int buffer_get_handler( PyObject *self, Py_buffer *buf, int flags ),
            p->buffer_get( buf, flags ),
            -1
            )

extern "C" void buffer_release_handler( PyObject *self, Py_buffer *buf )
{
    PythonExtensionBase *p = getPythonExtensionBase( self );
    p->buffer_release( buf );
    // NOTE: No way to indicate error to Python
}

#undef ADD_HANDLER
#undef REF

There is still substantial duplication going on:

  • every single handler receives PyObject *self as the first parameter.
  • almost every handler falls into one of two categories:
  • … PyObject * returning nullptr
  • … int returning -1
#define ADD_HANDLER( type_, handlerfunc, return_expr, return_err ) \
    extern "C" handlerfunc \
    { \
        try \
        { \
            PythonExtensionBase *p = getPythonExtensionBase( self ); /* always the first parameter, see S below */ \
            return return_expr; \
        } \
        catch( Py::Exception & ) /* indicate error */ \
        { \
            return return_err;  \
        } \
    }

#define ADD_HANDLERo( handlerfunc, return_expr ) ADD_HANDLER( PyObject*, handlerfunc, return_expr, nullptr )
#define ADD_HANDLERi( handlerfunc, return_expr ) ADD_HANDLER( int      , handlerfunc, return_expr, -1 )

#define REF(x) new_reference_to( p -> x )
#define S PyObject *self
#define O PyObject *other

#ifdef PYCXX_PYTHON_2TO3
ADD_HANDLERi( print_handler                     ( S, FILE *fp, int flags )                , p->print(fp,flags)   )
#endif
ADD_HANDLERo( getattr_handler                   ( S, char *name )                         , REF(getattr(name))   )
ADD_HANDLERi( setattr_handler                   ( S, char *name, PyObject *value )        , p->setattr( name, Py::Object( value ) )   )
ADD_HANDLERo( getattro_handler                  ( S, PyObject *name )                     , REF(getattro(Py::String(name)))   )
ADD_HANDLERi( setattro_handler                  ( S, PyObject *name, PyObject *value )    , p->setattro( Py::String(name), Py::Object(value) )   )
ADD_HANDLERo( rich_compare_handler              ( S, O, int op )                          , REF(  rich_compare(Py::Object(other), op)  )   )
ADD_HANDLERo( repr_handler                      ( S )                                     , REF(repr())   )
ADD_HANDLERo( str_handler                       ( S )                                     , REF(str())   )
ADD_HANDLER( Py_hash_t, -1,
            hash_handler                        ( S )                                     , p->hash()   )
ADD_HANDLERo( call_handler                      ( S, PyObject *args, PyObject *kw )       , new_reference_to(  p->call( Py::Object(args), kw ? Py::Object(kw) : Py::Object() )  )    )
ADD_HANDLERo( iter_handler                      ( S )                                     , REF(iter())   )
ADD_HANDLERo( iternext_handler                  ( S )                                     , p->iternext()   )    // might be a NULL ptr on end of iteration
// Sequence methods
ADD_HANDLER( Py_ssize_t, -1,
            sequence_length_handler             ( S )                                     , p->sequence_length()   )
ADD_HANDLERo( sequence_concat_handler           ( S, O )                                  , REF(sequence_concat(Py::Object(other)))   )
ADD_HANDLERo( sequence_repeat_handler           ( S, Py_ssize_t count )                   , REF(sequence_repeat(count))   )
ADD_HANDLERo( sequence_item_handler             ( S, Py_ssize_t index )                   , REF(sequence_item(index))   )
ADD_HANDLERi( sequence_ass_item_handler         ( S, Py_ssize_t index, PyObject *value )  , p->sequence_ass_item(index,Py::Object(value))   )
// Mapping
ADD_HANDLER( Py_ssize_t, -1,
            mapping_length_handler              ( S )                                     , p->mapping_length()   )
ADD_HANDLERo( mapping_subscript_handler         ( S, PyObject *key )                      , REF( mapping_subscript(Py::Object(key))   )
ADD_HANDLERi( mapping_ass_subscript_handler     ( S, PyObject *key, PyObject *value )     , p->mapping_ass_subscript( Py::Object(key), Py::Object(value) )   )
// Number
ADD_HANDLERo( number_negative_handler           ( S )                                     , REF(number_negative ())  )
ADD_HANDLERo( number_positive_handler           ( S )                                     , REF(number_positive ())  )
ADD_HANDLERo( PyObject *number_absolute_handler ( S )                                     , REF(number_absolute ())  )
ADD_HANDLERo( PyObject *number_invert_handler   ( S )                                     , REF(number_invert   ())  )
ADD_HANDLERo( number_int_handler                ( S )                                     , REF(number_int      ())  )
ADD_HANDLERo( number_float_handler              ( S )                                     , REF(number_float    ())  )
ADD_HANDLERo( number_add_handler                ( S, O )                                  , REF(number_add      (Py::Object(other)) )   )
ADD_HANDLERo( number_subtract_handler           ( S, O )                                  , REF(number_subtract (Py::Object(other)) )   )
ADD_HANDLERo( number_multiply_handler           ( S, O )                                  , REF(number_multiply (Py::Object(other)) )   )
ADD_HANDLERo( number_remainder_handler          ( S, O )                                  , REF(number_remainder(Py::Object(other)) )   )
ADD_HANDLERo( number_divmod_handler             ( S, O )                                  , REF(number_divmod   (Py::Object(other)) )   )
ADD_HANDLERo( number_lshift_handler             ( S, O )                                  , REF(number_lshift   (Py::Object(other)) )   )
ADD_HANDLERo( number_rshift_handler             ( S, O )                                  , REF(number_rshift   (Py::Object(other)) )   )
ADD_HANDLERo( number_and_handler                ( S, O )                                  , REF(number_and      (Py::Object(other)) )   )
ADD_HANDLERo( number_xor_handler                ( S, O )                                  , REF(number_xor      (Py::Object(other)) )   )
ADD_HANDLERo( number_or_handler                 ( S, O )                                  , REF(number_or       (Py::Object(other)) )   )
ADD_HANDLERo( number_power_handler              ( S, PyObject *p, PyObject *q )           , REF(number_power    (Py::Object(p),Py::Object(q)) )   )
// Buffer
ADD_HANDLERi( buffer_get_handler                ( S, Py_buffer *buf, int flags )          , p->buffer_get( buf, flags )   )

extern "C" void buffer_release_handler( S, Py_buffer *buf )
{
    PythonExtensionBase *p = getPythonExtensionBase( self );
    p->buffer_release( buf );
    // NOTE: No way to indicate error to Python
}

#undef ADD_HANDLER
#undef ADD_HANDLERo
#undef ADD_HANDLERi
#undef REF
#undef S
#undef O

Each of these handlers is used exactly once. So it is redundant to declare them, use them and then define them.

Better to define them just before they are used.

This avoids having to have a separate declaration. Also it avoids the programmer from having to jump to 3 separate places in the source file just to figure out one mechanism.

Moving some chunks so that all handler related code is contiguous, we have:

#define ADD_HANDLER( returntype, return_err, handlerfunc, return_expr ) \
    extern "C" returntype handlerfunc \
    { \
        try \
        { \
            PythonExtensionBase *p = getPythonExtensionBase( self ); /* always the first parameter, see S below */ \
            return return_expr; \
        } \
        catch( Py::Exception & ) /* indicate error */ \
        { \
            return return_err;  \
        } \
    }

#define ADD_HANDLERo( func, return_expr ) ADD_HANDLER( PyObject*, nullptr, func, return_expr )
#define ADD_HANDLERi( func, return_expr ) ADD_HANDLER( int      , -1     , func, return_expr )

#define REF(x) new_reference_to( p -> x )
#define S PyObject *self
#define O PyObject *other

// Sequence
ADD_HANDLER( Py_ssize_t, -1,
            sequence_length_handler             ( S )                                     , p->sequence_length()   )
ADD_HANDLERo( sequence_concat_handler           ( S, O )                                  , REF(sequence_concat(Py::Object(other)))   )
ADD_HANDLERo( sequence_repeat_handler           ( S, Py_ssize_t count )                   , REF(sequence_repeat(count))   )
ADD_HANDLERo( sequence_item_handler             ( S, Py_ssize_t index )                   , REF(sequence_item(index))   )
ADD_HANDLERi( sequence_ass_item_handler         ( S, Py_ssize_t index, PyObject *value )  , p->sequence_ass_item(index,Py::Object(value))   )

PythonType &PythonType::supportSequenceType()
{
    if( !sequence_table )
    {
        sequence_table = new PySequenceMethods;
        memset( sequence_table, 0, sizeof( PySequenceMethods ) );   // ensure new fields are 0
        table->tp_as_sequence           = sequence_table;

        sequence_table->sq_length       = sequence_length_handler;
        sequence_table->sq_concat       = sequence_concat_handler;
        sequence_table->sq_repeat       = sequence_repeat_handler;
        sequence_table->sq_item         = sequence_item_handler;
        sequence_table->sq_ass_item     = sequence_ass_item_handler;    // BAS setup seperately?
        // QQQ sq_inplace_concat
        // QQQ sq_inplace_repeat
    }
    return *this;
}

// Mapping
ADD_HANDLER( Py_ssize_t, -1,
            mapping_length_handler              ( S )                                     , p->mapping_length()   )
ADD_HANDLERo( mapping_subscript_handler         ( S, PyObject *key )                      , REF( mapping_subscript(Py::Object(key)))   )
ADD_HANDLERi( mapping_ass_subscript_handler     ( S, PyObject *key, PyObject *value )     , p->mapping_ass_subscript( Py::Object(key), Py::Object(value) )   )

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;
        mapping_table->mp_subscript     = mapping_subscript_handler;
        mapping_table->mp_ass_subscript = mapping_ass_subscript_handler;    // BAS setup seperately?
    }
    return *this;
}

// Number
ADD_HANDLERo( number_negative_handler           ( S )                                     , REF(number_negative ())  )
ADD_HANDLERo( number_positive_handler           ( S )                                     , REF(number_positive ())  )
ADD_HANDLERo( number_absolute_handler           ( S )                                     , REF(number_absolute ())  )
ADD_HANDLERo( number_invert_handler             ( S )                                     , REF(number_invert   ())  )
ADD_HANDLERo( number_int_handler                ( S )                                     , REF(number_int      ())  )
ADD_HANDLERo( number_float_handler              ( S )                                     , REF(number_float    ())  )
ADD_HANDLERo( number_add_handler                ( S, O )                                  , REF(number_add      (Py::Object(other)) )   )
ADD_HANDLERo( number_subtract_handler           ( S, O )                                  , REF(number_subtract (Py::Object(other)) )   )
ADD_HANDLERo( number_multiply_handler           ( S, O )                                  , REF(number_multiply (Py::Object(other)) )   )
ADD_HANDLERo( number_remainder_handler          ( S, O )                                  , REF(number_remainder(Py::Object(other)) )   )
ADD_HANDLERo( number_divmod_handler             ( S, O )                                  , REF(number_divmod   (Py::Object(other)) )   )
ADD_HANDLERo( number_lshift_handler             ( S, O )                                  , REF(number_lshift   (Py::Object(other)) )   )
ADD_HANDLERo( number_rshift_handler             ( S, O )                                  , REF(number_rshift   (Py::Object(other)) )   )
ADD_HANDLERo( number_and_handler                ( S, O )                                  , REF(number_and      (Py::Object(other)) )   )
ADD_HANDLERo( number_xor_handler                ( S, O )                                  , REF(number_xor      (Py::Object(other)) )   )
ADD_HANDLERo( number_or_handler                 ( S, O )                                  , REF(number_or       (Py::Object(other)) )   )
ADD_HANDLERo( number_power_handler              ( S, PyObject *p, PyObject *q )           , REF(number_power    (Py::Object(p),Py::Object(q)) )   )

PythonType &PythonType::supportNumberType()
{
    if( !number_table )
    {
        number_table = new PyNumberMethods;
        memset( number_table, 0, sizeof( PyNumberMethods ) );   // ensure new fields are 0
        table->tp_as_number             = number_table;

        number_table->nb_add            = number_add_handler;
        number_table->nb_subtract       = number_subtract_handler;
        number_table->nb_multiply       = number_multiply_handler;
        number_table->nb_remainder      = number_remainder_handler;
        number_table->nb_divmod         = number_divmod_handler;
        number_table->nb_power          = number_power_handler;
        number_table->nb_negative       = number_negative_handler;
        number_table->nb_positive       = number_positive_handler;
        number_table->nb_absolute       = number_absolute_handler;
        number_table->nb_invert         = number_invert_handler;
        number_table->nb_lshift         = number_lshift_handler;
        number_table->nb_rshift         = number_rshift_handler;
        number_table->nb_and            = number_and_handler;
        number_table->nb_xor            = number_xor_handler;
        number_table->nb_or             = number_or_handler;
        number_table->nb_int            = number_int_handler;
        number_table->nb_float          = number_float_handler;

        // QQQ lots of new methods to add
    }
    return *this;
}

// Support
#ifdef PYCXX_PYTHON_2TO3
ADD_HANDLERi( print_handler                     ( S, FILE *fp, int flags )                , p->print(fp,flags)   )
#endif
ADD_HANDLERo( getattr_handler                   ( S, char *name )                         , REF(getattr(name))   )
ADD_HANDLERi( setattr_handler                   ( S, char *name, PyObject *value )        , p->setattr(name,Py::Object(value))   )
ADD_HANDLERo( getattro_handler                  ( S, PyObject *name )                     , REF( getattro(Py::String(name)) )   )
ADD_HANDLERi( setattro_handler                  ( S, PyObject *name, PyObject *value )    , p->setattro( Py::String(name), Py::Object(value) )   )
ADD_HANDLERo( rich_compare_handler              ( S, O, int op )                          , REF(  rich_compare(Py::Object(other), op)  )   )
ADD_HANDLERo( repr_handler                      ( S )                                     , REF(repr())   )
ADD_HANDLERo( str_handler                       ( S )                                     , REF(str())   )
ADD_HANDLER( Py_hash_t, -1,
            hash_handler                        ( S )                                     , p->hash()   )
ADD_HANDLERo( call_handler                      ( S, PyObject *args, PyObject *kw )       , new_reference_to(  p->call( Py::Object(args), kw ? Py::Object(kw) : Py::Object() )  )    )
ADD_HANDLERo( iter_handler                      ( S )                                     , REF(iter())   )
ADD_HANDLERo( iternext_handler                  ( S )                                     , p->iternext()   )    // might be a NULL ptr on end of iteration

#define SUPP( _Foo_, _statement_ ) \
    PythonType &PythonType::_Foo_() \
    { \
        _statement_ \
        return *this; \
    }

    SUPP( supportClass        , table->tp_flags |= Py_TPFLAGS_BASETYPE; )
#ifdef PYCXX_PYTHON_2TO3
    SUPP( supportPrint        , table->tp_print         = print_handler; )
#endif

    SUPP( supportGetattr      , table->tp_getattr       = getattr_handler; )
    SUPP( supportSetattr      , table->tp_setattr       = setattr_handler; )
    SUPP( supportGetattro     , table->tp_getattro      = getattro_handler; )
    SUPP( supportSetattro     , table->tp_setattro      = setattro_handler; )
#ifdef PYCXX_PYTHON_2TO3
    SUPP( supportCompare      , )
#endif
    SUPP( supportRichCompare  , table->tp_richcompare   = rich_compare_handler; )
    SUPP( supportRepr         , table->tp_repr          = repr_handler; )
    SUPP( supportStr          , table->tp_str           = str_handler; )
    SUPP( supportHash         , table->tp_hash          = hash_handler; )
    SUPP( supportCall         , table->tp_call          = call_handler; )
    SUPP( supportIter         , table->tp_iter          = iter_handler;
         table->tp_iternext      = iternext_handler; )
#undef SUPP

// Buffer
ADD_HANDLERi( buffer_get_handler                ( S, Py_buffer *buf, int flags )          , p->buffer_get( buf, flags )   )

extern "C" void buffer_release_handler( S, Py_buffer *buf )
{
    PythonExtensionBase *p = getPythonExtensionBase( self );
    p->buffer_release( buf );
    // NOTE: No way to indicate error to Python
}

// End of Handlers
#undef ADD_HANDLER
#undef ADD_HANDLERo
#undef ADD_HANDLERi
#undef REF
#undef S
#undef O

Note that consistent parameter alignment allows the reader to instantly identify blocks that do the same thing as one another.


Finally, it seems wasteful that we define a function, only to slot its pointer into some C function table
It would be nicer if we could do it all on one line.
C++11 supports lambdas, and a lambda with an empty capture list does exactly this.
I was initially worried that it would create a function with a C++ signature which couldn't be assigned to a pure C function pointer (because in C++ you should wrap 'extern C' around a function to prevent C++ name mangling)
However, it looks as though it does an implicit typecast to resolve this.

So here is the final code:

/* π 
 Note that it is possible to assign a captureless lambda to an 'extern C' function pointer:
     { void (*func)() = [](){ cout << "hi"; }; func(); }
     { float (*func)(int,double) = [](int i,double d)->float{ cout << i,d; return 0.01f; }; func(1,0.01); } */

#define HANDLER( RET, PARAMS, IS_REF, EXPR, ERR ) \
    [] PARAMS -> RET \
    { \
        try { \
            PythonExtensionBase *p = getPythonExtensionBase( self ); \
            return IS_REF( p -> EXPR ); \
        } \
        catch( Py::Exception&  ) { /* indicate error */ \
            return ERR;  \
        } \
    }

#define HANDLER_i( params, is_ref, return_expr )  HANDLER( int       , params, is_ref, return_expr, -1      )
#define HANDLER_t( params, is_ref, return_expr )  HANDLER( Py_ssize_t, params, is_ref, return_expr, -1      )
#define HANDLER_h( params, is_ref, return_expr )  HANDLER( Py_hash_t , params, is_ref, return_expr, -1      )
#define HANDLER_o( params, is_ref, return_expr )  HANDLER( PyObject* , params, is_ref, return_expr, nullptr )

#define S PyObject* self
#define O PyObject* other
#define REF(x) new_reference_to( x )

PythonType& PythonType::supportSequenceType()
{
    if( !sequence_table )
    {
        sequence_table = new PySequenceMethods;
        memset( sequence_table, 0, sizeof( PySequenceMethods ) );   // ensure new fields are 0
        table->tp_as_sequence           = sequence_table;

        //sequence_table->sq_length       = sequence_length_handler;
        sequence_table->sq_length       = HANDLER_t(   ( S )                                     ,     , sequence_length()                          );
        sequence_table->sq_concat       = HANDLER_o(   ( S, O )                                  , REF , sequence_concat(Py::Object(other))         );
        sequence_table->sq_repeat       = HANDLER_o(   ( S, Py_ssize_t count )                   , REF , sequence_repeat(count)                     );
        sequence_table->sq_item         = HANDLER_o(   ( S, Py_ssize_t index )                   , REF , sequence_item(index)                       );
        sequence_table->sq_ass_item     = HANDLER_i(   ( S, Py_ssize_t index, PyObject* value )  ,     , sequence_ass_item(index,Py::Object(value)) );    // BAS setup seperately?
        // QQQ sq_inplace_concat
        // QQQ sq_inplace_repeat
    }
    return *this;
}

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        = HANDLER_t(   ( S )                                     ,     , mapping_length()                                               );
        mapping_table->mp_subscript     = HANDLER_o(   ( S, PyObject* key )                      , REF , mapping_subscript(Py::Object(key))                             );
        mapping_table->mp_ass_subscript = HANDLER_i(   ( S, PyObject* key, PyObject* value )     ,     , mapping_ass_subscript( Py::Object(key), Py::Object(value) )    );    // BAS setup seperately?
    }
    return *this;
}

PythonType& PythonType::supportNumberType()
{
    if( !number_table )
    {
        number_table = new PyNumberMethods;
        memset( number_table, 0, sizeof( PyNumberMethods ) );   // ensure new fields are 0
        table->tp_as_number             = number_table;

        number_table->nb_int            = HANDLER_o(   ( S )                                     , REF, number_int       ( )                            );
        number_table->nb_float          = HANDLER_o(   ( S )                                     , REF, number_float     ( )                            );
        number_table->nb_negative       = HANDLER_o(   ( S )                                     , REF, number_negative  ( )                            );
        number_table->nb_positive       = HANDLER_o(   ( S )                                     , REF, number_positive  ( )                            );
        number_table->nb_absolute       = HANDLER_o(   ( S )                                     , REF, number_absolute  ( )                            );
        number_table->nb_invert         = HANDLER_o(   ( S )                                     , REF, number_invert    ( )                            );
        number_table->nb_add            = HANDLER_o(   ( S, O )                                  , REF, number_add       (Py::Object(other))            );
        number_table->nb_subtract       = HANDLER_o(   ( S, O )                                  , REF, number_subtract  (Py::Object(other))            );
        number_table->nb_multiply       = HANDLER_o(   ( S, O )                                  , REF, number_multiply  (Py::Object(other))            );
        number_table->nb_remainder      = HANDLER_o(   ( S, O )                                  , REF, number_remainder (Py::Object(other))            );
        number_table->nb_divmod         = HANDLER_o(   ( S, O )                                  , REF, number_divmod    (Py::Object(other))            );
        number_table->nb_lshift         = HANDLER_o(   ( S, O )                                  , REF, number_lshift    (Py::Object(other))            );
        number_table->nb_rshift         = HANDLER_o(   ( S, O )                                  , REF, number_lshift    (Py::Object(other))            );
        number_table->nb_and            = HANDLER_o(   ( S, O )                                  , REF, number_and       (Py::Object(other))            );
        number_table->nb_xor            = HANDLER_o(   ( S, O )                                  , REF, number_xor       (Py::Object(other))            );
        number_table->nb_or             = HANDLER_o(   ( S, O )                                  , REF, number_or        (Py::Object(other))            );
        number_table->nb_power          = HANDLER_o(   ( S, PyObject* p, PyObject* q )           , REF, number_power     (Py::Object(p),Py::Object(q))  );

        // QQQ lots of new methods to add
    }
    return *this;
}

#define SUPP( _Foo_, _statement_ ) \
    PythonType& PythonType::_Foo_() \
    { \
        _statement_; \
        return *this; \
    }

SUPP( supportClass        , table->tp_flags |= Py_TPFLAGS_BASETYPE; )
#ifdef PYCXX_PYTHON_2TO3
SUPP( supportPrint        , table->tp_print         = HANDLER_i(   ( S, FILE*     fp     , int       flags ) ,     , print(fp,flags)                                            )   )
#endif

SUPP( supportGetattr      , table->tp_getattr       = HANDLER_o(   ( S, char*     name                     ) , REF , getattr(name)                                              )   )
SUPP( supportSetattr      , table->tp_setattr       = HANDLER_i(   ( S, char*     name   , PyObject* value ) ,     , setattr(name, Py::Object(value))                           )   )
SUPP( supportGetattro     , table->tp_getattro      = HANDLER_o(   ( S, PyObject* name                     ) , REF , getattro(Py::String(name))                                 )   )
SUPP( supportSetattro     , table->tp_setattro      = HANDLER_i(   ( S, PyObject* name   , PyObject* value ) ,     , setattro(Py::String(name), Py::Object(value))              )   )
#ifdef PYCXX_PYTHON_2TO3
SUPP( supportCompare      , )
#endif
SUPP( supportRichCompare  , table->tp_richcompare   = HANDLER_o(   ( S, O                , int       op    ) , REF , rich_compare(Py::Object(other), op)                        )   )
SUPP( supportRepr         , table->tp_repr          = HANDLER_o(   ( S                                     ) , REF , repr()                                                     )   )
SUPP( supportStr          , table->tp_str           = HANDLER_o(   ( S                                     ) , REF , str()                                                      )   )
SUPP( supportHash         , table->tp_hash          = HANDLER_h(   ( S                                     ) ,     , hash()                                                     )   )
SUPP( supportCall         , table->tp_call          = HANDLER_o(   ( S, PyObject* args   , PyObject* kw    ) , REF , call( Py::Object{args}, kw?Py::Object{kw}:Py::Object{} )   )   )
SUPP( supportIter         , table->tp_iter          = HANDLER_o(   ( S                                     ) , REF , iter()                                                     );
                            table->tp_iternext      = HANDLER_o(   ( S                                     ) ,     , iternext()                                                 )   )
#undef SUPP

PythonType& PythonType::supportBufferType()
{
    if( !buffer_table )
    {
        buffer_table = new PyBufferProcs;
        memset( buffer_table, 0, sizeof( PyBufferProcs ) );   // ensure new fields are 0
        table->tp_as_buffer                         = buffer_table;

        buffer_table->bf_getbuffer                  = HANDLER_i(   ( S, Py_buffer *buf, int flags )          ,     , buffer_get(buf,flags)   );
        buffer_table->bf_releasebuffer              = [] ( S, Py_buffer *buf ) {
            PythonExtensionBase *p = getPythonExtensionBase( self ); p->buffer_release( buf ); }; // NOTE: No way to indicate error to Python
    }
    return *this;
}

// End of Handlers
#undef HANDLER_o
#undef HANDLER_i
#undef HANDLER_t
#undef HANDLER_h
#undef S
#undef O
#undef REF
#undef HANDLER

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