From: Alf P. Steinbach /Usenet on
[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

--
blog at <url: http://alfps.wordpress.com>
From: Alf P. Steinbach /Usenet on
* 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

--
blog at <url: http://alfps.wordpress.com>
From: sturlamolden on
On 17 Jul, 15:02, "Alf P. Steinbach /Usenet" <alf.p.steinbach
+use...(a)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?



















From: Vladimir Jovic on
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 ?