1
2
3
4
5
6 """defines class that configure class definition and class declaration exposing"""
7
8 import os
9 import user_text
10 import properties
11 import decl_wrapper
12 import scopedef_wrapper
13 from pyplusplus import messages
14 from pygccxml import declarations
15 import indexing_suite1 as isuite1
16 import indexing_suite2 as isuite2
17
18 ACCESS_TYPES = declarations.ACCESS_TYPES
19 VIRTUALITY_TYPES = declarations.VIRTUALITY_TYPES
27
28
29 always_expose_using_scope_documentation = \
30 """boolean, configures how Py++ should generate code for class.
31 Py can generate code using IDL like syntax:
32
33 class_< ... >( ... )
34 .def( ... );
35
36 Or it can generate code using more complex form:
37
38 typedef bp::class_< my_class > my_class_exposer_t;
39 my_class_exposer_t my_class_exposer = my_class_exposer_t( "my_class" );
40 boost::python::scope my_class_scope( my_class_exposer );
41 my_class_exposer.def( ... );
42
43 Also, the second way is much longer, it solves few problems:
44
45 - you can not expose enums and internal classes defined within the class using first method
46 - you will get much better compilation errors
47 - the code looks like regular C++ code after all :-)
48
49 By default, this property is set to False. Also, Py++ knows pretty well
50 when it have to ignore this property and generate right code
51 """
54 """defines few properties that are common to
55 L{class declaration<pygccxml.declarations.class_declaration_t>} and
56 L{definition<pygccxml.declarations.class_t>} classes
57 """
59 object.__init__( self )
60 self._always_expose_using_scope = None
61 self._indexing_suite = None
62 self._equality_comparable = None
63 self._less_than_comparable = None
64 self._isuite_version = 1
65 self._opaque = False
66
68 return self._isuite_version
70 assert version in ( 1, 2 )
71 if self._isuite_version != version:
72 self._isuite_version = version
73 self._indexing_suite = None
74 indexing_suite_version = property( _get_indexing_suite_version, _set_indexing_suite_version
75 , doc="indexing suite version")
76
77 @property
79 """reference to indexing suite configuration class.
80
81 If the class is not STD container, this property will contain None"
82 """
83 if self._indexing_suite is None:
84 if self.container_traits:
85 if self._isuite_version == 1:
86 self._indexing_suite = isuite1.indexing_suite1_t( self )
87 else:
88 self._indexing_suite = isuite2.indexing_suite2_t( self )
89 return self._indexing_suite
90
97
108
110 self._always_expose_using_scope = value
111 always_expose_using_scope = property( _get_always_expose_using_scope, _set_always_expose_using_scope
112 , doc="please see L{class_wrapper.always_expose_using_scope_documentation} variable for documentation." )
113
115 if None is self._equality_comparable:
116 self._equality_comparable = declarations.has_public_equal( self )
117 return self._equality_comparable
118
120 self._equality_comparable = value
121
122 equality_comparable = property( _get_equality_comparable, _set_equality_comparable
123 , doc="indicates existence of public operator=" \
124 +"Default value is calculated, based on information presented in the declarations tree" )
125
127 if None is self._less_than_comparable:
128 self._less_than_comparable = declarations.has_public_less( self )
129 return self._less_than_comparable
130
132 self._less_than_comparable = value
133
134 less_than_comparable = property( _get_less_than_comparable, _set_less_than_comparable
135 , doc="indicates existence of public operator<. " \
136 +"Default value is calculated, based on information presented in the declarations tree" )
137
140
144
145 opaque = property( _get_opaque, _set_opaque
146 , doc="If True, Py++ will treat return types and arguments T* as opaque types." \
147 +"Thus it will be able to generate code, that uses " \
148 +" BOOST_PYTHON_OPAQUE_SPECIALIZED_TYPE_ID macro in a right places." )
149
150 @property
152 return self.alias + '_exposer'
153
154
155 -class class_declaration_t( class_common_details_t
156 , decl_wrapper.decl_wrapper_t
157 , declarations.class_declaration_t ):
158 - def __init__(self, *arguments, **keywords):
162
163 -class class_t( class_common_details_t
164 , scopedef_wrapper.scopedef_t
165 , declarations.class_t):
166
171
172 FAKE_CONSTRUCTOR_TYPES = ( declarations.member_function_t, declarations.free_function_t )
173 FAKE_CONSTRUCTOR_TYPE_NAMES = 'member and free functions'
174
175 - def __init__(self, *arguments, **keywords):
176 class_common_details_t.__init__( self )
177 declarations.class_t.__init__(self, *arguments, **keywords )
178 scopedef_wrapper.scopedef_t.__init__( self )
179
180 self._redefine_operators = False
181 self._held_type = None
182 self._noncopyable = None
183 self._wrapper_alias = None
184 self._registration_code_head = []
185 self._registration_code_tail = []
186 self._declaration_code = []
187 self._wrapper_code = []
188 self._destructor_code = []
189 self._exception_translation_code = None
190 self._properties = []
191 self._redefined_funcs = None
192 self._require_self_reference = False
193 self._exposed_class_type = self.EXPOSED_CLASS_TYPE.DECLARED
194 self._expose_this = None
195 self._expose_sizeof = None
196 self._fake_constructors = []
197 self._no_init = None
198
199 @property
201 """list of fake constructors"""
202 return self._fake_constructors
203
205 """f - reference to a calldef_t object or list of them
206
207 boost::python::make_constructor allows to register a C++ function, as a
208 class constructor.
209 """
210 if isinstance( f, declarations.calldef_t ):
211 self._fake_constructors.add( f )
212 else:
213 self._fake_constructors.extend( f )
214
216 return self._redefine_operators
218 self._redefine_operators = new_value
219 redefine_operators = property( _get_redefine_operators, _set_redefine_operators
220 , doc="tells Py++ to redefine operators from base class in this class, False by default")
221
223 return self._exposed_class_type
227 exposed_class_type = property( _get_exposed_class_type, _set_exposed_class_type
228 , doc="set this value to CLASS_TYPE.WRAPPER, if you need to transfer ownership of" \
229 "polymorphic class" )
230
232 return self._held_type
235 held_type = property( _get_held_type, _set_held_type
236 , doc="string, this property tells Py++ what HeldType this class has" \
237 +"Default value is calculated, based on information presented in exposed declarations" )
238
245 noncopyable = property( _get_noncopyable, _set_noncopyable
246 , doc="True if the class is noncopyable, False otherwies" \
247 +"Default value is calculated, based on information presented in the declarations tree" )
248
250 if None is self._wrapper_alias:
251 self._wrapper_alias = self._generate_valid_name(self.partial_name) + "_wrapper"
252 return self._wrapper_alias
254 self._wrapper_alias = walias
255 wrapper_alias = property( _get_wrapper_alias, _set_wrapper_alias
256 , doc="class-wrapper name")
257
258 @property
260 """
261 List of strings, that contains valid C++ code, that will be added to
262 the class declaration section
263 """
264 return self._declaration_code
265
266 @property
268 """
269 List of strings, that contains valid C++ code, that will be added to
270 the head of the class registration section
271 """
272 return self._registration_code_head
273
274 @property
276 """
277 List of strings, that contains valid C++ code, that will be added to
278 the tail of the class registration section
279 """
280 return self._registration_code_tail
281
282 @property
289
290 @property
292 """
293 List of strings, that contains valid C++ code, that will be added to
294 the class wrapper.
295 """
296 return self._wrapper_code
297
299 c = self.find_trivial_constructor()
300 if c:
301 return c.body
302 else:
303 return ''
305 c = self.find_trivial_constructor()
306 if c:
307 c.body = body
308 null_constructor_body = property( _get_null_constructor_body, _set_null_constructor_body
309 , doc="null constructor code, that will be added as is to the null constructor of class-wrapper" )
310
312 c = self.find_copy_constructor()
313 if c:
314 return c.body
315 else:
316 return ''
317
319 c = self.find_copy_constructor()
320 if c:
321 c.body = body
322 copy_constructor_body = property( _get_copy_constructor_body, _set_copy_constructor_body
323 , doc="copy constructor code, that will be added as is to the copy constructor of class-wrapper")
324
325 @property
327 """list of code to be added to wrapper destructor"""
328 return self._destructor_code
329
331 """adds code to the class-wrapper destructor"""
332 self._destructor_code.append( code )
333
334 @property
336 """exception argument name for translate exception function
337
338 If you don't understand what this argument is, please take a look on
339 Boost.Python documentation: http://www.boost.org/libs/python/doc/v2/exception_translator.html
340 """
341 return 'exc'
342
344 return self._exception_translation_code
346 self._exception_translation_code = code
347 exception_translation_code = property( _get_exception_translation_code, _set_exception_translation_code
348 , doc="C++ exception to Python exception translation code" \
349 +"\nExample: PyErr_SetString(PyExc_RuntimeError, exc.what()); " \
350 +"\nPy++ will generate the rest of the code." \
351 +"\nPay attention: the exception variable name is exc." )
352
354 """registers exception translation to string
355
356 @param python_exception_type: Python exception type, for example PyExc_RuntimeError
357 @type python_exception_type: str
358
359 @param to_string: C++ expression that extracts information from exception.
360 The type of expression should be char*.
361 @type to_string: str
362 """
363
364
365
366
367 code = "PyErr_SetString( %(exception_type)s, %(to_string)s ); " \
368 % { 'exception_type' : python_exception_type, 'to_string' : to_string }
369 self.exception_translation_code = code
370
374
388
389
390 add_code = add_registration_code
391
395
396 - def set_constructors_body( self, body ):
397 """Sets the body for all constructors"""
398 constrs = self.constructors(allow_empty=True, recursive=False)
399 if constrs:
400 constrs.body = body
401 self.null_constructor_body = body
402 self.copy_constructor_body = body
403
421
450
451
452 members = filter( is_exportable, members )
453 sorted_members = members
454 if sort:
455 sorted_members = sort( members )
456 return sorted_members
457
458 @property
460 """list of properties"""
461 return self._properties
462
464 """adds new property to the class
465
466 @param name: name of the property
467 @type name: str
468
469 @param fget: reference to the class member function
470 @param fset: reference to the class member function, could be None
471 @param doc: documentation string
472 """
473 self._properties.append( properties.property_t( name, fget, fset, doc ) )
474
475 - def add_properties( self, recognizer=None, exclude_accessors=False ):
478
482
484 """returns list of member functions that should be defined in class wrapper
485
486 It comes useful in 3 tier hierarchy:
487 struct base{
488 virtual void do_nothing() = 0;
489 };
490
491 struct derived{
492 virtual void do_something() = 0;
493 };
494
495 struct concrete{
496 virtual void do_nothing(){}
497 virtual void do_something(){}
498 };
499
500 derived_wrapper should define do_nothing function, otherwise the generated
501 code will not compile
502 """
503
504 if isinstance( self._redefined_funcs, list ):
505 return self._redefined_funcs
506
507 all_included = declarations.custom_matcher_t( lambda decl: decl.ignore == False and decl.exportable )
508 all_protected = declarations.access_type_matcher_t( 'protected' ) & all_included
509 all_pure_virtual = declarations.virtuality_type_matcher_t( VIRTUALITY_TYPES.PURE_VIRTUAL )
510 all_virtual = declarations.virtuality_type_matcher_t( VIRTUALITY_TYPES.VIRTUAL ) \
511 & ( declarations.access_type_matcher_t( 'public' ) \
512 | declarations.access_type_matcher_t( 'protected' ))
513 all_not_pure_virtual = ~all_pure_virtual
514
515 query = all_protected | all_pure_virtual
516 mf_query = query | all_virtual
517 relevant_opers = declarations.custom_matcher_t( lambda decl: decl.symbol in ('()', '[]') )
518 funcs = []
519 defined_funcs = []
520
521 for base in self.recursive_bases:
522 if base.access == ACCESS_TYPES.PRIVATE:
523 continue
524 base_cls = base.related_class
525
526 funcs.extend( base_cls.member_functions( mf_query, recursive=False, allow_empty=True ) )
527 funcs.extend( base_cls.member_operators( relevant_opers & query, recursive=False, allow_empty=True ) )
528
529 defined_funcs.extend( base_cls.member_functions( all_not_pure_virtual, recursive=False, allow_empty=True ) )
530 defined_funcs.extend( base_cls.member_operators( all_not_pure_virtual & relevant_opers, recursive=False, allow_empty=True ) )
531
532 not_reimplemented_funcs = set()
533 is_same_function = declarations.is_same_function
534 for f in funcs:
535 cls_fs = self.calldefs( name=f.name, recursive=False, allow_empty=True )
536 for cls_f in cls_fs:
537 if is_same_function( f, cls_f ):
538 break
539 else:
540
541 for f_impl in not_reimplemented_funcs:
542 if is_same_function( f, f_impl ):
543 if declarations.is_base_and_derived( f_impl.parent, f.parent ):
544
545 not_reimplemented_funcs.remove( f_impl )
546 not_reimplemented_funcs.add( f )
547 break
548 else:
549
550 if f.virtuality != VIRTUALITY_TYPES.PURE_VIRTUAL:
551 not_reimplemented_funcs.add( f )
552 else:
553 for f_defined in defined_funcs:
554 if is_same_function( f, f_defined ):
555 break
556 else:
557 not_reimplemented_funcs.add( f )
558 functions = filter( lambda f: ( False == f.ignore and True == f.exportable )
559 or all_pure_virtual( f )
560 , list( not_reimplemented_funcs ) )
561
562
563
564
565
566
567 def buggy_bpl_filter( f ):
568 if f.parent is self:
569 return False
570 if f.access_type != ACCESS_TYPES.PUBLIC:
571 return False
572 if f.virtuality != VIRTUALITY_TYPES.NOT_VIRTUAL:
573 return False
574
575 this_funs = self.decls( name=f.name
576 , decl_type=declarations.calldef_t
577 , recursive=False
578 , allow_empty=True )
579 for this_f in this_funs:
580 if is_same_function( this_f, f ):
581
582 return False
583 else:
584 return True
585
586 tmp = {}
587 for redefined_f in functions:
588
589 for rfo in redefined_f.overloads:
590 if id(rfo) in tmp:
591 continue
592 if buggy_bpl_filter( rfo ):
593 tmp[ id(rfo) ] = rfo
594 functions.extend( tmp.values() )
595
596 functions.sort( cmp=lambda f1, f2: cmp( ( f1.name, f1.location.as_tuple() )
597 , ( f2.name, f2.location.as_tuple() ) ) )
598
599 self._redefined_funcs = functions
600 return self._redefined_funcs
601
640
652
662
663 if self.member_operators( is_assign, allow_empty=True, recursive=False ):
664 return impl_details.GUESS_VALUES.ALWAYS_TRUE
665 return super(class_t, self).guess_always_expose_using_scope_value()
666
668 return self._require_self_reference
671 require_self_reference = property( _get_require_self_reference, _set_require_self_reference
672 , doc="boolean, if True the first argument to the constructor will be reference to self object" )
673
675 return self._expose_this
677 self._expose_this = new_value
678 expose_this = property( _get_expose_this, _set_expose_this
679 , doc="boolean, if True an object address( this pointer ) will be exposed to Python as integer.")
680
682 return self._expose_sizeof
684 self._expose_sizeof = new_value
685 expose_sizeof = property( _get_expose_sizeof, _set_expose_sizeof
686 , doc="boolean, if True the sizeof(obj) will be exposed to Python as integer.")
687
688 @property
690 """returns True, if during exposing this class, new scope will be created
691
692 For example, anonymous structs will be exposed in a parent scope.
693 """
694 if not self.name:
695 return False
696 elif self.class_type == declarations.CLASS_TYPES.UNION:
697 return False
698 else:
699 return True
700
731 self._no_init = value
732
733 no_init = property( _get_no_init, _set_no_init
734 , doc="If True, class will be registered with 'boost::python::no_init'" )
735