Package pyplusplus :: Package function_transformers :: Module transformers |

# Source Code for Module pyplusplus.function_transformers.transformers

      1  # Copyright 2006 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  # Initial author: Matthias Baas
      7
      8  """This module contains the standard argument policy objects.
      9
     10  The following policies are available:
     11
     12   - L{output_t}
     13   - L{input_t}
     14   - L{inout_t}
     15   - L{input_array_t}
     16   - L{output_array_t}
     17   - L{type_modifier_t}
     18  """
     19  import os
     20  import string
     21  import transformer
     22  import controllers
     23  from pygccxml import declarations
     24  from pyplusplus import code_repository
     25
     26  #TODO: pointers should be checked for NULL
     27
     28 -def is_ref_or_ptr( type_ ):
     29      return declarations.is_pointer( type_ ) or declarations.is_reference( type_ )
     30
     31 -def is_ptr_or_array( type_ ):
     32      return declarations.is_pointer( type_ ) or declarations.is_array( type_ )
     33
     34 -def remove_ref_or_ptr( type_ ):
     35      if declarations.is_pointer( type_ ):
     36          return declarations.remove_pointer( type_ )
     37      elif declarations.is_reference( type_ ):
     38          return declarations.remove_reference( type_ )
     39      else:
     40          raise TypeError( 'Type should be reference or pointer, got %s.' % type_ )
     41
     42
     43  # output_t
     44 -class output_t( transformer.transformer_t ):
     45      """Handles a single output variable.
     46
     47      The specified variable is removed from the argument list and is turned
     48      into a return value.
     49
     50      void getValue(int& v) -> v = getValue()
     51      """
     52
     53 -    def __init__(self, function, arg_ref):
     54          transformer.transformer_t.__init__( self, function )
     55          """Constructor.
     56
     57          The specified argument must be a reference or a pointer.
     58
     59          @param arg_ref: Index of the argument that is an output value (the first arg has index 1).
     60          @type arg_ref: int
     61          """
     62          self.arg = self.get_argument( arg_ref )
     63          self.arg_index = self.function.arguments.index( self.arg )
     64
     65          if not is_ref_or_ptr( self.arg.type ):
     66              raise ValueError( '%s\nin order to use "output" transformation, argument %s type must be a reference or a pointer (got %s).' ) \
     67                    % ( function, self.arg_ref.name, arg.type)
     68
     69 -    def __str__(self):
     70          return "output(%d)"%(self.arg.name)
     71
     72 -    def required_headers( self ):
     73          """Returns list of header files that transformer generated code depends on."""
     74          return [ code_repository.convenience.file_name ]
     75
     76 -    def __configure_sealed( self, controller ):
     77          #removing arg from the function wrapper definition
     78          controller.remove_wrapper_arg( self.arg.name )
     79          #declaring new variable, which will keep result
     80          var_name = controller.declare_variable( remove_ref_or_ptr( self.arg.type ), self.arg.name )
     81          #adding just declared variable to the original function call expression
     82          controller.modify_arg_expression( self.arg_index, var_name )
     83          #adding the variable to return variables list
     84          controller.return_variable( var_name )
     85
     86 -    def __configure_v_mem_fun_default( self, controller ):
     87          self.__configure_sealed( controller )
     88
     89 -    def __configure_v_mem_fun_override( self, controller ):
     90          controller.remove_py_arg( self.arg_index )
     91          tmpl = string.Template(
     92              '$name = boost::python::extract< $type >( pyplus_conv::get_out_argument( $py_result, "$name" ) );' )
     93          store_py_result_in_arg = tmpl.substitute( name=self.arg.name
     94                                                    , type=remove_ref_or_ptr( self.arg.type ).decl_string
     95                                                    , py_result=controller.py_result_variable.name )
     96          controller.add_py_post_call_code( store_py_result_in_arg )
     97
     98 -    def configure_mem_fun( self, controller ):
     99          self.__configure_sealed( controller )
    100
    101 -    def configure_free_fun(self, controller ):
    102          self.__configure_sealed( controller )
    103
    104 -    def configure_virtual_mem_fun( self, controller ):
    105          self.__configure_v_mem_fun_default( controller.default_controller )
    106          self.__configure_v_mem_fun_override( controller.override_controller )
    107
    108  # input_t
    109 -class type_modifier_t(transformer.transformer_t):
    110      """Change/modify type of the argument.
    111
    112      Right now compiler should be able to use implicit conversion
    113      """
    114
    115 -    def __init__(self, function, arg_ref, modifier):
    116          """Constructor.
    117
    118          modifier is callable, which take the type of the argument and should return
    119          new type
    120          """
    121          transformer.transformer_t.__init__( self, function )
    122          self.arg = self.get_argument( arg_ref )
    123          self.arg_index = self.function.arguments.index( self.arg )
    124          self.modifier = modifier
    125
    126 -    def __str__(self):
    127          return "type_modifier(%s)" % self.arg.name
    128
    129 -    def __configure_sealed( self, controller ):
    130          w_arg = controller.find_wrapper_arg( self.arg.name )
    131          w_arg.type = self.modifier( self.arg.type )
    132          if not declarations.is_convertible( w_arg.type, self.arg.type ):
    133              casting_code = 'reinterpret_cast< %s >( %s )' % ( self.arg.type, w_arg.name )
    134              controller.modify_arg_expression(self.arg_index, casting_code)
    135
    136 -    def __configure_v_mem_fun_default( self, controller ):
    137          self.__configure_sealed( controller )
    138
    139 -    def configure_mem_fun( self, controller ):
    140          self.__configure_sealed( controller )
    141
    142 -    def configure_free_fun(self, controller ):
    143          self.__configure_sealed( controller )
    144
    145 -    def configure_virtual_mem_fun( self, controller ):
    146          self.__configure_v_mem_fun_default( controller.default_controller )
    147
    148 -    def required_headers( self ):
    149          """Returns list of header files that transformer generated code depends on."""
    150          return []
    151
    152  # input_t
    153 -class input_t(type_modifier_t):
    154      """Handles a single input variable.
    155
    156      The reference on the specified variable is removed.
    157
    158      void setValue(int& v) -> setValue(v)
    159      """
    160
    161 -    def __init__(self, function, arg_ref):
    162          """Constructor.
    163
    164          The specified argument must be a reference or a pointer.
    165
    166          @param idx: Index of the argument that is an output value (the first arg has index 1).
    167          @type idx: int
    168          """
    169          type_modifier_t.__init__( self, function, arg_ref, remove_ref_or_ptr )
    170
    171          if not is_ref_or_ptr( self.arg.type ):
    172              raise ValueError( '%s\nin order to use "input" transformation, argument %s type must be a reference or a pointer (got %s).' ) \
    173                    % ( function, self.arg_ref.name, arg.type)
    174
    175 -    def __str__(self):
    176          return "input(%s)"%(self.arg.name)
    177
    178  # input_t
    179 -class from_address_t(type_modifier_t):
    180      """Handles a single input variable.
    181
    182      Replaces the actual argument type with some integral type, so you
    183      can use ctypes package.
    184
    185      void do_smth(int** image) -> do_smth(unsigned int addressof_image)
    186
    187      """
    188
    189 -    def __init__(self, function, arg_ref):
    190          """Constructor.
    191
    192          The specified argument must be a reference or a pointer.
    193
    194          @param idx: Index of the argument that is an output value (the first arg has index 1).
    195          @type idx: int
    196          """
    197          modifier = lambda type_: declarations.FUNDAMENTAL_TYPES[ 'unsigned int' ]
    198          type_modifier_t.__init__( self, function, arg_ref, modifier )
    199
    200          if not is_ptr_or_array( self.arg.type ):
    201              raise ValueError( '%s\nin order to use "from_address_t" transformation, argument %s type must be a pointer or a array (got %s).' ) \
    202                    % ( function, self.arg_ref.name, arg.type)
    203
    204 -    def __str__(self):
    205          return "from_address(%s)"%(self.arg.name)
    206
    207  # inout_t
    208 -class inout_t(transformer.transformer_t):
    209      """Handles a single input/output variable.
    210
    211      void foo(int& v) -> v = foo(v)
    212      """
    213
    214 -    def __init__(self, function, arg_ref):
    215          """Constructor.
    216
    217          The specified argument must be a reference or a pointer.
    218
    219          @param idx: Index of the argument that is an in/out value (the first arg has index 1).
    220          @type idx: int
    221          """
    222          transformer.transformer_t.__init__( self, function )
    223          self.arg = self.get_argument( arg_ref )
    224          self.arg_index = self.function.arguments.index( self.arg )
    225
    226          if not is_ref_or_ptr( self.arg.type ):
    227              raise ValueError( '%s\nin order to use "inout" transformation, argument %s type must be a reference or a pointer (got %s).' ) \
    228                    % ( function, self.arg_ref.name, arg.type)
    229
    230 -    def __str__(self):
    231          return "inout(%s)"%(self.arg.name)
    232
    233 -    def __configure_sealed(self, controller):
    234          w_arg = controller.find_wrapper_arg( self.arg.name )
    235          w_arg.type = remove_ref_or_ptr( self.arg.type )
    236          #adding the variable to return variables list
    237          controller.return_variable( w_arg.name )
    238
    239 -    def __configure_v_mem_fun_default( self, controller ):
    240          self.__configure_sealed( controller )
    241
    242 -    def __configure_v_mem_fun_override( self, controller ):
    243          tmpl = string.Template(
    244              '$name = boost::python::extract< $type >( pyplus_conv::get_out_argument( $py_result, "$name" ) );' )
    245          store_py_result_in_arg = tmpl.substitute( name=self.arg.name
    246                                                    , type=remove_ref_or_ptr( self.arg.type ).decl_string
    247                                                    , py_result=controller.py_result_variable.name )
    248          controller.add_py_post_call_code( store_py_result_in_arg )
    249
    250 -    def configure_mem_fun( self, controller ):
    251          self.__configure_sealed( controller )
    252
    253 -    def configure_free_fun(self, controller ):
    254          self.__configure_sealed( controller )
    255
    256 -    def configure_virtual_mem_fun( self, controller ):
    257          self.__configure_v_mem_fun_override( controller.override_controller )
    258          self.__configure_v_mem_fun_default( controller.default_controller )
    259
    260 -    def required_headers( self ):
    261          """Returns list of header files that transformer generated code depends on."""
    262          return [ code_repository.convenience.file_name ]
    263
    264
    265  _seq2arr = string.Template( os.linesep.join([
    266                'pyplus_conv::ensure_uniform_sequence< $type >( $pylist, $array_size );'
    267              , 'pyplus_conv::copy_sequence( $pylist, pyplus_conv::array_inserter( $native_array, $array_size ) );']))
    268
    269  _seq2vector = string.Template( os.linesep.join([
    270                   'pyplus_conv::ensure_uniform_sequence< $type >( $pylist );'
    271                 , 'pyplus_conv::copy_sequence( $pylist, std::back_inserter( $native_array), boost::type< $type >() );']))
    272
    273  _arr2seq = string.Template(
    274              'pyplus_conv::copy_container( $native_array, $native_array + $array_size, pyplus_conv::list_inserter( $pylist ) );' )
    275
    276 -class input_static_array_t(transformer.transformer_t):
    277      """Handles an input array with fixed size.
    278
    279      void setVec3(double* v) ->  setVec3(object v)
    280      # v must be a sequence of 3 floats
    281      """
    282
    283 -    def __init__(self, function, arg_ref, size):
    284          """Constructor.
    285
    286          @param size: The fixed size of the input array
    287          @type size: int
    288          """
    289          transformer.transformer_t.__init__( self, function )
    290
    291          self.arg = self.get_argument( arg_ref )
    292          self.arg_index = self.function.arguments.index( self.arg )
    293
    294          if not is_ptr_or_array( self.arg.type ):
    295              raise ValueError( '%s\nin order to use "input_array" transformation, argument %s type must be a array or a pointer (got %s).' ) \
    296                    % ( function, self.arg.name, self.arg.type)
    297
    298          self.array_size = size
    299          self.array_item_type = declarations.remove_const( declarations.array_item_type( self.arg.type ) )
    300
    301 -    def __str__(self):
    302          return "input_array(%s,%d)"%( self.arg.name, self.array_size)
    303
    304 -    def required_headers( self ):
    305          """Returns list of header files that transformer generated code depends on."""
    306          return [ code_repository.convenience.file_name ]
    307
    308 -    def __configure_sealed(self, controller):
    309          global _seq2arr
    310          w_arg = controller.find_wrapper_arg( self.arg.name )
    311          w_arg.type = declarations.dummy_type_t( "boost::python::object" )
    312
    313          # Declare a variable that will hold the C array...
    314          native_array = controller.declare_variable( self.array_item_type
    315                                                      , "native_" + self.arg.name
    316                                                      , '[%d]' % self.array_size )
    317
    318          copy_pylist2arr = _seq2arr.substitute( type=self.array_item_type
    319                                                  , pylist=w_arg.name
    320                                                  , array_size=self.array_size
    321                                                  , native_array=native_array )
    322
    323          controller.add_pre_call_code( copy_pylist2arr )
    324
    325          controller.modify_arg_expression( self.arg_index, native_array )
    326
    327 -    def __configure_v_mem_fun_default( self, controller ):
    328          self.__configure_sealed( controller )
    329
    330 -    def __configure_v_mem_fun_override( self, controller ):
    331          global _arr2seq
    332          pylist = controller.declare_py_variable( declarations.dummy_type_t( 'boost::python::list' )
    333                                                   , 'py_' + self.arg.name )
    334
    335          copy_arr2pylist = _arr2seq.substitute( native_array=self.arg.name
    336                                                  , array_size=self.array_size
    337                                                  , pylist=pylist )
    338
    339          controller.add_py_pre_call_code( copy_arr2pylist )
    340
    341 -    def configure_mem_fun( self, controller ):
    342          self.__configure_sealed( controller )
    343
    344 -    def configure_free_fun(self, controller ):
    345          self.__configure_sealed( controller )
    346
    347 -    def configure_virtual_mem_fun( self, controller ):
    348          self.__configure_v_mem_fun_override( controller.override_controller )
    349          self.__configure_v_mem_fun_default( controller.default_controller )
    350
    351
    352  # s - static
    353 -class output_static_array_t(transformer.transformer_t):
    354      """Handles an output array of a fixed size.
    355
    356      void getVec3(double* v) -> v = getVec3()
    357      # v will be a list with 3 floats
    358      """
    359
    360 -    def __init__(self, function, arg_ref, size):
    361          """Constructor.
    362
    363          @param idx: Index of the argument that is an output array (the first arg has index 1).
    364          @type idx: int
    365          @param size: The fixed size of the output array
    366          @type size: int
    367          """
    368          transformer.transformer_t.__init__( self, function )
    369          self.arg = self.get_argument( arg_ref )
    370          self.arg_index = self.function.arguments.index( self.arg )
    371
    372          if not is_ptr_or_array( self.arg.type ):
    373              raise ValueError( '%s\nin order to use "input_array" transformation, argument %s type must be a array or a pointer (got %s).' ) \
    374                    % ( function, self.arg.name, self.arg.type)
    375
    376          self.array_size = size
    377          self.array_item_type = declarations.array_item_type( self.arg.type )
    378
    379 -    def __str__(self):
    380          return "output_array(%s,%d)"%( self.arg.name, self.array_size)
    381
    382 -    def required_headers( self ):
    383          """Returns list of header files that transformer generated code depends on."""
    384          return [ code_repository.convenience.file_name ]
    385
    386 -    def __configure_sealed(self, controller):
    387          global _arr2seq
    388          #removing arg from the function wrapper definition
    389          controller.remove_wrapper_arg( self.arg.name )
    390
    391          # Declare a variable that will hold the C array...
    392          native_array = controller.declare_variable( self.array_item_type
    393                                                      , "native_" + self.arg.name
    394                                                      , '[%d]' % self.array_size )
    395
    396          #adding just declared variable to the original function call expression
    397          controller.modify_arg_expression( self.arg_index, native_array )
    398
    399          # Declare a Python list which will receive the output...
    400          pylist = controller.declare_variable( declarations.dummy_type_t( "boost::python::list" )
    401                                                , 'py_' + self.arg.name )
    402
    403          copy_arr2pylist = _arr2seq.substitute( native_array=native_array
    404                                                 , array_size=self.array_size
    405                                                 , pylist=pylist )
    406
    407          controller.add_post_call_code( copy_arr2pylist )
    408
    409          #adding the variable to return variables list
    410          controller.return_variable( pylist )
    411
    412 -    def __configure_v_mem_fun_default( self, controller ):
    413          self.__configure_sealed( controller )
    414
    415 -    def __configure_v_mem_fun_override( self, controller ):
    416          global _seq2arr
    417          seq = controller.declare_py_variable( declarations.dummy_type_t( 'boost::python::object' )
    418                                                , 'py_' + self.arg.name )
    419          controller.remove_py_arg( self.arg_index )
    420          tmpl = string.Template( '$seq = pyplus_conv::get_out_argument( $py_result, "$name" );' )
    421          get_ref_to_seq = tmpl.substitute( seq=seq
    422                                            , py_result=controller.py_result_variable.name
    423                                            , name=self.arg.name )
    424          controller.add_py_post_call_code( get_ref_to_seq )
    425
    426          copy_pylist2arr = _seq2arr.substitute( type=self.array_item_type
    427                                                 , pylist=seq
    428                                                 , array_size=self.array_size
    429                                                 , native_array=self.arg.name )
    430          controller.add_py_post_call_code( copy_pylist2arr )
    431
    432 -    def configure_mem_fun( self, controller ):
    433          self.__configure_sealed( controller )
    434
    435 -    def configure_free_fun(self, controller ):
    436          self.__configure_sealed( controller )
    437
    438 -    def configure_virtual_mem_fun( self, controller ):
    439          self.__configure_v_mem_fun_override( controller.override_controller )
    440          self.__configure_v_mem_fun_default( controller.default_controller )
    441
    442
    443 -class input_c_buffer_t(transformer.transformer_t):
    444      """Handles an input of C buffere:
    445
    446      void write( byte *buffer, int size ) -> void write( python sequence )
    447      """
    448
    449 -    def __init__(self, function, buffer_arg_ref, size_arg_ref):
    450          """Constructor.
    451
    452          @param buffer_arg_ref: "reference" to the buffer argument
    453          @param buffer_arg_ref: "reference" to argument, which holds buffer size
    454          """
    455          transformer.transformer_t.__init__( self, function )
    456
    457          self.buffer_arg = self.get_argument( buffer_arg_ref )
    458          self.buffer_arg_index = self.function.arguments.index( self.buffer_arg )
    459
    460          self.size_arg = self.get_argument( size_arg_ref )
    461          self.size_arg_index = self.function.arguments.index( self.size_arg )
    462
    463          if not is_ptr_or_array( self.buffer_arg.type ):
    464              raise ValueError( '%s\nin order to use "input_c_buffer" transformation, "buffer" argument %s type must be a array or a pointer (got %s).' ) \
    465                    % ( function, self.buffer_arg.name, self.buffer_arg.type)
    466
    467          if not declarations.is_integral( self.size_arg.type ):
    468              raise ValueError( '%s\nin order to use "input_c_buffer" transformation, "size" argument %s type must be an integral type (got %s).' ) \
    469                    % ( function, self.size_arg.name, self.size_arg.type)
    470
    471          self.buffer_item_type = declarations.remove_const( declarations.array_item_type( self.buffer_arg.type ) )
    472
    473 -    def __str__(self):
    474          return "input_c_buffer(buffer arg=%s, size arg=%s)" \
    475                 % ( self.buffer_arg.name, self.size_arg.name)
    476
    477 -    def required_headers( self ):
    478          """Returns list of header files that transformer generated code depends on."""
    479          return [ code_repository.convenience.file_name, '<vector>', '<iterator>' ]
    480
    481 -    def __configure_sealed(self, controller):
    482          global _seq2arr
    483          w_buffer_arg = controller.find_wrapper_arg( self.buffer_arg.name )
    484          w_buffer_arg.type = declarations.dummy_type_t( "boost::python::object" )
    485
    486          controller.remove_wrapper_arg( self.size_arg.name )
    487
    488          size_var = controller.declare_variable(
    489                            declarations.remove_const( self.size_arg.type )
    490                          , self.size_arg.name
    491                          , ' = boost::python::len(%s)' % w_buffer_arg.name )
    492
    493          # Declare a variable that will hold the C array...
    494          buffer_var = controller.declare_variable(
    495                            declarations.dummy_type_t( "std::vector< %s >" % self.buffer_item_type.decl_string )
    496                          , "native_" + self.buffer_arg.name )
    497
    498          controller.add_pre_call_code( '%s.reserve( %s );' % ( buffer_var, size_var ) )
    499
    500          copy_pylist2arr = _seq2vector.substitute( type=self.buffer_item_type
    501                                                    , pylist=w_buffer_arg.name
    502                                                    , native_array=buffer_var )
    503
    504          controller.add_pre_call_code( copy_pylist2arr )
    505
    506          controller.modify_arg_expression( self.buffer_arg_index, '&%s[0]' % buffer_var )
    507          controller.modify_arg_expression( self.size_arg_index, '%s' % size_var )
    508
    509 -    def __configure_v_mem_fun_default( self, controller ):
    510          self.__configure_sealed( controller )
    511
    512 -    def __configure_v_mem_fun_override( self, controller ):
    513          raise NotImplementedError()
    514          #global _arr2seq
    515          #pylist = controller.declare_py_variable( declarations.dummy_type_t( 'boost::python::list' )
    516                                                   #, 'py_' + self.arg.name )
    517
    518          #copy_arr2pylist = _arr2seq.substitute( native_array=self.arg.name
    519                                                  #, array_size=self.array_size
    520                                                  #, pylist=pylist )
    521
    522          #controller.add_py_pre_call_code( copy_arr2pylist )
    523
    524 -    def configure_mem_fun( self, controller ):
    525          self.__configure_sealed( controller )
    526
    527 -    def configure_free_fun(self, controller ):
    528          self.__configure_sealed( controller )
    529
    530 -    def configure_virtual_mem_fun( self, controller ):
    531          self.__configure_v_mem_fun_override( controller.override_controller )
    532          self.__configure_v_mem_fun_default( controller.default_controller )
    533
    534
    535 -class transfer_ownership_t(type_modifier_t):
    536      """see http://boost.org/libs/python/doc/v2/faq.html#ownership
    537      """
    538 -    def __init__(self, function, arg_ref):
    539          """Constructor."""
    540          transformer.transformer_t.__init__( self, function )
    541          self.arg = self.get_argument( arg_ref )
    542          self.arg_index = self.function.arguments.index( self.arg )
    543          if not declarations.is_pointer( self.arg.type ):
    544              raise ValueError( '%s\nin order to use "transfer ownership" transformation, argument %s type must be a pointer (got %s).' ) \
    545                    % ( function, self.arg_ref.name, arg.type)
    546
    547 -    def __str__(self):
    548          return "transfer_ownership(%s)" % self.arg.name
    549
    550 -    def __configure_sealed( self, controller ):
    551          w_arg = controller.find_wrapper_arg( self.arg.name )
    552          naked_type = declarations.remove_pointer( self.arg.type )
    553          naked_type = declarations.remove_declarated( naked_type )
    554          w_arg.type = declarations.dummy_type_t( 'std::auto_ptr< %s >' % naked_type.decl_string )
    555          controller.modify_arg_expression(self.arg_index, w_arg.name + '.release()' )
    556
    557 -    def __configure_v_mem_fun_default( self, controller ):
    558          self.__configure_sealed( controller )
    559
    560 -    def configure_mem_fun( self, controller ):
    561          self.__configure_sealed( controller )
    562
    563 -    def configure_free_fun(self, controller ):
    564          self.__configure_sealed( controller )
    565
    566 -    def configure_virtual_mem_fun( self, controller ):
    567          raise NotImplementedError(self.__class__.__name__)
    568
    569  #TODO: FT for constructor
    570      #~ def configure_constructor( self, controller ):
    571          #~ self.__configure_sealed( controller )
    572
    573 -    def required_headers( self ):
    574          """Returns list of header files that transformer generated code depends on."""
    575          return []
    576