Package pyplusplus :: Package decl_wrappers :: Module calldef_wrapper

Source Code for Module pyplusplus.decl_wrappers.calldef_wrapper

  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   
  6  """contains classes that allow to configure code generation for free\\member functions, operators and etc.""" 
  7   
  8  import os 
  9  import user_text 
 10  import algorithm 
 11  import decl_wrapper 
 12  from pyplusplus import messages 
 13  from pygccxml import declarations 
 14  from pyplusplus import function_transformers as ft 
15 16 -class calldef_t(decl_wrapper.decl_wrapper_t):
17 """base class, for code generator configration, for function declaration classes.""" 18 19 BOOST_PYTHON_MAX_ARITY = 10 20 """Boost.Python configuration macro value. 21 22 A function has more than BOOST_PYTHON_MAX_ARITY arguments, will not compile. 23 You should adjust BOOST_PYTHON_MAX_ARITY macro. 24 For more information see: http://mail.python.org/pipermail/c++-sig/2002-June/001554.html 25 """ 26
27 - def __init__(self, *arguments, **keywords):
28 decl_wrapper.decl_wrapper_t.__init__( self, *arguments, **keywords ) 29 30 self._call_policies = None 31 self._use_keywords = True 32 self._use_default_arguments = True 33 self._create_with_signature = None 34 self._overridable = None 35 self._non_overridable_reason = None 36 self._transformations = None
37
38 - def get_call_policies(self):
39 return self._call_policies
40 - def set_call_policies(self, call_policies):
41 self._call_policies = call_policies
42 call_policies = property( get_call_policies, set_call_policies 43 , doc="reference to L{call policies<call_policy_t>} class." \ 44 +"Default value is calculated at runtime, based on return value.") 45
46 - def _get_use_keywords(self):
47 return self._use_keywords and bool( self.arguments )
48 - def _set_use_keywords(self, use_keywords):
49 self._use_keywords = use_keywords
50 use_keywords = property( _get_use_keywords, _set_use_keywords 51 , doc="boolean, if True, allows to call function from Python using keyword arguments." \ 52 +"Default value is True.") 53
55 if None is self._create_with_signature: 56 self._create_with_signature = bool( self.overloads ) 57 58 if not self._create_with_signature and declarations.templates.is_instantiation( self.name ): 59 self._create_with_signature = True 60 61 if not self._create_with_signature and isinstance( self.parent, declarations.class_t ): 62 for hi in self.parent.recursive_bases: 63 if hi.access_type == 'private': 64 continue 65 funcs = hi.related_class.calldefs( self.name, recursive=False, allow_empty=True ) 66 for f in funcs: 67 if f.argument_types != self.argument_types: 68 self._create_with_signature = True 69 break 70 if self._create_with_signature: 71 break 72 if not self._create_with_signature: 73 self._create_with_signature \ 74 = bool( self.parent.calldefs( self.name, recursive=False, allow_empty=True ) ) 75 return self._create_with_signature
76
77 - def _set_create_with_signature(self, create_with_signature):
78 self._create_with_signature = create_with_signature
79 create_with_signature = property( _get_create_with_signature, _set_create_with_signature 80 , doc="boolean, if True Py++ will generate next code: def( ..., function type( function ref )"\ 81 +"Thus, the generated code is safe, when a user creates function overloading." \ 82 +"Default value is computed, based on information from the declarations tree" ) 83
85 return self._use_default_arguments
86 - def _set_use_default_arguments(self, use_default_arguments):
87 self._use_default_arguments = use_default_arguments
88 use_default_arguments = property( _get_use_default_arguments, _set_use_default_arguments 89 , doc="boolean, if True Py++ will generate code that will set default arguments" \ 90 +"Default value is True.") 91
92 - def has_wrapper( self ):
93 """returns True, if function - wrapper is needed 94 95 The functionality by this function is uncomplete. So please don't 96 use it in your code. 97 """ 98 if not isinstance( self, declarations.member_calldef_t ): 99 return False 100 elif self.virtuality == declarations.VIRTUALITY_TYPES.PURE_VIRTUAL: 101 return True 102 elif self.access_type == declarations.ACCESS_TYPES.PROTECTED: 103 return True 104 else: 105 return False
106
107 - def get_overridable( self ):
108 """Check if the method can be overridden.""" 109 if None is self._overridable: 110 if isinstance( self, declarations.member_calldef_t ) \ 111 and self.virtuality != declarations.VIRTUALITY_TYPES.NOT_VIRTUAL \ 112 and declarations.is_reference( self.return_type ): 113 self._overridable = False 114 self._non_overridable_reason = messages.W1049 115 else: 116 self._overridable = True 117 self._non_overridable_reason = "" 118 return self._overridable
119
120 - def set_overridable( self, overridable ):
121 self._overridable = overridable
122 123 overridable = property( get_overridable, set_overridable 124 , doc = get_overridable.__doc__ ) 125 126 @property
127 - def non_overridable_reason( self ):
128 """returns the reason the function could not be overriden""" 129 return self._non_overridable_reason
130
131 - def mark_as_non_overridable( self, reason ):
132 """mark this function as non-overridable 133 134 Not all fucntions could be overrided from Python, for example virtual function 135 that returns non const reference to a member variable. Py++ allows you to 136 mark these functions and provide and explanation to the user. 137 """ 138 self.overridable = False 139 self._non_overridable_reason = messages.W0000 % reason
140 141 @property
142 - def transformations(self):
143 """return list of function transformations that should be applied on the function""" 144 if None is self._transformations: 145 #TODO: for trivial cases get_size( int&, int& ) Py++ should guess 146 #function transformers 147 self._transformations = [] 148 return self._transformations
149
150 - def add_transformation(self, *transformer_creators, **keywd):
151 """add new function transformation. 152 153 transformer_creators - list of transformer creators, which should be applied on the function 154 keywd - keyword arguments for L{function_transformation_t} class initialization 155 """ 156 self.transformations.append( ft.function_transformation_t( self, transformer_creators, **keywd ) )
157
158 - def _exportable_impl_derived( self ):
159 return ''
160
161 - def _exportable_impl( self ):
162 if not self.parent.name: 163 return messages.W1057 % str( self ) 164 all_types = [ arg.type for arg in self.arguments ] 165 all_types.append( self.return_type ) 166 for some_type in all_types: 167 if isinstance( some_type, declarations.ellipsis_t ): 168 return messages.W1053 % str( self ) 169 units = declarations.decompose_type( some_type ) 170 ptr2functions = filter( lambda unit: isinstance( unit, declarations.calldef_type_t ) 171 , units ) 172 if ptr2functions: 173 return messages.W1004 174 #Function that take as agrument some instance of non public class 175 #will not be exported. Same to the return variable 176 if isinstance( units[-1], declarations.declarated_t ): 177 dtype = units[-1] 178 if isinstance( dtype.declaration.parent, declarations.class_t ): 179 if dtype.declaration not in dtype.declaration.parent.public_members: 180 return messages.W1005 181 no_ref = declarations.remove_reference( some_type ) 182 no_ptr = declarations.remove_pointer( no_ref ) 183 no_const = declarations.remove_const( no_ptr ) 184 if declarations.is_array( no_const ): 185 return messages.W1006 186 return self._exportable_impl_derived()
187
188 - def _readme_impl( self ):
189 def is_double_ptr( type_ ): 190 #check for X** 191 if not declarations.is_pointer( type_ ): 192 return False 193 base = declarations.remove_pointer( type_ ) 194 return declarations.is_pointer( base )
195 196 def suspicious_type( type_ ): 197 if not declarations.is_reference( type_ ): 198 return False 199 type_no_ref = declarations.remove_reference( type_ ) 200 return not declarations.is_const( type_no_ref ) \ 201 and ( declarations.is_fundamental( type_no_ref ) 202 or declarations.is_enum( type_no_ref ) )
203 msgs = [] 204 #TODO: functions that takes as argument pointer to pointer to smth, could not be exported 205 #see http://www.boost.org/libs/python/doc/v2/faq.html#funcptr 206 207 if len( self.arguments ) > calldef_t.BOOST_PYTHON_MAX_ARITY: 208 msgs.append( messages.W1007 % ( calldef_t.BOOST_PYTHON_MAX_ARITY, len( self.arguments ) ) ) 209 210 if self.transformations: 211 #if user defined transformation, than I think it took care of the problems 212 ft = self.transformations[0] 213 if ft.alias == ft.unique_name: 214 msgs.append( messages.W1044 % ft.alias ) 215 return msgs 216 217 if suspicious_type( self.return_type ) and None is self.call_policies: 218 msgs.append( messages.W1008 ) 219 220 if ( declarations.is_pointer( self.return_type ) or is_double_ptr( self.return_type ) ) \ 221 and None is self.call_policies: 222 msgs.append( messages.W1050 % str(self.return_type) ) 223 224 for index, arg in enumerate( self.arguments ): 225 if suspicious_type( arg.type ): 226 msgs.append( messages.W1009 % ( arg.name, index ) ) 227 if is_double_ptr( arg.type ): 228 msgs.append( messages.W1051 % ( arg.name, index, str(arg.type) ) ) 229 230 if False == self.overridable: 231 msgs.append( self._non_overridable_reason) 232 233 problematics = algorithm.registration_order.select_problematics( self ) 234 if problematics: 235 tmp = [] 236 for f in problematics: 237 tmp.append( os.linesep + '\t' + str(f) ) 238 msgs.append( messages.W1010 % os.linesep.join( tmp ) ) 239 return msgs 240
241 -class member_function_t( declarations.member_function_t, calldef_t ):
242 """defines a set of properties, that will instruct Py++ how to expose the member function"""
243 - def __init__(self, *arguments, **keywords):
244 declarations.member_function_t.__init__( self, *arguments, **keywords ) 245 calldef_t.__init__( self ) 246 self._use_overload_macro = False 247 self._override_precall_code = [] 248 self._default_precall_code = []
249
250 - def add_override_precall_code(self, code):
251 """add code, which should be executed, before overrided member function call""" 252 self._override_precall_code.append( code )
253 254 @property
255 - def override_precall_code(self):
256 """code, which should be executed, before overrided member function call""" 257 return self._override_precall_code
258
259 - def add_default_precall_code(self, code):
260 """add code, which should be executed, before this member function call""" 261 self._default_precall_code.append( code )
262 263 @property
264 - def default_precall_code(self):
265 """code, which should be executed, before this member function call""" 266 return self._default_precall_code
267
268 - def get_use_overload_macro(self):
269 return self._use_overload_macro
270 - def set_use_overload_macro(self, use_macro):
271 self._use_overload_macro = use_macro
272 use_overload_macro = property( get_use_overload_macro, set_use_overload_macro 273 , doc="boolean, if True, will use BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS macro to expose declarations" \ 274 +"Default value is False.") 275
276 - def _exportable_impl_derived(self):
281
282 - def _readme_impl( self ):
283 msgs = super( member_function_t, self )._readme_impl() 284 if self.does_throw == False \ 285 and self.virtuality != declarations.VIRTUALITY_TYPES.NOT_VIRTUAL: 286 msgs.append( messages.W1046 ) 287 return msgs
288
289 -class constructor_t( declarations.constructor_t, calldef_t ):
290 """defines a set of properties, that will instruct Py++ how to expose the constructor"""
291 - def __init__(self, *arguments, **keywords):
292 declarations.constructor_t.__init__( self, *arguments, **keywords ) 293 calldef_t.__init__( self ) 294 self._body = '' 295 self._allow_implicit_conversion = True
296
297 - def _get_body(self):
298 return self._body
299 - def _set_body(self, body):
300 self._body = body
301 body = property( _get_body, _set_body 302 , doc="string, class-wrapper constructor body" ) 303
304 - def _exportable_impl_derived( self ):
305 if self.is_artificial: 306 return messages.W1012 307 if self.access_type == declarations.ACCESS_TYPES.PRIVATE: 308 return messages.W1013 309 return ''
310
312 """ returns true if the constructor can take part in implicit conversions. 313 314 For more information see: 315 316 * http://boost.org/libs/python/doc/v2/implicit.html#implicitly_convertible-spec 317 318 * http://msdn2.microsoft.com/en-us/library/h1y7x448.aspx 319 """ 320 if self.parent.is_abstract: #user is not able to create an instance of the class 321 return False 322 if self.is_copy_constructor: 323 return False 324 if 1 != len( self.arguments ): 325 return False 326 if self.parent.find_out_member_access_type( self ) != declarations.ACCESS_TYPES.PUBLIC: 327 return False 328 return True
329
331 return self._allow_implicit_conversion and self.does_define_implicit_conversion()
332 - def _set_allow_implicit_conversion(self, allow_implicit_conversion):
333 self._allow_implicit_conversion = allow_implicit_conversion
334 allow_implicit_conversion = property( _get_allow_implicit_conversion, _set_allow_implicit_conversion 335 , doc="boolean, indicates whether Py++ should generate implicitly_convertible code or not" \ 336 "Default value is calculated from the constructor type." )
337
338 -class destructor_t( declarations.destructor_t, calldef_t ):
339 """you may ignore this class for he time being. 340 341 In future it will contain "body" property, that will allow to insert user 342 code to class-wrapper destructor. 343 """ 344 #TODO: add body property
345 - def __init__(self, *arguments, **keywords):
346 declarations.destructor_t.__init__( self, *arguments, **keywords ) 347 calldef_t.__init__( self )
348
349 -class operators_helper:
350 """helps Py++ to deal with C++ operators""" 351 inplace = [ '+=', '-=', '*=', '/=', '%=', '>>=', '<<=', '&=', '^=', '|=' ] 352 comparison = [ '==', '!=', '<', '>', '<=', '>=' ] 353 non_member = [ '+', '-', '*', '/', '%', '&', '^', '|', ] 354 unary = [ '!', '~', '+', '-' ] 355 356 all = inplace + comparison + non_member + unary 357 358 @staticmethod
359 - def is_supported( oper ):
360 """returns True if Boost.Python support the operator""" 361 if oper.symbol == '*' and len( oper.arguments ) == 0: 362 #dereference does not make sense 363 return False 364 if oper.symbol != '<<': 365 return oper.symbol in operators_helper.all 366 367 args_len = len( oper.arguments ) 368 if isinstance( oper, declarations.member_operator_t ):# and args_len != 1: 369 return False #Boost.Python does not support member operator<< :-( 370 if isinstance( oper, declarations.free_operator_t ) and args_len != 2: 371 return False 372 if not declarations.is_same( oper.return_type, oper.arguments[0].type ): 373 return False 374 type_ = oper.return_type 375 if not declarations.is_reference( type_ ): 376 return False 377 type_ = declarations.remove_reference( type_ ) 378 if declarations.is_const( type_ ): 379 return False 380 if args_len == 2: 381 #second argument should has "T const &" type, otherwise the code will not compile 382 tmp = oper.arguments[1].type 383 if not declarations.is_reference( tmp ): 384 return False 385 tmp = declarations.remove_reference( tmp ) 386 if not declarations.is_const( tmp ): 387 return False 388 return declarations.is_std_ostream( type_ ) or declarations.is_std_wostream( type_ )
389 390 @staticmethod
391 - def exportable( oper ):
392 """returns True if Boost.Python or Py++ know how to export the operator""" 393 if isinstance( oper, declarations.member_operator_t ) and oper.symbol in ( '()', '[]', '=' ): 394 return '' 395 if not operators_helper.is_supported( oper ): 396 return messages.W1014 % oper.name 397 if isinstance( oper, declarations.free_operator_t ): 398 #Py++ should find out whether the relevant class is exposed to Python 399 #and if not, than this operator should not be exposed too 400 included = filter( lambda decl: decl.ignore == False, oper.class_types ) 401 if not included: 402 return messages.W1052 % str(oper) 403 404 return ''
405
406 -class member_operator_t( declarations.member_operator_t, calldef_t ):
407 """defines a set of properties, that will instruct Py++ how to expose the member operator"""
408 - def __init__(self, *arguments, **keywords):
409 declarations.member_operator_t.__init__( self, *arguments, **keywords ) 410 calldef_t.__init__( self ) 411 self._override_precall_code = [] 412 self._default_precall_code = []
413
414 - def add_override_precall_code(self, code):
415 self._override_precall_code.append( code )
416 417 @property
418 - def override_precall_code(self):
419 return self._override_precall_code
420
421 - def add_default_precall_code(self, code):
422 self._default_precall_code.append( code )
423 424 @property
425 - def default_precall_code(self):
426 return self._default_precall_code
427
428 - def _get_alias( self):
429 alias = super( member_operator_t, self )._get_alias() 430 if alias == self.name: 431 if self.symbol == '()': 432 alias = '__call__' 433 elif self.symbol == '[]': 434 alias = '__getitem__' 435 elif self.symbol == '=': 436 alias = 'assign' 437 else: 438 pass 439 return alias
440 alias = property( _get_alias, decl_wrapper.decl_wrapper_t._set_alias 441 , doc="Gives right alias for operator()( __call__ ) and operator[]( __getitem__ )" ) 442
443 - def _exportable_impl_derived( self ):
448
449 450 -class casting_operator_t( declarations.casting_operator_t, calldef_t ):
451 """defines a set of properties, that will instruct Py++ how to expose the casting operator""" 452
454 """ 455 Creates a map of special cases ( aliases ) for casting operator. 456 """ 457 special_cases = {} 458 const_t = declarations.const_t 459 pointer_t = declarations.pointer_t 460 for type_ in declarations.FUNDAMENTAL_TYPES.values(): 461 alias = None 462 if declarations.is_same( type_, declarations.bool_t() ): 463 alias = '__int__' 464 elif declarations.is_integral( type_ ): 465 if 'long' in type_.decl_string: 466 alias = '__long__' 467 else: 468 alias = '__int__' 469 elif declarations.is_floating_point( type_ ): 470 alias = '__float__' 471 else: 472 continue #void 473 if alias: 474 special_cases[ type_ ] = alias 475 special_cases[ const_t( type_ ) ] = alias 476 special_cases[ pointer_t( const_t( declarations.char_t() ) ) ] = '__str__' 477 std_string = '::std::basic_string<char,std::char_traits<char>,std::allocator<char> >' 478 std_wstring1 = '::std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> >' 479 std_wstring2 = '::std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >' 480 special_cases[ std_string ] = '__str__' 481 special_cases[ std_wstring1 ] = '__str__' 482 special_cases[ std_wstring2 ] = '__str__' 483 special_cases[ '::std::string' ] = '__str__' 484 special_cases[ '::std::wstring' ] = '__str__' 485 486 #TODO: add 487 # std::complex<SomeType> some type should be converted to double 488 return special_cases
489 490 SPECIAL_CASES = prepare_special_cases() 491 #casting_member_operator_t.prepare_special_cases() 492
493 - def __init__(self, *arguments, **keywords):
494 declarations.casting_operator_t.__init__( self, *arguments, **keywords ) 495 calldef_t.__init__( self )
496
497 - def _get_alias( self):
498 if not self._alias or self.name == super( casting_operator_t, self )._get_alias(): 499 return_type = declarations.remove_alias( self.return_type ) 500 decl_string = return_type.decl_string 501 for type_, alias in self.SPECIAL_CASES.items(): 502 if isinstance( type_, declarations.type_t ): 503 if declarations.is_same( return_type, type_ ): 504 self._alias = alias 505 break 506 else: 507 if decl_string == type_: 508 self._alias = alias 509 break 510 else: 511 self._alias = 'as_' + self._generate_valid_name(self.return_type.decl_string) 512 return self._alias
513 alias = property( _get_alias, decl_wrapper.decl_wrapper_t._set_alias 514 , doc="Gives right alias for casting operators: __int__, __long__, __str__." \ 515 +"If there is no built-in type, creates as_xxx alias" ) 516
517 - def _exportable_impl_derived( self ):
518 if not declarations.is_fundamental( self.return_type ) and not self.has_const: 519 return messages.W1016 520 if self.access_type != declarations.ACCESS_TYPES.PUBLIC: 521 return messages.W1017 522 return ''
523
524 525 -class free_function_t( declarations.free_function_t, calldef_t ):
526 """defines a set of properties, that will instruct Py++ how to expose the free function"""
527 - def __init__(self, *arguments, **keywords):
528 declarations.free_function_t.__init__( self, *arguments, **keywords ) 529 calldef_t.__init__( self ) 530 self._use_overload_macro = False 531 self._declaration_code = []
532
533 - def add_declaration_code( self, code ):
534 """adds the code to the declaration section""" 535 self.declaration_code.append( user_text.user_text_t( code ) )
536 537 @property
538 - def declaration_code( self ):
539 """ 540 List of strings, that contains valid C++ code, that will be added to 541 the same file in which the registration code for the function will be 542 generated 543 """ 544 return self._declaration_code
545
546 - def get_use_overload_macro(self):
547 return self._use_overload_macro
548 - def set_use_overload_macro(self, use_macro):
549 self._use_overload_macro = use_macro
550 use_overload_macro = property( get_use_overload_macro, set_use_overload_macro 551 , doc="boolean, if True, will use BOOST_PYTHON_FUNCTION_OVERLOADS macro to expose declarations" \ 552 +"Default value is False.")
553
554 555 -class free_operator_t( declarations.free_operator_t, calldef_t ):
556 """defines a set of properties, that will instruct Py++ how to expose the free operator"""
557 - def __init__(self, *arguments, **keywords):
558 declarations.free_operator_t.__init__( self, *arguments, **keywords ) 559 calldef_t.__init__( self )
560
561 - def _exportable_impl_derived( self ):
562 return operators_helper.exportable( self )
563