1 //-----------------------------------------------------------------------------
2 //
3 // Copyright (c) 1998 - 2007, The Regents of the University of California
4 // Produced at the Lawrence Livermore National Laboratory
5 // All rights reserved.
6 //
7 // This file is part of PyCXX. For details,see http://cxx.sourceforge.net/. The
8 // full copyright notice is contained in the file COPYRIGHT located at the root
9 // of the PyCXX distribution.
10 //
11 // Redistribution  and  use  in  source  and  binary  forms,  with  or  without
12 // modification, are permitted provided that the following conditions are met:
13 //
14 //  - Redistributions of  source code must  retain the above  copyright notice,
15 //    this list of conditions and the disclaimer below.
16 //  - Redistributions in binary form must reproduce the above copyright notice,
17 //    this  list of  conditions  and  the  disclaimer (as noted below)  in  the
18 //    documentation and/or materials provided with the distribution.
19 //  - Neither the name of the UC/LLNL nor  the names of its contributors may be
20 //    used to  endorse or  promote products derived from  this software without
21 //    specific prior written permission.
22 //
23 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT  HOLDERS AND CONTRIBUTORS "AS IS"
24 // AND ANY EXPRESS OR  IMPLIED WARRANTIES, INCLUDING,  BUT NOT  LIMITED TO, THE
25 // IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS FOR A PARTICULAR  PURPOSE
26 // ARE  DISCLAIMED.  IN  NO  EVENT  SHALL  THE  REGENTS  OF  THE  UNIVERSITY OF
27 // CALIFORNIA, THE U.S.  DEPARTMENT  OF  ENERGY OR CONTRIBUTORS BE  LIABLE  FOR
28 // ANY  DIRECT,  INDIRECT,  INCIDENTAL,  SPECIAL,  EXEMPLARY,  OR CONSEQUENTIAL
29 // DAMAGES (INCLUDING, BUT NOT  LIMITED TO, PROCUREMENT OF  SUBSTITUTE GOODS OR
30 // SERVICES; LOSS OF  USE, DATA, OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER
31 // CAUSED  AND  ON  ANY  THEORY  OF  LIABILITY,  WHETHER  IN  CONTRACT,  STRICT
32 // LIABILITY, OR TORT  (INCLUDING NEGLIGENCE OR OTHERWISE)  ARISING IN ANY  WAY
33 // OUT OF THE  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
34 // DAMAGE.
35 //
36 //-----------------------------------------------------------------------------
37 
38 #ifndef __CXX_ExtensionClass__h
39 #define __CXX_ExtensionClass__h
40 
41 #define PYCXX_NOARGS_METHOD_NAME( NAME ) _callNoArgsMethod__##NAME
42 #define PYCXX_VARARGS_METHOD_NAME( NAME ) _callVarArgsMethod__##NAME
43 #define PYCXX_KEYWORDS_METHOD_NAME( NAME ) _callKeywordsMethod__##NAME
44 
45 #define PYCXX_NOARGS_METHOD_DECL( CLS, NAME ) \
46     static PyObject *PYCXX_NOARGS_METHOD_NAME( NAME )( PyObject *_self, PyObject *, PyObject * ) \
47     { \
48         try \
49         { \
50             Py::PythonClassInstance *self_python = reinterpret_cast< Py::PythonClassInstance * >( _self ); \
51             CLS *self = reinterpret_cast< CLS * >( self_python->m_pycxx_object ); \
52             Py::Object r( (self->NAME)() ); \
53             return Py::new_reference_to( r.ptr() ); \
54         } \
55         catch( Py::BaseException & ) \
56         { \
57             return 0; \
58         } \
59     }
60 #define PYCXX_VARARGS_METHOD_DECL( CLS, NAME ) \
61     static PyObject *PYCXX_VARARGS_METHOD_NAME( NAME )( PyObject *_self, PyObject *_a, PyObject * ) \
62     { \
63         try \
64         { \
65             Py::PythonClassInstance *self_python = reinterpret_cast< Py::PythonClassInstance * >( _self ); \
66             CLS *self = reinterpret_cast< CLS * >( self_python->m_pycxx_object ); \
67             Py::Tuple a( _a ); \
68             Py::Object r( (self->NAME)( a ) ); \
69             return Py::new_reference_to( r.ptr() ); \
70         } \
71         catch( Py::BaseException & ) \
72         { \
73             return 0; \
74         } \
75     }
76 #define PYCXX_KEYWORDS_METHOD_DECL( CLS, NAME ) \
77     static PyObject *PYCXX_KEYWORDS_METHOD_NAME( NAME )( PyObject *_self, PyObject *_a, PyObject *_k ) \
78     { \
79         try \
80         { \
81             Py::PythonClassInstance *self_python = reinterpret_cast< Py::PythonClassInstance * >( _self ); \
82             CLS *self = reinterpret_cast< CLS * >( self_python->m_pycxx_object ); \
83             Py::Tuple a( _a ); \
84             Py::Dict k; \
85             if( _k != NULL ) \
86                 k = _k; \
87             Py::Object r( (self->NAME)( a, k ) ); \
88             return Py::new_reference_to( r.ptr() ); \
89         } \
90         catch( Py::BaseException & ) \
91         { \
92             return 0; \
93         } \
94     }
95 
96 // need to support METH_STATIC and METH_CLASS
97 
98 #define PYCXX_ADD_NOARGS_METHOD( PYNAME, NAME, docs ) \
99     add_method( #PYNAME, (PyCFunction)PYCXX_NOARGS_METHOD_NAME( NAME ), METH_NOARGS, docs )
100 #define PYCXX_ADD_VARARGS_METHOD( PYNAME, NAME, docs ) \
101     add_method( #PYNAME, (PyCFunction)PYCXX_VARARGS_METHOD_NAME( NAME ), METH_VARARGS, docs )
102 #define PYCXX_ADD_KEYWORDS_METHOD( PYNAME, NAME, docs ) \
103     add_method( #PYNAME, (PyCFunction)PYCXX_KEYWORDS_METHOD_NAME( NAME ), METH_VARARGS | METH_KEYWORDS, docs )
104 
105 namespace Py
106 {
107     extern PythonExtensionBase *getPythonExtensionBase( PyObject *self );
108     struct PythonClassInstance
109     {
110         PyObject_HEAD
111         PythonExtensionBase *m_pycxx_object;
112     };
113 
114     class ExtensionClassMethodsTable
115     {
116     public:
ExtensionClassMethodsTable()117         ExtensionClassMethodsTable()
118         : m_methods_table( new PyMethodDef[ METHOD_TABLE_SIZE_INCREMENT ] )
119         , m_methods_used( 0 )
120         , m_methods_size( METHOD_TABLE_SIZE_INCREMENT )
121         {
122             // add the sentinel marking the table end
123             PyMethodDef *p = &m_methods_table[ 0 ];
124 
125             p->ml_name = NULL;
126             p->ml_meth = NULL;
127             p->ml_flags = 0;
128             p->ml_doc = NULL;
129         }
130 
~ExtensionClassMethodsTable()131         ~ExtensionClassMethodsTable()
132         {
133             delete[] m_methods_table;
134         }
135 
136         // check that all methods added are unique
check_unique_method_name(const char * _name)137         void check_unique_method_name( const char *_name )
138         {
139             std::string name( _name );
140             for( int i=0; i<m_methods_used; i++ )
141             {
142                 if( name == m_methods_table[i].ml_name )
143                 {
144                     throw AttributeError( name );
145                 }
146             }
147         }
add_method(const char * name,PyCFunction function,int flags,const char * doc)148         PyMethodDef *add_method( const char *name, PyCFunction function, int flags, const char *doc )
149         {
150             check_unique_method_name( name );
151 
152             // see if there is enough space for one more method
153             if( m_methods_used == (m_methods_size-1) )
154             {
155                 PyMethodDef *old_mt = m_methods_table;
156                 m_methods_size += METHOD_TABLE_SIZE_INCREMENT;
157                 PyMethodDef *new_mt = new PyMethodDef[ m_methods_size ];
158                 for( int i=0; i<m_methods_used; i++ )
159                 {
160                     new_mt[ i ] = old_mt[ i ];
161                 }
162                 delete[] old_mt;
163                 m_methods_table = new_mt;
164             }
165 
166             // add method into the table
167             PyMethodDef *p = &m_methods_table[ m_methods_used ];
168             p->ml_name = name;
169             p->ml_meth = function;
170             p->ml_flags = flags;
171             p->ml_doc = doc;
172 
173             m_methods_used++;
174             p++;
175 
176             // add the sentinel marking the table end
177             p->ml_name = NULL;
178             p->ml_meth = NULL;
179             p->ml_flags = 0;
180             p->ml_doc = NULL;
181 
182             return m_methods_table;
183         }
184 
185     private:
186         enum {METHOD_TABLE_SIZE_INCREMENT = 1};
187         PyMethodDef *m_methods_table;
188         int m_methods_used;
189         int m_methods_size;
190     };
191 
192     template<TEMPLATE_TYPENAME T> class PythonClass
193     : public PythonExtensionBase
194     {
195     protected:
PythonClass(PythonClassInstance * self,Tuple &,Dict &)196         explicit PythonClass( PythonClassInstance *self, Tuple &/*args*/, Dict &/*kwds*/ )
197         : PythonExtensionBase()
198         , m_class_instance( self )
199         {
200         }
201 
~PythonClass()202         virtual ~PythonClass()
203         {}
204 
methodTable()205         static ExtensionClassMethodsTable &methodTable()
206         {
207             static ExtensionClassMethodsTable *method_table;
208             if( method_table == NULL )
209                 method_table = new ExtensionClassMethodsTable;
210             return *method_table;
211         }
212 
add_method(const char * name,PyCFunction function,int flags,const char * doc=NULL)213         static void add_method( const char *name, PyCFunction function, int flags, const char *doc=NULL )
214         {
215             behaviors().set_methods( methodTable().add_method( name, function, flags, doc ) );
216         }
217 
behaviors()218         static PythonType &behaviors()
219         {
220             static PythonType *p;
221             if( p == NULL )
222             {
223 #if defined( _CPPRTTI ) || defined( __GNUG__ )
224                 const char *default_name = (typeid( T )).name();
225 #else
226                 const char *default_name = "unknown";
227 #endif
228                 p = new PythonType( sizeof( PythonClassInstance ), 0, default_name );
229                 p->set_tp_new( extension_object_new );
230                 p->set_tp_init( extension_object_init );
231                 p->set_tp_dealloc( extension_object_deallocator );
232 
233                 // we are a class
234                 p->supportClass();
235 
236                 // always support get and set attr
237                 p->supportGetattro();
238                 p->supportSetattro();
239             }
240 
241             return *p;
242         }
243 
extension_object_new(PyTypeObject * subtype,PyObject *,PyObject *)244         static PyObject *extension_object_new( PyTypeObject *subtype, PyObject * /*args*/, PyObject * /*kwds*/ )
245         {
246 #ifdef PYCXX_DEBUG
247             std::cout << "extension_object_new()" << std::endl;
248 #endif
249 #if defined( Py_LIMITED_API )
250             PyObject *object = reinterpret_cast<allocfunc>( PyType_GetSlot( subtype, Py_tp_alloc ) )( subtype, 0 );
251 #else
252             PyObject *object = subtype->tp_alloc( subtype, 0 );
253 #endif
254             if( object == NULL )
255                 return NULL;
256 
257             PythonClassInstance *o = reinterpret_cast<PythonClassInstance *>( object );
258             o->m_pycxx_object = NULL;
259 
260             PyObject *self = reinterpret_cast<PyObject *>( o );
261 #ifdef PYCXX_DEBUG
262             std::cout << "extension_object_new() => self=0x" << std::hex << reinterpret_cast< unsigned long >( self ) << std::dec << std::endl;
263 #endif
264             return self;
265         }
266 
extension_object_init(PyObject * _self,PyObject * args_,PyObject * kwds_)267         static int extension_object_init( PyObject *_self, PyObject *args_, PyObject *kwds_ )
268         {
269             try
270             {
271                 Py::Tuple args( args_ );
272                 Py::Dict kwds;
273                 if( kwds_ != NULL )
274                     kwds = kwds_;
275 
276                 PythonClassInstance *self = reinterpret_cast<PythonClassInstance *>( _self );
277 #ifdef PYCXX_DEBUG
278                 std::cout << "extension_object_init( self=0x" << std::hex << reinterpret_cast< unsigned long >( self ) << std::dec << " )" << std::endl;
279                 std::cout << "    self->m_pycxx_object=0x" << std::hex << reinterpret_cast< unsigned long >( self->m_pycxx_object ) << std::dec << std::endl;
280 #endif
281 
282                 if( self->m_pycxx_object == NULL )
283                 {
284                     self->m_pycxx_object = new T( self, args, kwds );
285 #ifdef PYCXX_DEBUG
286                     std::cout << "    self->m_pycxx_object=0x" << std::hex << reinterpret_cast< unsigned long >( self->m_pycxx_object ) << std::dec << std::endl;
287 #endif
288                 }
289                 else
290                 {
291 #ifdef PYCXX_DEBUG
292                     std::cout << "    reinit - self->m_pycxx_object=0x" << std::hex << reinterpret_cast< unsigned long >( self->m_pycxx_object ) << std::dec << std::endl;
293 #endif
294                     self->m_pycxx_object->reinit( args, kwds );
295                 }
296             }
297             catch( BaseException & )
298             {
299                 return -1;
300             }
301             return 0;
302         }
303 
extension_object_deallocator(PyObject * _self)304         static void extension_object_deallocator( PyObject *_self )
305         {
306             PythonClassInstance *self = reinterpret_cast< PythonClassInstance * >( _self );
307 #ifdef PYCXX_DEBUG
308             std::cout << "extension_object_deallocator( self=0x" << std::hex << reinterpret_cast< unsigned long >( self ) << std::dec << " )" << std::endl;
309             std::cout << "    self->m_pycxx_object=0x" << std::hex << reinterpret_cast< unsigned long >( self->m_pycxx_object ) << std::dec << std::endl;
310 #endif
311             delete self->m_pycxx_object;
312 #ifdef Py_LIMITED_API
313             freefunc fn = reinterpret_cast<freefunc>( PyType_GetSlot( _self->ob_type, Py_tp_free ) );
314             fn( _self );
315 #else
316             _self->ob_type->tp_free( _self );
317 #endif
318         }
319 
320     public:
type_object()321         static PyTypeObject *type_object()
322         {
323             return behaviors().type_object();
324         }
325 
type()326         static Object type()
327         {
328             return Object( reinterpret_cast<PyObject *>( behaviors().type_object() ) );
329         }
330 
check(PyObject * p)331         static bool check( PyObject *p )
332         {
333             // is p a me or a derived me
334             switch( PyObject_IsInstance( p, reinterpret_cast<PyObject *>( type_object() ) ) )
335             {
336                 default:
337                 case -1:
338                     throw Exception();
339                 case 0:
340                     return false;
341                 case 1:
342                     return true;
343             }
344         }
345 
check(const Object & ob)346         static bool check( const Object &ob )
347         {
348             return check( ob.ptr() );
349         }
350 
selfPtr()351         virtual PyObject *selfPtr()
352         {
353             return reinterpret_cast<PyObject *>( m_class_instance );
354         }
355 
self()356         virtual Object self()
357         {
358             return Object( reinterpret_cast<PyObject *>( m_class_instance ) );
359         }
360 
361     protected:
362     private:
363         PythonClassInstance *m_class_instance;
364 
365     private:
366         //
367         // prevent the compiler generating these unwanted functions
368         //
369         explicit PythonClass( const PythonClass<T> &other );
370         void operator=( const PythonClass<T> &rhs );
371     };
372 
373     //
374     // ExtensionObject<T> is an Object that will accept only T's.
375     //
376     template<TEMPLATE_TYPENAME T>
377     class PythonClassObject: public Object
378     {
379     public:
380 
PythonClassObject(PyObject * pyob)381         explicit PythonClassObject( PyObject *pyob )
382         : Object( pyob )
383         {
384             validate();
385         }
386 
PythonClassObject(const PythonClassObject<T> & other)387         PythonClassObject( const PythonClassObject<T> &other )
388         : Object( *other )
389         {
390             validate();
391         }
392 
PythonClassObject(const Object & other)393         PythonClassObject( const Object &other )
394         : Object( *other )
395         {
396             validate();
397         }
398 
operator =(const Object & rhs)399         PythonClassObject &operator=( const Object &rhs )
400         {
401             *this = *rhs;
402             return *this;
403         }
404 
operator =(PyObject * rhsp)405         PythonClassObject &operator=( PyObject *rhsp )
406         {
407             if( ptr() != rhsp )
408                 set( rhsp );
409             return *this;
410         }
411 
accepts(PyObject * pyob) const412         virtual bool accepts( PyObject *pyob ) const
413         {
414             return( pyob && T::check( pyob ) );
415         }
416 
417         //
418         //    Obtain a pointer to the PythonExtension object
419         //
getCxxObject(void)420         T *getCxxObject( void )
421         {
422             return dynamic_cast< T * >( getPythonExtensionBase( ptr() ) );
423         }
424     };
425 } // Namespace Py
426 
427 // End of __CXX_ExtensionClass__h
428 #endif
429