pyplusplus.decl_wrappers.calldef_wrapper

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
     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 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):
    277          if self.access_type == declarations.ACCESS_TYPES.PRIVATE \
    278             and self.virtuality == declarations.VIRTUALITY_TYPES.NOT_VIRTUAL:
    279              return messages.W1011
    280          return ''
    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
    311 -    def does_define_implicit_conversion( self ):
    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
    330 -    def _get_allow_implicit_conversion(self):
    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 ):
    444          if self.access_type == declarations.ACCESS_TYPES.PRIVATE \
    445             and self.virtuality == declarations.VIRTUALITY_TYPES.NOT_VIRTUAL:
    446              return messages.W1015
    447          return operators_helper.exportable( 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
    453 -    def prepare_special_cases():
    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