FAQ
[Cross-posted comp.lang.c++ and comp.lang.python]

Consider the following code, from an example usage of some C++ support for
Python I'm working on, "cppy":


<code>
struct Noddy
{
PyPtr first;
PyPtr last;
int number;

Noddy( PyWeakPtr pySelf, PyPtr args, PyPtr kwArgs )
: number( 0 )
{
// ... some initialization here
}

PyPtr name()
{
return (PyString( first ) + L" " + PyString( last )).pyPtr();
}
};

struct NoddyPyClass: PyClass< Noddy >
{
typedef PyClass< Noddy > Base;

NoddyPyClass( PyModule& m, PyString const& name, PyString const& doc )
: Base( m, name, doc,
Exposition()
.method(
L"name", CPPY_METHOD_FORWARDER( name ),
L"Return the name, combining the first and last name"
)
.attribute(
L"first", CPPY_GETSET_FORWARDERS( first ),
L"first name"
)
.attribute(
L"last", CPPY_GETSET_FORWARDERS( last ),
L"last name"
)
.attribute(
L"number", CPPY_GETSET_FORWARDERS( number ),
L"noddy number"
)
)
{}
};
</code>


Originally e.g.

CPPY_GETSET_FORWARDERS( number ),

had to be written as

CPPY_GETSET_FORWARDERS( int, number ),

because in order to use a compile time member pointer as template actual
argument, one needs to supply the member type as a separate template argument.

E.g. the template might look like

template< class Class, class MemberType, MemberType Class::*pMember >
struct ForwardersForGetAndSet
{
// Some definitions here, hardwiring that compile time member pointer!
};

Apparently there was no way around the user repeating explicitly the member type
that the compiler already knew about... It seemed akin to deducing the return
type of a function. Difficult in C++98 (although Boost does a fair job).

But then it seemed that I'm not totally senile yet, for this occurred to me:

#define CPPY_GETSET_FORWARDERS( name ) \
::progrock::cppy::forwardersGetSet( \
&CppClass::name \
).themForwarders< &CppClass::name >()

Here forwardersGetSet is a templated function that via argument type deduction
produces an instance of a templated struct, thereby "knowing" the member type,
which struct in turn has a member function templated on the member pointer,
which the macro supplies *twice*, once as run-time arg and once as compile-time.

He he.

Perhaps this trick is well-known already, but it was new to me, so! :-)


Cheers,

- Alf

