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