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