Search Discussions

  • Alf P. Steinbach /Usenet at Jul 17, 2010 at 1:02 pm

    * Alf P. Steinbach /Usenet, on 17.07.2010 11:50:
    [Cross-posted comp.lang.c++ and comp.lang.python]

    [snip]
    this occurred to me:

    #define CPPY_GETSET_FORWARDERS( name ) \
    ::progrock::cppy::forwardersGetSet( \
    &CppClass::name \
    ).themForwarders< &CppClass::name >()

    Here forwardersGetSet is a templated function that via argument type
    deduction produces an instance of a templated struct, thereby "knowing"
    the member type, which struct in turn has a member function templated on
    the member pointer, which the macro supplies *twice*, once as run-time
    arg and once as compile-time.
    That trick allowed uniform treatment of data and function member pointers. :-)

    So, with cppy the complete noddy2 example from the docs (I'm sort of working my
    way through the Python doc examples) now looks as shown below.

    The "typelessness" of the 'first' and 'last' members is in order to recreate as
    close as possible the noddy2 functionality, or, lack of it. Also, I haven't got
    around to sort of backporting the 'Exposition' scheme to modules. So, exposure
    of module functionality looks a little different from same for types, for now.


    <code>
    // The Python 3.1.1 docs' Noddy 2 example rewritten using cppy.
    // Docs: "Extending and Embedding the Python Interpreter" ?2.1.1

    #include <progrock/cppy/PyClass.h> // PyWeakPtr, PyPtr, PyModule, PyClass
    using namespace progrock;

    namespace {
    using namespace cppy;

    struct Noddy
    {
    PyPtr first;
    PyPtr last;
    int number;

    Noddy( PyWeakPtr pySelf, PyPtr args, PyPtr kwArgs )
    : number( 0 )
    {
    devsupport::suppressUnusedWarning( pySelf );

    PyWeakPtr pFirstName = 0;
    PyWeakPtr pLastName = 0;

    static char* kwlist[] = { "first", "last", "number", 0 };

    ::PyArg_ParseTupleAndKeywords(
    args.rawPtr(), kwArgs.rawPtr(), "|OOi", kwlist,
    pointerTo( pFirstName ), pointerTo( pLastName ), &number
    )
    Accept< IsNonZero >()
    throwX( "Invalid args" );
    if( pFirstName != 0 ) { first = pFirstName; }
    if( pLastName != 0 ) { last = pLastName; }
    }

    PyPtr name()
    {
    (first != 0)
    throwX( ::PyExc_AttributeError, "first" );
    (last != 0)
    throwX( ::PyExc_AttributeError, "last" );
    return (PyString( first ) + L" " + PyString( last )).pyPtr();
    }
    };

    struct NoddyPyClass: PyClass< Noddy >
    {
    NoddyPyClass( PyModule& m, PyString const& name, PyString const& doc )
    : PyClass< Noddy >( m, name, doc, Exposition()
    .method(
    L"name", CPPY_GLUE( name ),
    L"Return the name, combining the first and last name"
    )
    .attribute(
    L"first", CPPY_GLUE( first ), L"first name"
    )
    .attribute(
    L"last", CPPY_GLUE( last ), L"last name"
    )
    .attribute(
    L"number", CPPY_GLUE( number ), L"noddy number"
    )
    )
    {}
    };

    class NoddyModule: public PyModule
    {
    private:
    NoddyPyClass noddyPyClass_;

    public:
    NoddyModule()
    : PyModule(
    L"noddy2", L"Example module that creates an extension type." )
    , noddyPyClass_( *this,
    L"Noddy", L"A Noddy object has a name and a noddy number" )
    {}
    };
    } // namespace <anon>


    PyMODINIT_FUNC
    PyInit_noddy2()
    {
    return cppy::safeInit< NoddyModule >();
    }
    </code>


    I wonder if this is readable / self-documenting, or not?


    Cheers,

    - Alf
  • Sturlamolden at Jul 17, 2010 at 2:19 pm

    On 17 Jul, 15:02, "Alf P. Steinbach /Usenet" <alf.p.steinbach +use... at gmail.com> wrote:

    #include <progrock/cppy/PyClass.h> ? ? ?// PyWeakPtr, PyPtr, PyModule, PyClass
    using namespace progrock;

    namespace {
    ? ? ?using namespace cppy;

    ? ? ?struct Noddy
    ? ? ?{
    ? ? ? ? ?PyPtr ? ? ? first;
    ? ? ? ? ?PyPtr ? ? ? last;
    ? ? ? ? ?int ? ? ? ? number;

    ? ? ? ? ?Noddy( PyWeakPtr pySelf, PyPtr args, PyPtr kwArgs )
    ? ? ? ? ? ? ?: number( 0 )
    ? ? ? ? ?{
    ? ? ? ? ? ? ?devsupport::suppressUnusedWarning( pySelf );

    ? ? ? ? ? ? ?PyWeakPtr ? pFirstName ?= 0;
    ? ? ? ? ? ? ?PyWeakPtr ? pLastName ? = 0;

    ? ? ? ? ? ? ?static char* ? ?kwlist[] = { "first", "last", "number", 0 };

    ? ? ? ? ? ? ?::PyArg_ParseTupleAndKeywords(
    ? ? ? ? ? ? ? ? ?args.rawPtr(), kwArgs.rawPtr(), "|OOi", kwlist,
    ? ? ? ? ? ? ? ? ?pointerTo( pFirstName ), pointerTo( pLastName ), &number
    ? ? ? ? ? ? ? ? ?)
    ? ? ? ? ? ? ? ? ?>> Accept< IsNonZero >()
    ? ? ? ? ? ? ? ? ?|| throwX( "Invalid args" );

    ? ? ? ? ? ? ?if( pFirstName != 0 ) ? { first = pFirstName; }
    ? ? ? ? ? ? ?if( pLastName != 0 ) ? ?{ last = pLastName; }
    ? ? ? ? ?}

    ? ? ? ? ?PyPtr name()
    ? ? ? ? ?{
    ? ? ? ? ? ? ?(first != 0)
    ? ? ? ? ? ? ? ? ?|| throwX( ::PyExc_AttributeError, "first" );
    ? ? ? ? ? ? ?(last != 0)
    ? ? ? ? ? ? ? ? ?|| throwX( ::PyExc_AttributeError, "last" );
    ? ? ? ? ? ? ?return (PyString( first ) + L" " + PyString( last )).pyPtr();
    ? ? ? ? ?}
    ? ? ?};

    ? ? ?struct NoddyPyClass: PyClass< Noddy >
    ? ? ?{
    ? ? ? ? ?NoddyPyClass( PyModule& m, PyString const& name, PyString const& doc )
    ? ? ? ? ? ? ?: PyClass< Noddy >( m, name, doc, Exposition()
    ? ? ? ? ? ? ? ? ?.method(
    ? ? ? ? ? ? ? ? ? ? ?L"name", ? ?CPPY_GLUE( name ),
    ? ? ? ? ? ? ? ? ? ? ?L"Return the name, combining the first and last name"
    ? ? ? ? ? ? ? ? ? ? ?)
    ? ? ? ? ? ? ? ? ?.attribute(
    ? ? ? ? ? ? ? ? ? ? ?L"first", ? CPPY_GLUE( first ), ? ? L"first name"
    ? ? ? ? ? ? ? ? ? ? ?)
    ? ? ? ? ? ? ? ? ?.attribute(
    ? ? ? ? ? ? ? ? ? ? ?L"last", ? ?CPPY_GLUE( last ), ? ? ?L"last name"
    ? ? ? ? ? ? ? ? ? ? ?)
    ? ? ? ? ? ? ? ? ?.attribute(
    ? ? ? ? ? ? ? ? ? ? ?L"number", ?CPPY_GLUE( number ), ? ?L"noddy number"
    ? ? ? ? ? ? ? ? ? ? ?)
    ? ? ? ? ? ? ? ? ?)
    ? ? ? ? ?{}
    ? ? ?};

    ? ? ?class NoddyModule: public PyModule
    ? ? ?{
    ? ? ?private:
    ? ? ? ? ?NoddyPyClass ? ?noddyPyClass_;

    ? ? ?public:
    ? ? ? ? ?NoddyModule()
    ? ? ? ? ? ? ?: PyModule(
    ? ? ? ? ? ? ? ? ?L"noddy2", L"Example module that creates an extension type." )
    ? ? ? ? ? ? ?, noddyPyClass_( *this,
    ? ? ? ? ? ? ? ? ?L"Noddy", L"A Noddy object has a name and a noddy number" )
    ? ? ? ? ?{}
    ? ? ?};

    } ? ?// namespace <anon>

    PyMODINIT_FUNC
    PyInit_noddy2()
    {
    ? ? ?return cppy::safeInit< NoddyModule >();}
    I wonder if this is readable / self-documenting, or not?



    Are you serious?

    It's C++, Heaven forbid, and you wonder if it's readable or not?
  • Vladimir Jovic at Jul 19, 2010 at 7:41 am

    Alf P. Steinbach /Usenet wrote:

    #include <progrock/cppy/PyClass.h> // PyWeakPtr, PyPtr, PyModule,
    PyClass
    using namespace progrock;

    namespace {
    using namespace cppy;

    struct Noddy
    {
    PyPtr first;
    PyPtr last;
    int number;

    Noddy( PyWeakPtr pySelf, PyPtr args, PyPtr kwArgs )
    : number( 0 )
    {
    devsupport::suppressUnusedWarning( pySelf );

    PyWeakPtr pFirstName = 0;
    PyWeakPtr pLastName = 0;

    static char* kwlist[] = { "first", "last", "number", 0 };

    ::PyArg_ParseTupleAndKeywords(
    args.rawPtr(), kwArgs.rawPtr(), "|OOi", kwlist,
    pointerTo( pFirstName ), pointerTo( pLastName ), &number
    )
    Accept< IsNonZero >()
    throwX( "Invalid args" );
    if( pFirstName != 0 ) { first = pFirstName; }
    if( pLastName != 0 ) { last = pLastName; }
    Why not initiallize all member variables in the initializer list?

    }

    PyPtr name()
    {
    (first != 0)
    throwX( ::PyExc_AttributeError, "first" );
    (last != 0)
    throwX( ::PyExc_AttributeError, "last" );
    Nice trick. I would still find this more readable :

    if ( first != 0 )
    {
    throwX( ::PyExc_AttributeError, "first" );
    }

    return (PyString( first ) + L" " + PyString( last )).pyPtr();
    }
    };

    struct NoddyPyClass: PyClass< Noddy >
    {
    NoddyPyClass( PyModule& m, PyString const& name, PyString const&
    doc )
    : PyClass< Noddy >( m, name, doc, Exposition()
    .method(
    L"name", CPPY_GLUE( name ),
    L"Return the name, combining the first and last name"
    )
    .attribute(
    L"first", CPPY_GLUE( first ), L"first name"
    )
    .attribute(
    L"last", CPPY_GLUE( last ), L"last name"
    )
    .attribute(
    L"number", CPPY_GLUE( number ), L"noddy number"
    )
    )
    {}
    };

    class NoddyModule: public PyModule
    {
    private:
    NoddyPyClass noddyPyClass_;

    public:
    NoddyModule()
    : PyModule(
    L"noddy2", L"Example module that creates an extension
    type." )
    , noddyPyClass_( *this,
    L"Noddy", L"A Noddy object has a name and a noddy number" )

    hmmm what is L ?
  • Alf P. Steinbach /Usenet at Jul 19, 2010 at 8:21 am

    * Vladimir Jovic, on 19.07.2010 09:41:
    Alf P. Steinbach /Usenet wrote:
    #include <progrock/cppy/PyClass.h> // PyWeakPtr, PyPtr, PyModule,
    PyClass
    using namespace progrock;

    namespace {
    using namespace cppy;

    struct Noddy
    {
    PyPtr first;
    PyPtr last;
    int number;

    Noddy( PyWeakPtr pySelf, PyPtr args, PyPtr kwArgs )
    : number( 0 )
    {
    devsupport::suppressUnusedWarning( pySelf );

    PyWeakPtr pFirstName = 0;
    PyWeakPtr pLastName = 0;

    static char* kwlist[] = { "first", "last", "number", 0 };

    ::PyArg_ParseTupleAndKeywords(
    args.rawPtr(), kwArgs.rawPtr(), "|OOi", kwlist,
    pointerTo( pFirstName ), pointerTo( pLastName ), &number
    )
    Accept< IsNonZero >()
    throwX( "Invalid args" );
    if( pFirstName != 0 ) { first = pFirstName; }
    if( pLastName != 0 ) { last = pLastName; }
    Why not initiallize all member variables in the initializer list?
    'PyPtr' is a smart pointer with default constructor, so 'first' and 'last' are
    initialized to zero implicitly.

    My goal was to emulate the Python 3.1.1 docs' Noddy2 example as closely as
    possible. And that code's assignment of zero to 'first' and 'last' here
    corresponds to default initialization to zero. :-)

    Anyway, parsing the Python arguments in the constructor initializer list is
    possible, but would be inefficient to do directly since it would then involve
    parsing the Python arguments three times (once for each data member).

    There is at least one way around that problem, namely outfitting a derived class
    with knowledge of the Noddy class' Python-side __init__ arguments.

    But I haven't implemented such support, and I don't know if it would do any
    good. As it is Noddy above is self-contained, except for exporting names to the
    Python side. With a derived class doing the argument parsing it would be less
    self-contained, and the machinery for that would add complexity, I think.

    So, I strive to make things simple and explicit (KISS: Keep It Simple, Stupid!).

    And I strive to not go into orgies of smart templates doing things implicitly...

    }

    PyPtr name()
    {
    (first != 0)
    throwX( ::PyExc_AttributeError, "first" );
    (last != 0)
    throwX( ::PyExc_AttributeError, "last" );
    Nice trick. I would still find this more readable :

    if ( first != 0 )
    {
    throwX( ::PyExc_AttributeError, "first" );
    }
    Ah, well. :-)

    You can read about the rationale here: <url:
    http://alfps.wordpress.com/2010/07/18/cppx-b-true-or-b-thrown-using-the-throwing-pattern/>
    -- posted yesterday.

    Of course the only reason that those member variables /can/ be 0 is because I'm
    following the doc's progression of examples. The next version in the docs avoid
    the 0 possibility, as I recall. And the simple way of achieving that in cppy
    would be to declare 'first' and 'last' as PyString instead of generic PyPtr...

    return (PyString( first ) + L" " + PyString( last )).pyPtr();
    }
    };

    struct NoddyPyClass: PyClass< Noddy >
    {
    NoddyPyClass( PyModule& m, PyString const& name, PyString
    const& doc )
    : PyClass< Noddy >( m, name, doc, Exposition()
    .method(
    L"name", CPPY_GLUE( name ),
    L"Return the name, combining the first and last name"
    )
    .attribute(
    L"first", CPPY_GLUE( first ), L"first name"
    )
    .attribute(
    L"last", CPPY_GLUE( last ), L"last name"
    )
    .attribute(
    L"number", CPPY_GLUE( number ), L"noddy number"
    )
    )
    {}
    };

    class NoddyModule: public PyModule
    {
    private:
    NoddyPyClass noddyPyClass_;

    public:
    NoddyModule()
    : PyModule(
    L"noddy2", L"Example module that creates an extension
    type." )
    , noddyPyClass_( *this,
    L"Noddy", L"A Noddy object has a name and a noddy
    number" )

    hmmm what is L ?
    It's standard C and C++ notation for a wide character literal, where each
    character is of type 'wchar_t' instead of plain 'char'.

    Essentially the intent is to hold Unicode UTF16 or UTF32 code points, which is
    how it is in practice, although for political correctness the C and C++
    standards allow for other wide character encodings (not necessarily Unicode).

    It is the only way to /portably/ use national character literals in C++, because
    otherwise the result depends on the source code encoding. E.g.

    sizeof( "?" )

    yields 2 (the character '?' plus a zero byte at the end) with source code in
    Windows ANSI Western, and 3 with source code in UTF8, while

    sizeof( L"?" )/sizeof( wchar_t )

    should in practice -- with Unicode wide chars -- always yield 2, namely the
    wide character encoding of '?' plus a zero wchar_t.

    The Python C API generally assumes that all 'char' strings are encoded in UTF8,
    while in Windows they're generally in Windows ANSI Western, so, I avoid that.


    Cheers, & thanks,

    - Alf

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
grouppython-list @
categoriespython
postedJul 17, '10 at 9:50a
activeJul 19, '10 at 8:21a
posts5
users3
websitepython.org

People

Translate

site design / logo © 2022 Grokbase