Sharing: member type deduction for member pointers (Alf's device?)

Alf P. Steinbach /Usenet alf.p.steinbach+usenet at gmail.com
Mon Jul 19 04:21:52 EDT 2010


* 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

-- 
blog at <url: http://alfps.wordpress.com>



More information about the Python-list mailing list