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 8import os 9from . import user_text 10from . import algorithm 11from . import decl_wrapper 12from pyplusplus import messages 13from pygccxml import declarations 14from pyplusplus import function_transformers as ft 15 16class calldef_t(decl_wrapper.decl_wrapper_t): 17 """base class, for code generator configuration, 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 :class:`decl_wrappers.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 54 def _get_create_with_signature(self): 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 84 def _get_use_default_arguments(self): 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 incomplete. 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 overridden""" 129 return self._non_overridable_reason 130 131 def mark_as_non_overridable( self, reason ): 132 """ 133 mark this function as final - user will not be able to override it from Python 134 135 Not all functions could be overridden from Python, for example virtual function 136 that returns non const reference to a member variable. `Py++` allows you to 137 mark these functions and provide and explanation to the user. 138 """ 139 self.overridable = False 140 self._non_overridable_reason = messages.W0000 % reason 141 142 @property 143 def transformations(self): 144 """return list of function transformations that should be applied on the function""" 145 if None is self._transformations: 146 #TODO: for trivial cases get_size( int&, int& ) `Py++` should guess 147 #function transformers 148 self._transformations = [] 149 return self._transformations 150 151 def add_transformation(self, *transformer_creators, **keywd): 152 """add new function transformation. 153 154 transformer_creators - list of transformer creators, which should be applied on the function 155 keywd - keyword arguments for :class:`function_transformers.function_transformation_t` class initialization 156 """ 157 self.transformations.append( ft.function_transformation_t( self, transformer_creators, **keywd ) ) 158 159 def _exportable_impl_derived( self ): 160 return '' 161 162 def _exportable_impl( self ): 163 if self.transformations: 164 #It is possible that the function asked for the user attention. 165 #The user paid attention and created a transformation. 166 #Py++ should be silent in this case. 167 return '' 168 if not self.parent.name: 169 return messages.W1057 % str( self ) 170 all_types = [ arg.decl_type for arg in self.arguments ] 171 all_types.append( self.return_type ) 172 for some_type in all_types: 173 if isinstance( some_type, declarations.ellipsis_t ): 174 return messages.W1053 % str( self ) 175 units = declarations.decompose_type( some_type ) 176 ptr2functions = [unit for unit in units if isinstance( unit, declarations.calldef_type_t )] 177 if ptr2functions: 178 return messages.W1004 179 #Function that take as agrument some instance of non public class 180 #will not be exported. Same to the return variable 181 if isinstance( units[-1], declarations.declarated_t ): 182 dtype = units[-1] 183 if isinstance( dtype.declaration.parent, declarations.class_t ): 184 if dtype.declaration not in dtype.declaration.parent.public_members: 185 return messages.W1005 186 no_ref = declarations.remove_reference( some_type ) 187 no_ptr = declarations.remove_pointer( no_ref ) 188 no_const = declarations.remove_const( no_ptr ) 189 if declarations.is_array( no_const ): 190 return messages.W1006 191 return self._exportable_impl_derived() 192 193 def _readme_impl( self ): 194 def is_double_ptr( type_ ): 195 #check for X** 196 if not declarations.is_pointer( type_ ): 197 return False 198 base = declarations.remove_pointer( type_ ) 199 return declarations.is_pointer( base ) 200 201 def suspicious_type( type_ ): 202 if not declarations.is_reference( type_ ): 203 return False 204 type_no_ref = declarations.remove_reference( type_ ) 205 return not declarations.is_const( type_no_ref ) \ 206 and ( declarations.is_fundamental( type_no_ref ) 207 or declarations.is_enum( type_no_ref ) ) 208 msgs = [] 209 #TODO: functions that takes as argument pointer to pointer to smth, could not be exported 210 #see http://www.boost.org/libs/python/doc/v2/faq.html#funcptr 211 212 if len( self.arguments ) > calldef_t.BOOST_PYTHON_MAX_ARITY: 213 msgs.append( messages.W1007 % ( calldef_t.BOOST_PYTHON_MAX_ARITY, len( self.arguments ) ) ) 214 215 if self.transformations: 216 #if user defined transformation, than I think it took care of the problems 217 ft = self.transformations[0] 218 if ft.alias == ft.unique_name: 219 msgs.append( messages.W1044 % ft.alias ) 220 return msgs 221 222 if suspicious_type( self.return_type ) and None is self.call_policies: 223 msgs.append( messages.W1008 ) 224 225 if ( declarations.is_pointer( self.return_type ) or is_double_ptr( self.return_type ) ) \ 226 and None is self.call_policies: 227 msgs.append( messages.W1050 % str(self.return_type) ) 228 229 for index, arg in enumerate( self.arguments ): 230 if suspicious_type( arg.decl_type ): 231 msgs.append( messages.W1009 % ( arg.name, index ) ) 232 if is_double_ptr( arg.decl_type ): 233 msgs.append( messages.W1051 % ( arg.name, index, str(arg.decl_type) ) ) 234 235 if False == self.overridable: 236 msgs.append( self._non_overridable_reason) 237 238 problematics = algorithm.registration_order.select_problematics( self ) 239 if problematics: 240 tmp = [] 241 for f in problematics: 242 tmp.append( os.linesep + '\t' + str(f) ) 243 msgs.append( messages.W1010 % os.linesep.join( tmp ) ) 244 return msgs 245 246class member_function_t( declarations.member_function_t, calldef_t ): 247 """defines a set of properties, that will instruct `Py++` how to expose the member function""" 248 def __init__(self, *arguments, **keywords): 249 declarations.member_function_t.__init__( self, *arguments, **keywords ) 250 calldef_t.__init__( self ) 251 self._use_overload_macro = False 252 self._override_precall_code = [] 253 self._overide_native_precall_code = [] 254 self._default_precall_code = [] 255 self._adaptor = None 256 257 def _get_adaptor(self): 258 return self._adaptor 259 def _set_adaptor(self, adaptor): 260 self._adaptor = adaptor 261 adaptor = property( _get_adaptor, _set_adaptor 262 , doc="string, if contains value `Py++` will generate code the following code: " \ 263 +".def(<name>, <adaptor>(<function reference>), <other args> ) " \ 264 +". The property is relevant for public, non virtual member functions." ) 265 266 267 def add_override_precall_code(self, code): 268 """add code, which should be executed, before overridden member function call""" 269 self._override_precall_code.append( code ) 270 271 @property 272 def override_precall_code(self): 273 """code, which should be executed, before overrided member function call""" 274 return self._override_precall_code 275 276 def add_override_native_precall_code(self, code): 277 """add code, which should be executed, before native member function call""" 278 self._overide_native_precall_code.append( code ) 279 280 @property 281 def override_native_precall_code(self): 282 """code, which should be executed, before overrided member function call""" 283 return self._overide_native_precall_code 284 285 def add_default_precall_code(self, code): 286 """add code, which should be executed, before this member function call""" 287 self._default_precall_code.append( code ) 288 289 @property 290 def default_precall_code(self): 291 """code, which should be executed, before this member function call""" 292 return self._default_precall_code 293 294 def get_use_overload_macro(self): 295 return self._use_overload_macro 296 def set_use_overload_macro(self, use_macro): 297 self._use_overload_macro = use_macro 298 use_overload_macro = property( get_use_overload_macro, set_use_overload_macro 299 , doc="boolean, if True, will use BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS macro to expose declarations" \ 300 +"Default value is False.") 301 302 def _exportable_impl_derived(self): 303 if self.access_type == declarations.ACCESS_TYPES.PRIVATE \ 304 and self.virtuality == declarations.VIRTUALITY_TYPES.NOT_VIRTUAL: 305 return messages.W1011 306 return '' 307 308 def _readme_impl( self ): 309 msgs = super( member_function_t, self )._readme_impl() 310 if self.does_throw == False \ 311 and self.virtuality != declarations.VIRTUALITY_TYPES.NOT_VIRTUAL: 312 msgs.append( messages.W1046 ) 313 return msgs 314 315class constructor_t( declarations.constructor_t, calldef_t ): 316 """defines a set of properties, that will instruct `Py++` how to expose the constructor""" 317 def __init__(self, *arguments, **keywords): 318 declarations.constructor_t.__init__( self, *arguments, **keywords ) 319 calldef_t.__init__( self ) 320 self._body = '' 321 self._allow_implicit_conversion = True 322 323 def _get_body(self): 324 return self._body 325 def _set_body(self, body): 326 self._body = body 327 body = property( _get_body, _set_body 328 , doc="string, class-wrapper constructor body" ) 329 330 def _exportable_impl_derived( self ): 331 if self.is_artificial: 332 return messages.W1012 333 if self.access_type == declarations.ACCESS_TYPES.PRIVATE: 334 return messages.W1013 335 return '' 336 337 def does_define_implicit_conversion( self ): 338 """ returns true if the constructor can take part in implicit conversions. 339 340 For more information see: 341 342 * http://boost.org/libs/python/doc/v2/implicit.html#implicitly_convertible-spec 343 * http://msdn2.microsoft.com/en-us/library/h1y7x448.aspx 344 * http://msdn.microsoft.com/en-us/library/s2ff0fz8%28VS.100%29.aspx 345 """ 346 if self.parent.is_abstract: #user is not able to create an instance of the class 347 return False 348 if declarations.is_copy_constructor(self): 349 return False 350 if not( len( self.arguments) and len( self.required_args ) < 2 ): 351 return False 352 if self.parent.find_out_member_access_type( self ) != declarations.ACCESS_TYPES.PUBLIC: 353 return False 354 return True 355 356 def _get_allow_implicit_conversion(self): 357 return self._allow_implicit_conversion and self.does_define_implicit_conversion() 358 def _set_allow_implicit_conversion(self, allow_implicit_conversion): 359 self._allow_implicit_conversion = allow_implicit_conversion 360 allow_implicit_conversion = property( _get_allow_implicit_conversion, _set_allow_implicit_conversion 361 , doc="boolean, indicates whether `Py++` should generate implicitly_convertible code or not" \ 362 "Default value is calculated from the constructor type." ) 363 364class destructor_t( declarations.destructor_t, calldef_t ): 365 """you may ignore this class for he time being. 366 367 In future it will contain "body" property, that will allow to insert user 368 code to class-wrapper destructor. 369 """ 370 #TODO: add body property 371 def __init__(self, *arguments, **keywords): 372 declarations.destructor_t.__init__( self, *arguments, **keywords ) 373 calldef_t.__init__( self ) 374 375class operators_helper: 376 """helps `Py++` to deal with C++ operators""" 377 inplace = [ '+=', '-=', '*=', '/=', '%=', '>>=', '<<=', '&=', '^=', '|=' ] 378 comparison = [ '==', '!=', '<', '>', '<=', '>=' ] 379 non_member = [ '+', '-', '*', '/', '%', '&', '^', '|', ] 380 unary = [ '!', '~', '+', '-' ] 381 382 all = inplace + comparison + non_member + unary 383 384 @staticmethod 385 def is_supported( oper ): 386 """returns True if Boost.Python support the operator""" 387 if oper.symbol == '*' and len( oper.arguments ) == 0: 388 #dereference does not make sense 389 return False 390 if oper.symbol != '<<': 391 return oper.symbol in operators_helper.all 392 393 args_len = len( oper.arguments ) 394 if isinstance( oper, declarations.member_operator_t ):# and args_len != 1: 395 return False #Boost.Python does not support member operator<< :-( 396 if isinstance( oper, declarations.free_operator_t ) and args_len != 2: 397 return False 398 if not declarations.is_same( oper.return_type, oper.arguments[0].decl_type ): 399 return False 400 type_ = oper.return_type 401 if not declarations.is_reference( type_ ): 402 return False 403 type_ = declarations.remove_reference( type_ ) 404 if declarations.is_const( type_ ): 405 return False 406 if args_len == 2: 407 #second argument should has "T const &" type, otherwise the code will not compile 408 tmp = oper.arguments[1].decl_type 409 if not declarations.is_reference( tmp ): 410 return False 411 tmp = declarations.remove_reference( tmp ) 412 if not declarations.is_const( tmp ): 413 return False 414 return declarations.is_std_ostream( type_ ) or declarations.is_std_wostream( type_ ) 415 416 @staticmethod 417 def exportable( oper ): 418 """returns True if Boost.Python or `Py++` know how to export the operator""" 419 if isinstance( oper, declarations.member_operator_t ) and oper.symbol in ( '()', '[]', '=' ): 420 return '' 421 if not operators_helper.is_supported( oper ): 422 return messages.W1014 % oper.name 423 if isinstance( oper, declarations.free_operator_t ): 424 #`Py++` should find out whether the relevant class is exposed to Python 425 #and if not, than this operator should not be exposed too 426 included = [declaration for declaration in oper.class_types if declaration.ignore == False] 427 if not included: 428 return messages.W1052 % str(oper) 429 return '' 430 431 @staticmethod 432 def target_class( oper ): 433 """this functions returns reference to class/class declaration 434 in scope of which, the operator should be exposed.""" 435 if isinstance( oper.parent, declarations.class_t ): 436 return oper.parent 437 #now we deal with free operators 438 def find_class( type_ ): 439 type_ = declarations.remove_reference( type_ ) 440 if declarations.is_class( type_ ): 441 return declarations.class_traits.get_declaration( type_ ) 442 elif declarations.is_class_declaration( type_ ): 443 return declarations.class_declaration_traits.get_declaration( type_ ) 444 else: 445 return None 446 447 arg_1_class = find_class( oper.arguments[0].decl_type ) 448 arg_2_class = None 449 if 2 == len( oper.arguments ): 450 arg_2_class = find_class( oper.arguments[1].decl_type ) 451 452 if arg_1_class: 453 if declarations.is_std_ostream( arg_1_class ) or declarations.is_std_wostream( arg_1_class ): 454 #in most cases users doesn't expose std::ostream class 455 return arg_2_class 456 else: 457 return arg_1_class 458 else: 459 return arg_2_class 460 461 462class member_operator_t( declarations.member_operator_t, calldef_t ): 463 """defines a set of properties, that will instruct `Py++` how to expose the member operator""" 464 def __init__(self, *arguments, **keywords): 465 declarations.member_operator_t.__init__( self, *arguments, **keywords ) 466 calldef_t.__init__( self ) 467 self._override_precall_code = [] 468 self._default_precall_code = [] 469 self._overide_native_precall_code = [] 470 471 def add_override_precall_code(self, code): 472 self._override_precall_code.append( code ) 473 474 @property 475 def override_precall_code(self): 476 return self._override_precall_code 477 478 def add_default_precall_code(self, code): 479 self._default_precall_code.append( code ) 480 481 @property 482 def default_precall_code(self): 483 return self._default_precall_code 484 485 def add_override_native_precall_code(self, code): 486 """add code, which should be executed, before native member function call""" 487 self._overide_native_precall_code.append( code ) 488 489 @property 490 def override_native_precall_code(self): 491 """code, which should be executed, before overrided member function call""" 492 return self._overide_native_precall_code 493 494 495 def _get_alias( self): 496 alias = super( member_operator_t, self )._get_alias() 497 if alias == self.name: 498 if self.symbol == '()': 499 alias = '__call__' 500 elif self.symbol == '[]': 501 alias = '__getitem__' 502 elif self.symbol == '=': 503 alias = 'assign' 504 else: 505 pass 506 return alias 507 alias = property( _get_alias, decl_wrapper.decl_wrapper_t._set_alias 508 , doc="Gives right alias for operator()( __call__ ) and operator[]( __getitem__ )" ) 509 510 def _exportable_impl_derived( self ): 511 if self.access_type == declarations.ACCESS_TYPES.PRIVATE \ 512 and self.virtuality == declarations.VIRTUALITY_TYPES.NOT_VIRTUAL: 513 return messages.W1015 514 return operators_helper.exportable( self ) 515 516 @property 517 def target_class( self ): 518 return self.parent 519 520class casting_operator_t( declarations.casting_operator_t, calldef_t ): 521 """defines a set of properties, that will instruct `Py++` how to expose the casting operator""" 522 523 def prepare_special_cases(): 524 """ 525 Creates a map of special cases ( aliases ) for casting operator. 526 """ 527 special_cases = {} 528 const_t = declarations.const_t 529 pointer_t = declarations.pointer_t 530 for type_ in list(declarations.FUNDAMENTAL_TYPES.values()): 531 alias = None 532 if declarations.is_same( type_, declarations.bool_t() ): 533 alias = '__int__' 534 elif declarations.is_integral( type_ ): 535 if 'long' in type_.decl_string: 536 alias = '__long__' 537 else: 538 alias = '__int__' 539 elif declarations.is_floating_point( type_ ): 540 alias = '__float__' 541 else: 542 continue #void 543 if alias: 544 special_cases[ type_ ] = alias 545 special_cases[ const_t( type_ ) ] = alias 546 special_cases[ pointer_t( const_t( declarations.char_t() ) ) ] = '__str__' 547 std_string = '::std::basic_string<char,std::char_traits<char>,std::allocator<char> >' 548 std_wstring1 = '::std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> >' 549 std_wstring2 = '::std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >' 550 special_cases[ std_string ] = '__str__' 551 special_cases[ std_wstring1 ] = '__str__' 552 special_cases[ std_wstring2 ] = '__str__' 553 special_cases[ '::std::string' ] = '__str__' 554 special_cases[ '::std::wstring' ] = '__str__' 555 556 #TODO: add 557 # std::complex<SomeType> some type should be converted to double 558 return special_cases 559 560 SPECIAL_CASES = prepare_special_cases() 561 #casting_member_operator_t.prepare_special_cases() 562 563 def __init__(self, *arguments, **keywords): 564 declarations.casting_operator_t.__init__( self, *arguments, **keywords ) 565 calldef_t.__init__( self ) 566 567 def _get_alias( self): 568 if not self._alias or self.name == super( casting_operator_t, self )._get_alias(): 569 return_type = declarations.remove_alias( self.return_type ) 570 decl_string = return_type.decl_string 571 for type_, alias in list(self.SPECIAL_CASES.items()): 572 if isinstance( type_, declarations.type_t ): 573 if declarations.is_same( return_type, type_ ): 574 self._alias = alias 575 break 576 else: 577 if decl_string == type_: 578 self._alias = alias 579 break 580 else: 581 self._alias = 'as_' + self._generate_valid_name(self.return_type.decl_string) 582 return self._alias 583 alias = property( _get_alias, decl_wrapper.decl_wrapper_t._set_alias 584 , doc="Gives right alias for casting operators: __int__, __long__, __str__." \ 585 +"If there is no built-in type, creates as_xxx alias" ) 586 587 def _exportable_impl_derived( self ): 588 if not declarations.is_fundamental( self.return_type ) and not self.has_const: 589 return messages.W1016 590 if self.access_type != declarations.ACCESS_TYPES.PUBLIC: 591 return messages.W1017 592 return '' 593 594 595class free_function_t( declarations.free_function_t, calldef_t ): 596 """defines a set of properties, that will instruct `Py++` how to expose the free function""" 597 def __init__(self, *arguments, **keywords): 598 declarations.free_function_t.__init__( self, *arguments, **keywords ) 599 calldef_t.__init__( self ) 600 self._use_overload_macro = False 601 self._declaration_code = [] 602 self._adaptor = None 603 604 def _get_adaptor(self): 605 return self._adaptor 606 def _set_adaptor(self, adaptor): 607 self._adaptor = adaptor 608 adaptor = property( _get_adaptor, _set_adaptor 609 , doc="string, if contains value `Py++` will generate code the following code: " \ 610 +"def(<name>, <adaptor>(<function reference>), <other args> ) " ) 611 612 def add_declaration_code( self, code ): 613 """adds the code to the declaration section""" 614 self.declaration_code.append( user_text.user_text_t( code ) ) 615 616 @property 617 def declaration_code( self ): 618 """ 619 List of strings, that contains valid C++ code, that will be added to 620 the same file in which the registration code for the function will be 621 generated 622 """ 623 return self._declaration_code 624 625 def get_use_overload_macro(self): 626 return self._use_overload_macro 627 def set_use_overload_macro(self, use_macro): 628 self._use_overload_macro = use_macro 629 use_overload_macro = property( get_use_overload_macro, set_use_overload_macro 630 , doc="boolean, if True, will use BOOST_PYTHON_FUNCTION_OVERLOADS macro to expose declarations" \ 631 +"Default value is False.") 632 633 634class free_operator_t( declarations.free_operator_t, calldef_t ): 635 """defines a set of properties, that will instruct `Py++` how to expose the free operator""" 636 def __init__(self, *arguments, **keywords): 637 declarations.free_operator_t.__init__( self, *arguments, **keywords ) 638 calldef_t.__init__( self ) 639 self._target_class = None 640 641 def _exportable_impl_derived( self ): 642 return operators_helper.exportable( self ) 643 644 def get_target_class( self ): 645 if self._target_class is None: 646 self._target_class = operators_helper.target_class( self ) 647 return self._target_class 648 649 def set_target_class( self, class_ ): 650 self._target_class = class_ 651 652 _target_class_doc_ = "reference to class_t or class_declaration_t object." \ 653 + " There are use cases, where `Py++` doesn't guess right, in what scope" \ 654 + " free operator should be registered( exposed ). If this is your use case " \ 655 + " than setting the class will allow you to quickly fix the situation. " 656 657 target_class = property( get_target_class, set_target_class, doc=_target_class_doc_ ) 658