1# Copyright 2004-2008 Roman Yakovenko. 2# Distributed under the Boost Software License, Version 1.0. (See 3# accompanying file LICENSE_1_0.txt or copy at 4# http://www.boost.org/LICENSE_1_0.txt) 5 6import os 7import types 8from . import scoped 9from . import calldef 10from . import compound 11from . import algorithm 12from . import code_creator 13from . import smart_pointers 14from . import declaration_based 15from . import registration_based 16from pygccxml import declarations 17 18class class_declaration_t( scoped.scoped_t 19 , registration_based.registration_based_t ): 20 def __init__(self, class_inst ): 21 scoped.scoped_t.__init__( self, declaration=class_inst ) 22 registration_based.registration_based_t.__init__( self ) 23 self.works_on_instance = False 24 25 def _generate_class_definition(self): 26 class_identifier = algorithm.create_identifier( self, '::boost::python::class_' ) 27 return declarations.templates.join( class_identifier, [self.decl_identifier] ) 28 29 def _generate_code_no_scope(self): 30 result = [] 31 result.append( self._generate_class_definition() + '("%s")' % self.declaration.alias ) 32 for x in self.creators: 33 code = x.create() 34 tmpl = '%s.%s' 35 if self.is_comment( code ): 36 tmpl = '%s%s' 37 if code: 38 result.append( self.indent( tmpl % ( os.linesep, code ) ) ) 39 result.append( ';' ) 40 return ''.join( result ) 41 42 @property 43 def class_var_name(self): 44 return self.declaration.class_var_name 45 46 def is_exposed_using_scope(self): 47 if self.declaration.always_expose_using_scope: 48 return True 49 return bool( [cc for cc in self.creators if not cc.works_on_instance] ) 50 51 @property 52 def typedef_name( self ): 53 return self.class_var_name + '_t' 54 55 def _generate_code_with_scope(self): 56 result = [] 57 scope_var_name = self.alias + '_scope' 58 result.append( 'typedef ' + self._generate_class_definition() + ' ' + self.typedef_name + ';') 59 result.append( self.typedef_name + ' ' + self.class_var_name ) 60 result[-1] = result[-1] + ' = '+ self.typedef_name + '("%s");' % self.declaration.alias 61 62 result.append( algorithm.create_identifier( self, '::boost::python::scope' ) ) 63 result[-1] = result[-1] + ' ' + scope_var_name 64 result[-1] = result[-1] + '( %s );' % self.class_var_name 65 66 for x in self.creators: 67 if not x.works_on_instance: 68 result.append( x.create() ) 69 else: 70 result.append( '%s.%s;' % ( self.class_var_name, x.create() ) ) 71 72 code = os.linesep.join( result ) 73 74 result = [ '{ //scope begin' ] 75 result.append( self.indent( code ) ) 76 result.append( '} //scope end' ) 77 78 return os.linesep.join( result ) 79 80 def _create_impl(self): 81 if self.declaration.already_exposed: 82 return '' 83 if self.is_exposed_using_scope(): 84 return self._generate_code_with_scope() 85 else: 86 return self._generate_code_no_scope() 87 88 def _get_system_files_impl( self ): 89 return [] 90 91class class_t( scoped.scoped_t, registration_based.registration_based_t ): 92 """ 93 Creates boost.python code that needed to export a class 94 """ 95 def __init__(self, class_inst, wrapper=None ): 96 scoped.scoped_t.__init__( self, declaration=class_inst ) 97 registration_based.registration_based_t.__init__( self ) 98 self._wrapper = wrapper 99 self.works_on_instance = False 100 101 def _get_wrapper( self ): 102 return self._wrapper 103 def _set_wrapper( self, new_wrapper ): 104 self._wrapper = new_wrapper 105 wrapper = property( _get_wrapper, _set_wrapper ) 106 107 def _get_held_type(self): 108 return self.declaration.held_type 109 def _set_held_type(self, held_type): 110 assert isinstance( held_type, type(None) ) \ 111 or isinstance( held_type, smart_pointers.held_type_t ) \ 112 or isinstance( held_type, str ) 113 if isinstance( held_type, str ): 114 assert held_type # should be non emptry string 115 self.declaration.held_type = held_type 116 held_type = property( _get_held_type, _set_held_type ) 117 118 def _exported_base_classes(self): 119 if not self.declaration.bases: 120 return {}, {} 121 base_classes = {} 122 for hierarchy_info in self.declaration.recursive_bases: 123 if hierarchy_info.access_type == declarations.ACCESS_TYPES.PRIVATE: 124 continue 125 base_classes[ id( hierarchy_info.related_class ) ] = hierarchy_info 126 base_classes_size = len( base_classes ) 127 creators = {} 128 creators_len = 0 129 for creator in algorithm.make_flatten_generator( self.top_parent.body.creators ): 130 if isinstance( creator, class_t ) and id(creator.declaration) in base_classes: 131 creators[ id(creator.declaration) ] = creator 132 if len( creators ) == base_classes_size: 133 break #all classes has been found 134 return base_classes, creators 135 136 def _get_base_operators(self, base_classes, base_creators): 137 #May be in future I will redefine operators on wrapper class 138 #thus I will support [protected|private] [ [not|pure|] virtual] operators. 139 operator_creators = [] 140 for base_creator in list(base_creators.values()): 141 hierarchy_info = base_classes[ id( base_creator.declaration )] 142 if hierarchy_info.access_type != declarations.ACCESS_TYPES.PUBLIC: 143 continue 144 base_operator_creators = [creator for creator in base_creator.creators if isinstance( creator, calldef.operator_t ) 145 and isinstance( creator.declaration, declarations.member_operator_t ) 146 and creator.declaration.access_type 147 == declarations.ACCESS_TYPES.PUBLIC] 148 operator_creators.extend( base_operator_creators ) 149 return operator_creators 150 151 def _generate_noncopyable(self): 152 noncopyable_vars = declarations.find_noncopyable_vars(self.declaration) 153 copy_constr = declarations.find_copy_constructor(self.declaration) 154 155 if self.declaration.noncopyable \ 156 or copy_constr and copy_constr.is_artificial and noncopyable_vars: 157 return algorithm.create_identifier( self, '::boost::noncopyable' ) 158 159 def _generate_bases(self, base_creators): 160 bases = [] 161 assert isinstance( self.declaration, declarations.class_t ) 162 for base_desc in self.declaration.bases: 163 assert isinstance( base_desc, declarations.hierarchy_info_t ) 164 if base_desc.access != declarations.ACCESS_TYPES.PUBLIC: 165 continue 166 if id(base_desc.related_class) in base_creators: 167 bases.append( algorithm.create_identifier( self, base_desc.related_class.partial_decl_string ) ) 168 elif base_desc.related_class.already_exposed: 169 bases.append( base_desc.related_class.partial_decl_string ) 170 if not bases: 171 return None 172 bases_identifier = algorithm.create_identifier( self, '::boost::python::bases' ) 173 return declarations.templates.join( bases_identifier, bases ) 174 175 def _generated_held_type(self): 176 if isinstance( self.held_type, smart_pointers.held_type_t ): 177 return self.held_type.create( self ) 178 elif isinstance( self.held_type, str): 179 return self.held_type 180 else: 181 return None 182 183 def _generate_class_definition(self, base_creators): 184 class_identifier = algorithm.create_identifier( self, '::boost::python::class_' ) 185 args = [] 186 187 held_type = self._generated_held_type() 188 if self.wrapper: 189 if self.declaration.exposed_class_type == self.declaration.EXPOSED_CLASS_TYPE.WRAPPER: 190 args.append( self.wrapper.full_name ) 191 else: 192 if not self.target_configuration.boost_python_has_wrapper_held_type \ 193 or self.declaration.require_self_reference: 194 args.append( self.decl_identifier ) 195 if self.declaration.require_self_reference: 196 if not held_type: 197 args.append( self.wrapper.full_name ) 198 else: 199 args.append( self.wrapper.full_name ) 200 else: 201 args.append( self.decl_identifier ) 202 203 bases = self._generate_bases(base_creators) 204 if bases: 205 args.append( bases ) 206 207 if held_type: 208 args.append( held_type ) 209 notcopyable = self._generate_noncopyable() 210 if notcopyable: 211 args.append( notcopyable ) 212 return declarations.templates.join( class_identifier, args) 213 214 def _generate_constructor(self): 215 result = [] 216 result.append( '(' ) 217 result.append( ' "%s"' % self.alias ) 218 if self.documentation: 219 result.append( ', %s' % self.documentation ) 220 used_init = None 221 inits = [x for x in self.creators if isinstance( x, calldef.constructor_t )] 222 223 trivial_constructor = declarations.find_trivial_constructor(self.declaration) 224 225 if self.declaration.no_init: 226 result.append( ", " ) 227 result.append( algorithm.create_identifier( self, '::boost::python::no_init' ) ) 228 else: 229 if inits: 230 used_init = inits[0] 231 result.append( ", " ) 232 result.append( used_init.create_init_code() ) 233 result.append( ' )' ) 234 return ( ''.join( result ), used_init ) 235 236 def _generate_code_no_scope(self): 237 result = [] 238 base_classes, base_creators = self._exported_base_classes() 239 result.append( self._generate_class_definition(base_creators) ) 240 class_constructor, used_init = self._generate_constructor() 241 result.append( class_constructor ) 242 creators = self.creators 243 if self.declaration.redefine_operators: 244 creators = self.creators + self._get_base_operators(base_classes, base_creators) 245 for x in creators: 246 if not ( x is used_init ): 247 code = x.create() 248 tmpl = '%s.%s' 249 if self.is_comment( code ): 250 tmpl = '%s%s' 251 if code: 252 result.append( self.indent( tmpl % ( os.linesep, code ) ) ) 253 result.append( ';' ) 254 return ''.join( result ) 255 256 @property 257 def class_var_name(self): 258 return self.declaration.class_var_name 259 260 @property 261 def typedef_name( self ): 262 return self.class_var_name + '_t' 263 264 def create_typedef_code( self ): 265 base_classes, base_creators = self._exported_base_classes() 266 return 'typedef ' + self._generate_class_definition(base_creators) + ' ' + self.typedef_name + ';' 267 268 269 def _generate_code_with_scope(self): 270 result = [] 271 scope_var_name = self.alias + '_scope' 272 base_classes, base_creators = self._exported_base_classes() 273 result.append( 'typedef ' + self._generate_class_definition(base_creators) + ' ' + self.typedef_name + ';') 274 result.append( self.typedef_name + ' ' + self.class_var_name ) 275 result[-1] = result[-1] + ' = ' 276 class_constructor, used_init = self._generate_constructor() 277 result[-1] = result[-1] + self.typedef_name + class_constructor 278 result[-1] = result[-1] + ';' 279 280 result.append( algorithm.create_identifier( self, '::boost::python::scope' ) ) 281 result[-1] = result[-1] + ' ' + scope_var_name 282 result[-1] = result[-1] + '( %s );' % self.class_var_name 283 284 creators = self.creators 285 if self.declaration.redefine_operators: 286 creators = self.creators + self._get_base_operators(base_classes, base_creators) 287 288 for x in creators: 289 if x is used_init: 290 continue 291 if isinstance( x, ( calldef.calldef_t, calldef.calldef_overloads_t ) ): 292 x.works_on_instance = False 293 code = x.create() 294 if code: 295 result.append( code ) 296 continue 297 if not x.works_on_instance: 298 code = x.create() 299 if code: 300 result.append( code ) 301 else: 302 result.append( '%s.%s;' % ( self.class_var_name, x.create() ) ) 303 304 code = os.linesep.join( result ) 305 306 result = [ '{ //%s' % declarations.full_name( self.declaration, with_defaults=False ) ] 307 result.append( self.indent( code ) ) 308 result.append( '}' ) 309 310 return os.linesep.join( result ) 311 312 def is_exposed_using_scope(self): 313 if self.declaration.always_expose_using_scope: 314 return True 315 return bool( [cc for cc in self.creators if not cc.works_on_instance] ) 316 317 def _create_impl(self): 318 if self.declaration.already_exposed: 319 return '' 320 if self.is_exposed_using_scope(): 321 return self._generate_code_with_scope() 322 else: 323 return self._generate_code_no_scope() 324 325 def _get_system_files_impl( self ): 326 return [] 327 328class class_wrapper_t( scoped.scoped_t ): 329 """ 330 creates C++ code, which creates wrapper around a class 331 """ 332 333 def __init__(self, declaration, class_creator ): 334 scoped.scoped_t.__init__( self, declaration=declaration ) 335 self._class_creator = class_creator 336 self._base_wrappers = [] 337 338 def _get_wrapper_alias( self ): 339 return self.declaration.wrapper_alias 340 def _set_wrapper_alias( self, walias ): 341 self.declaration.wrapper_alias = walias 342 wrapper_alias = property( _get_wrapper_alias, _set_wrapper_alias ) 343 344 @property 345 def base_wrappers( self ): 346 if self.declaration.is_abstract and not self._base_wrappers: 347 bases = [ hi.related_class for hi in self.declaration.bases ] 348 creators_before_me = algorithm.creators_affect_on_me( self ) 349 self._base_wrappers \ 350 = [creator for creator in creators_before_me if isinstance( creator, class_wrapper_t ) 351 and creator.declaration in bases] 352 return self._base_wrappers 353 354 @property 355 def exposed_identifier(self): 356 return algorithm.create_identifier( self, self.declaration.partial_decl_string ) 357 358 @property 359 def class_creator(self): 360 return self._class_creator 361 362 @property 363 def full_name( self ): 364 if not isinstance( self.parent, class_wrapper_t ): 365 return self.declaration.wrapper_alias 366 else: 367 full_name = [self.wrapper_alias] 368 #may be we deal with enum 369 parent = self.parent 370 while isinstance( parent, class_wrapper_t ): 371 full_name.append( parent.wrapper_alias ) 372 parent = parent.parent 373 full_name.reverse() 374 return '::'.join( full_name ) 375 376 @property 377 def held_type(self): 378 return self._class_creator.held_type 379 380 @property 381 def boost_wrapper_identifier(self): 382 boost_wrapper = algorithm.create_identifier( self, '::boost::python::wrapper' ) 383 return declarations.templates.join( boost_wrapper, [self.exposed_identifier] ) 384 385 def _create_bases(self): 386 return ', '.join( [self.exposed_identifier, self.boost_wrapper_identifier] ) 387 388 def _create_impl(self): 389 if self.declaration.already_exposed: 390 return '' 391 answer = ['struct %s : %s {' % ( self.wrapper_alias, self._create_bases() )] 392 answer.append( '' ) 393 answer.append( self.create_internal_code( self.creators ) ) 394 answer.append( '' ) 395 answer.append( '};' ) 396 return os.linesep.join( answer ) 397 398 def _get_system_files_impl( self ): 399 return [] 400 401 402ctypes_base_classes = { 403 declarations.CLASS_TYPES.CLASS : 'Structure' 404 , declarations.CLASS_TYPES.UNION : 'Union' 405 , declarations.CLASS_TYPES.STRUCT : 'Structure' 406} 407 408class class_introduction_t(compound.compound_t, declaration_based.declaration_based_t): 409 def __init__( self, class_ ): 410 compound.compound_t.__init__(self) 411 declaration_based.declaration_based_t.__init__( self, class_ ) 412 413 @property 414 def ctypes_base_class( self ): 415 global ctypes_base_classes 416 return ctypes_base_classes[ self.declaration.class_type ] 417 418 def _create_impl(self): 419 result = [] 420 result.append( "class %(alias)s(ctypes.%(base)s):" 421 % dict( alias=self.alias, base=self.ctypes_base_class ) ) 422 result.append( self.indent( '"""class %s"""' % self.decl_identifier ) ) 423 if self.creators: 424 result.append( self.indent( '' ) ) 425 result.append( compound.compound_t.create_internal_code( self.creators ) ) 426 427 if isinstance( self.declaration.parent, declarations.namespace_t ) \ 428 and self.declaration.parent is not self.declaration.top_parent: #not a global namespace 429 result.append("") 430 result.append( '%(ns_full_name)s = %(name)s' 431 % dict( ns_full_name=self.complete_py_name, name=self.alias )) 432 return os.linesep.join( result ) 433 434 def _get_system_files_impl( self ): 435 return [] 436 437 438class class_declaration_introduction_t(code_creator.code_creator_t, declaration_based.declaration_based_t): 439 def __init__( self, class_declaration ): 440 code_creator.code_creator_t.__init__(self) 441 declaration_based.declaration_based_t.__init__( self, class_declaration ) 442 443 def _create_impl(self): 444 result = [] 445 result.append( "class %s(ctypes.Structure):" % self.alias ) 446 result.append( self.indent( '"""class declaration %s"""' % self.decl_identifier ) ) 447 result.append( self.indent( '_fields_ = []' ) ) 448 449 if isinstance( self.declaration.parent, declarations.namespace_t ) \ 450 and self.declaration.parent is not self.declaration.top_parent: #not a global namespace 451 result.append( '%(ns_full_name)s = %(name)s' 452 % dict( ns_full_name=self.complete_py_name, name=self.alias )) 453 return os.linesep.join( result ) 454 455 def _get_system_files_impl( self ): 456 return [] 457