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