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 
109     struct PythonClassInstance
110     {
111         PyObject_HEAD
112         PythonExtensionBase *m_pycxx_object;
113     };
114 
115     class ExtensionClassMethodsTable
116     {
117     public:
ExtensionClassMethodsTable()118         ExtensionClassMethodsTable()
119         : m_methods_table( new PyMethodDef[ METHOD_TABLE_SIZE_INCREMENT ] )
120         , m_methods_used( 0 )
121         , m_methods_size( METHOD_TABLE_SIZE_INCREMENT )
122         {
123             // add the sentinel marking the table end
124             PyMethodDef *p = &m_methods_table[ 0 ];
125 
126             p->ml_name = NULL;
127             p->ml_meth = NULL;
128             p->ml_flags = 0;
129             p->ml_doc = NULL;
130         }
131 
~ExtensionClassMethodsTable()132         ~ExtensionClassMethodsTable()
133         {
134             delete[] m_methods_table;
135         }
136 
137         // check that all methods added are unique
check_unique_method_name(const char * _name)138         void check_unique_method_name( const char *_name )
139         {
140             std::string name( _name );
141             for( int i=0; i<m_methods_used; i++ )
142             {
143                 if( name == m_methods_table[i].ml_name )
144                 {
145                     throw AttributeError( name );
146                 }
147             }
148         }
add_method(const char * name,PyCFunction function,int flags,const char * doc)149         PyMethodDef *add_method( const char *name, PyCFunction function, int flags, const char *doc )
150         {
151             check_unique_method_name( name );
152 
153             // see if there is enough space for one more method
154             if( m_methods_used == (m_methods_size-1) )
155             {
156                 PyMethodDef *old_mt = m_methods_table;
157                 m_methods_size += METHOD_TABLE_SIZE_INCREMENT;
158                 PyMethodDef *new_mt = new PyMethodDef[ m_methods_size ];
159                 for( int i=0; i<m_methods_used; i++ )
160                 {
161                     new_mt[ i ] = old_mt[ i ];
162                 }
163                 delete[] old_mt;
164                 m_methods_table = new_mt;
165             }
166 
167             // add method into the table
168             PyMethodDef *p = &m_methods_table[ m_methods_used ];
169             p->ml_name = const_cast<char *>( name );
170             p->ml_meth = function;
171             p->ml_flags = flags;
172             p->ml_doc = const_cast<char *>( doc );
173 
174             m_methods_used++;
175             p++;
176 
177             // add the sentinel marking the table end
178             p->ml_name = NULL;
179             p->ml_meth = NULL;
180             p->ml_flags = 0;
181             p->ml_doc = NULL;
182 
183             return m_methods_table;
184         }
185 
186     private:
187         enum {METHOD_TABLE_SIZE_INCREMENT = 1};
188         PyMethodDef *m_methods_table;
189         int m_methods_used;
190         int m_methods_size;
191     };
192 
193     template<TEMPLATE_TYPENAME T> class PythonClass
194     : public PythonExtensionBase
195     {
196     protected:
PythonClass(PythonClassInstance * self,Tuple &,Dict &)197         explicit PythonClass( PythonClassInstance *self, Tuple &/*args*/, Dict &/*kwds*/ )
198         : PythonExtensionBase()
199         , m_class_instance( self )
200         {
201         }
202 
~PythonClass()203         virtual ~PythonClass()
204         {}
205 
methodTable()206         static ExtensionClassMethodsTable &methodTable()
207         {
208             static ExtensionClassMethodsTable *method_table;
209             if( method_table == NULL )
210                 method_table = new ExtensionClassMethodsTable;
211             return *method_table;
212         }
213 
add_method(const char * name,PyCFunction function,int flags,const char * doc=NULL)214         static void add_method( const char *name, PyCFunction function, int flags, const char *doc=NULL )
215         {
216             behaviors().set_methods( methodTable().add_method( name, function, flags, doc ) );
217         }
218 
behaviors()219         static PythonType &behaviors()
220         {
221             static PythonType *p;
222             if( p == NULL )
223             {
224 #if defined( _CPPRTTI ) || defined( __GNUG__ )
225                 const char *default_name = (typeid( T )).name();
226 #else
227                 const char *default_name = "unknown";
228 #endif
229                 p = new PythonType( sizeof( PythonClassInstance ), 0, default_name );
230                 p->set_tp_new( extension_object_new );
231                 p->set_tp_init( extension_object_init );
232                 p->set_tp_dealloc( extension_object_deallocator );
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             PythonClassInstance *o = reinterpret_cast<PythonClassInstance *>( subtype->tp_alloc( subtype, 0 ) );
250             if( o == NULL )
251                 return NULL;
252 
253             o->m_pycxx_object = NULL;
254 
255             PyObject *self = reinterpret_cast<PyObject *>( o );
256 #ifdef PYCXX_DEBUG
257             std::cout << "extension_object_new() => self=0x" << std::hex << reinterpret_cast< unsigned long >( self ) << std::dec << std::endl;
258 #endif
259             return self;
260         }
261 
extension_object_init(PyObject * _self,PyObject * args_,PyObject * kwds_)262         static int extension_object_init( PyObject *_self, PyObject *args_, PyObject *kwds_ )
263         {
264             try
265             {
266                 Py::Tuple args( args_ );
267                 Py::Dict kwds;
268                 if( kwds_ != NULL )
269                     kwds = kwds_;
270 
271                 PythonClassInstance *self = reinterpret_cast<PythonClassInstance *>( _self );
272 #ifdef PYCXX_DEBUG
273                 std::cout << "extension_object_init( self=0x" << std::hex << reinterpret_cast< unsigned long >( self ) << std::dec << " )" << std::endl;
274                 std::cout << "    self->m_pycxx_object=0x" << std::hex << reinterpret_cast< unsigned long >( self->m_pycxx_object ) << std::dec << std::endl;
275 #endif
276 
277                 if( self->m_pycxx_object == NULL )
278                 {
279                     self->m_pycxx_object = new T( self, args, kwds );
280 #ifdef PYCXX_DEBUG
281                     std::cout << "    self->m_pycxx_object=0x" << std::hex << reinterpret_cast< unsigned long >( self->m_pycxx_object ) << std::dec << std::endl;
282 #endif
283                 }
284                 else
285                 {
286 #ifdef PYCXX_DEBUG
287                     std::cout << "    reinit - self->m_pycxx_object=0x" << std::hex << reinterpret_cast< unsigned long >( self->m_pycxx_object ) << std::dec << std::endl;
288 #endif
289                     self->m_pycxx_object->reinit( args, kwds );
290                 }
291             }
292             catch( BaseException & )
293             {
294                 return -1;
295             }
296             return 0;
297         }
298 
extension_object_deallocator(PyObject * _self)299         static void extension_object_deallocator( PyObject *_self )
300         {
301             PythonClassInstance *self = reinterpret_cast< PythonClassInstance * >( _self );
302 #ifdef PYCXX_DEBUG
303             std::cout << "extension_object_deallocator( self=0x" << std::hex << reinterpret_cast< unsigned long >( self ) << std::dec << " )" << std::endl;
304             std::cout << "    self->m_pycxx_object=0x" << std::hex << reinterpret_cast< unsigned long >( self->m_pycxx_object ) << std::dec << std::endl;
305 #endif
306             delete self->m_pycxx_object;
307             _self->ob_type->tp_free( _self );
308         }
309 
310     public:
type_object()311         static PyTypeObject *type_object()
312         {
313             return behaviors().type_object();
314         }
315 
type()316         static Object type()
317         {
318             return Object( reinterpret_cast<PyObject *>( behaviors().type_object() ) );
319         }
320 
check(PyObject * p)321         static bool check( PyObject *p )
322         {
323             // is p a me or a derived me
324             switch( PyObject_IsInstance( p, reinterpret_cast<PyObject *>( type_object() ) ) )
325             {
326                 default:
327                 case -1:
328                     throw Exception();
329                 case 0:
330                     return false;
331                 case 1:
332                     return true;
333             }
334         }
335 
check(const Object & ob)336         static bool check( const Object &ob )
337         {
338             return check( ob.ptr() );
339         }
340 
selfPtr()341         virtual PyObject *selfPtr()
342         {
343             return reinterpret_cast<PyObject *>( m_class_instance );
344         }
345 
self()346         virtual Object self()
347         {
348             return Object( reinterpret_cast<PyObject *>( m_class_instance ) );
349         }
350 
351     protected:
352     private:
353         PythonClassInstance *m_class_instance;
354 
355     private:
356         //
357         // prevent the compiler generating these unwanted functions
358         //
359         explicit PythonClass( const PythonClass<T> &other );
360         void operator=( const PythonClass<T> &rhs );
361     };
362 
363     //
364     // ExtensionObject<T> is an Object that will accept only T's.
365     //
366     template<TEMPLATE_TYPENAME T>
367     class PythonClassObject: public Object
368     {
369     public:
370 
PythonClassObject(PyObject * pyob)371         explicit PythonClassObject( PyObject *pyob )
372         : Object( pyob )
373         {
374             validate();
375         }
376 
PythonClassObject(const PythonClassObject<T> & other)377         PythonClassObject( const PythonClassObject<T> &other )
378         : Object( *other )
379         {
380             validate();
381         }
382 
PythonClassObject(const Object & other)383         PythonClassObject( const Object &other )
384         : Object( *other )
385         {
386             validate();
387         }
388 
operator =(const Object & rhs)389         PythonClassObject &operator=( const Object &rhs )
390         {
391             *this = *rhs;
392             return *this;
393         }
394 
operator =(PyObject * rhsp)395         PythonClassObject &operator=( PyObject *rhsp )
396         {
397             if( ptr() != rhsp )
398                 set( rhsp );
399             return *this;
400         }
401 
accepts(PyObject * pyob) const402         virtual bool accepts( PyObject *pyob ) const
403         {
404             return( pyob && T::check( pyob ) );
405         }
406 
407         //
408         //    Obtain a pointer to the PythonExtension object
409         //
getCxxObject(void)410         T *getCxxObject( void )
411         {
412             return dynamic_cast< T * >( getPythonExtensionBase( ptr() ) );
413         }
414     };
415 } // Namespace Py
416 
417 // End of __CXX_ExtensionClass__h
418 #endif
419