Package pygccxml :: Package declarations :: Module container_traits

# Source Code for Module pygccxml.declarations.container_traits

      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  """
      7  defines few algorithms, that deals with different properties of std containers
      8  """
      9
     10  import types
     11  import string
     12  import calldef
     13  import cpptypes
     14  import namespace
     15  import templates
     16  import type_traits
     17  import class_declaration
     18
     19  std_namespaces = ( 'std', 'stdext', '__gnu_cxx' )
     20
     21 -class defaults_eraser:
     22      @staticmethod
     23 -    def normalize( type_str ):
     24          return type_str.replace( ' ', '' )
     25
     26      @staticmethod
     27 -    def replace_basic_string( cls_name ):
     28          strings = {
     29                'std::string' : ( 'std::basic_string<char,std::char_traits<char>,std::allocator<char> >'
     30                                  , 'std::basic_string<char, std::char_traits<char>, std::allocator<char> >' )
     31              , 'std::wstring' : ( 'std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> >'
     32                                   , 'std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >' ) }
     33
     34          new_name = cls_name
     35          for short_name, long_names in strings.iteritems():
     36              for lname in long_names:
     37                  new_name = new_name.replace( lname, short_name )
     38          return new_name
     39
     40 -    class recursive_impl:
     41          @staticmethod
     42 -        def decorated_call_prefix( cls_name, text, doit ):
     43              has_text = cls_name.startswith( text )
     44              if has_text:
     45                  cls_name = cls_name[ len( text ): ]
     46              answer = doit( cls_name )
     47              if has_text:
     48                  answer = text + answer
     49              return answer
     50
     51          @staticmethod
     52 -        def decorated_call_suffix( cls_name, text, doit ):
     53              has_text = cls_name.endswith( text )
     54              if has_text:
     55                  cls_name = cls_name[: len( text )]
     56              answer = doit( cls_name )
     57              if has_text:
     58                  answer = answer + text
     59              return answer
     60
     61          @staticmethod
     62 -        def erase_call( cls_name ):
     63              global find_container_traits
     64              c_traits = find_container_traits( cls_name )
     65              if not c_traits:
     66                  return cls_name
     67              return c_traits.remove_defaults( cls_name )
     68
     69          @staticmethod
     70 -        def erase_recursive( cls_name ):
     71              ri = defaults_eraser.recursive_impl
     72              no_std = lambda cls_name: ri.decorated_call_prefix( cls_name, 'std::', ri.erase_call )
     73              no_stdext = lambda cls_name: ri.decorated_call_prefix( cls_name, 'stdext::', no_std )
     74              no_gnustd = lambda cls_name: ri.decorated_call_prefix( cls_name, '__gnu_cxx::', no_stdext )
     75              no_const = lambda cls_name: ri.decorated_call_prefix( cls_name, 'const ', no_gnustd )
     76              no_end_const = lambda cls_name: ri.decorated_call_suffix( cls_name, ' const', no_const )
     77              return no_end_const( cls_name )
     78
     79      @staticmethod
     80 -    def erase_recursive( cls_name ):
     81          return defaults_eraser.recursive_impl.erase_recursive( cls_name )
     82
     83      @staticmethod
     84 -    def erase_allocator( cls_name, default_allocator='std::allocator' ):
     85          cls_name = defaults_eraser.replace_basic_string( cls_name )
     86          c_name, c_args = templates.split( cls_name )
     87          if 2 != len( c_args ):
     88              return
     89          value_type = c_args[0]
     90          tmpl = string.Template( "$container< $value_type, $allocator<$value_type> >" )
     91          tmpl = tmpl.substitute( container=c_name, value_type=value_type, allocator=default_allocator )
     92          if defaults_eraser.normalize( cls_name ) == defaults_eraser.normalize( tmpl ):
     93              return templates.join( c_name, [defaults_eraser.erase_recursive( value_type )] )
     94
     95      @staticmethod
     96 -    def erase_container( cls_name, default_container_name='std::deque' ):
     97          cls_name = defaults_eraser.replace_basic_string( cls_name )
     98          c_name, c_args = templates.split( cls_name )
     99          if 2 != len( c_args ):
    100              return
    101          value_type = c_args[0]
    102          dc_no_defaults = defaults_eraser.erase_recursive( c_args[1] )
    103          if defaults_eraser.normalize( dc_no_defaults ) \
    104             != defaults_eraser.normalize( templates.join( default_container_name, [value_type] ) ):
    105              return
    106          return templates.join( c_name, [defaults_eraser.erase_recursive( value_type )] )
    107
    108      @staticmethod
    109 -    def erase_container_compare( cls_name, default_container_name='std::vector', default_compare='std::less' ):
    110          cls_name = defaults_eraser.replace_basic_string( cls_name )
    111          c_name, c_args = templates.split( cls_name )
    112          if 3 != len( c_args ):
    113              return
    114          dc_no_defaults = defaults_eraser.erase_recursive( c_args[1] )
    115          if defaults_eraser.normalize( dc_no_defaults ) \
    116             != defaults_eraser.normalize( templates.join( default_container_name, [c_args[0]] ) ):
    117              return
    118          dcomp_no_defaults = defaults_eraser.erase_recursive( c_args[2] )
    119          if defaults_eraser.normalize( dcomp_no_defaults ) \
    120             != defaults_eraser.normalize( templates.join( default_compare, [c_args[0]] ) ):
    121              return
    122          value_type = defaults_eraser.erase_recursive( c_args[0] )
    123          return templates.join( c_name, [value_type] )
    124
    125      @staticmethod
    126 -    def erase_compare_allocator( cls_name, default_compare='std::less', default_allocator='std::allocator' ):
    127          cls_name = defaults_eraser.replace_basic_string( cls_name )
    128          c_name, c_args = templates.split( cls_name )
    129          if 3 != len( c_args ):
    130              return
    131          value_type = c_args[0]
    132          tmpl = string.Template( "$container< $value_type, $compare<$value_type>, $allocator<$value_type> >" )
    133          tmpl = tmpl.substitute( container=c_name
    134                                  , value_type=value_type
    135                                  , compare=default_compare
    136                                  , allocator=default_allocator )
    137          if defaults_eraser.normalize( cls_name ) == defaults_eraser.normalize( tmpl ):
    138              return templates.join( c_name, [defaults_eraser.erase_recursive( value_type )] )
    139
    140      @staticmethod
    141 -    def erase_map_compare_allocator( cls_name, default_compare='std::less', default_allocator='std::allocator' ):
    142          cls_name = defaults_eraser.replace_basic_string( cls_name )
    143          c_name, c_args = templates.split( cls_name )
    144          if 4 != len( c_args ):
    145              return
    146          key_type = c_args[0]
    147          mapped_type = c_args[1]
    148          tmpls = [
    149              string.Template( "$container< $key_type, $mapped_type, $compare<$key_type>, $allocator< std::pair< const $key_type, $mapped_type> > >" )
    150              , string.Template( "$container< $key_type, $mapped_type, $compare<$key_type>, $allocator< std::pair< $key_type const, $mapped_type> > >" )
    151              , string.Template( "$container< $key_type, $mapped_type, $compare<$key_type>, $allocator< std::pair< $key_type, $mapped_type> > >" )]
    152          for tmpl in tmpls:
    153              tmpl = tmpl.substitute( container=c_name
    154                                      , key_type=key_type
    155                                      , mapped_type=mapped_type
    156                                      , compare=default_compare
    157                                      , allocator=default_allocator )
    158              if defaults_eraser.normalize( cls_name ) == defaults_eraser.normalize( tmpl ):
    159                  return templates.join( c_name
    160                                         , [ defaults_eraser.erase_recursive( key_type )
    161                                             , defaults_eraser.erase_recursive( mapped_type )] )
    162
    163
    164      @staticmethod
    165 -    def erase_hash_allocator( cls_name ):
    166          cls_name = defaults_eraser.replace_basic_string( cls_name )
    167          c_name, c_args = templates.split( cls_name )
    168          if len( c_args ) < 3:
    169              return
    170
    171          default_hash=None
    172          default_less='std::less'
    173          default_equal_to='std::equal_to'
    174          default_allocator='std::allocator'
    175
    176          tmpl = None
    177          if 3 == len( c_args ):
    178              default_hash='hash_compare'
    179              tmpl = "$container< $value_type, $hash<$value_type, $less<$value_type> >, $allocator<$value_type> >"
    180          elif 4 == len( c_args ):
    181              default_hash='hash'
    182              tmpl = "$container< $value_type, $hash<$value_type >, $equal_to<$value_type >, $allocator<$value_type> >"
    183          else:
    184              return
    185
    186          value_type = c_args[0]
    187          tmpl = string.Template( tmpl )
    188          for ns in std_namespaces:
    189              inst = tmpl.substitute( container=c_name
    190                                      , value_type=value_type
    191                                      , hash= ns + '::' + default_hash
    192                                      , less=default_less
    193                                      , equal_to=default_equal_to
    194                                      , allocator=default_allocator )
    195              if defaults_eraser.normalize( cls_name ) == defaults_eraser.normalize( inst ):
    196                  return templates.join( c_name, [defaults_eraser.erase_recursive( value_type )] )
    197
    198
    199      @staticmethod
    200 -    def erase_hashmap_compare_allocator( cls_name ):
    201          cls_name = defaults_eraser.replace_basic_string( cls_name )
    202          c_name, c_args = templates.split( cls_name )
    203
    204          default_hash=None
    205          default_less='std::less'
    206          default_allocator='std::allocator'
    207          default_equal_to = 'std::equal_to'
    208
    209          tmpl = None
    210          key_type = None
    211          mapped_type = None
    212          if 2 < len( c_args ):
    213              key_type = c_args[0]
    214              mapped_type = c_args[1]
    215          else:
    216              return
    217
    218          if 4 == len( c_args ):
    219              default_hash = 'hash_compare'
    220              tmpl = string.Template( "$container< $key_type, $mapped_type, $hash<$key_type, $less<$key_type> >, $allocator< std::pair< const $key_type, $mapped_type> > >" )
    221              if key_type.startswith( 'const ' ) or key_type.endswith( ' const' ):
    222                  tmpl = string.Template( "$container< $key_type, $mapped_type, $hash<$key_type, $less<$key_type> >, $allocator< std::pair< $key_type, $mapped_type> > >" )
    223          elif 5 == len( c_args ):
    224              default_hash = 'hash'
    225              tmpl = string.Template( "$container< $key_type, $mapped_type, $hash<$key_type >, $equal_to<$key_type>, $allocator< $mapped_type> >" )
    226              if key_type.startswith( 'const ' ) or key_type.endswith( ' const' ):
    227                  tmpl = string.Template( "$container< $key_type, $mapped_type, $hash<$key_type >, $equal_to<$key_type>, $allocator< $mapped_type > >" )
    228          else:
    229              return
    230
    231          for ns in std_namespaces:
    232              inst = tmpl.substitute( container=c_name
    233                                      , key_type=key_type
    234                                      , mapped_type=mapped_type
    235                                      , hash=ns + '::' + default_hash
    236                                      , less=default_less
    237                                      , equal_to = default_equal_to
    238                                      , allocator=default_allocator )
    239              if defaults_eraser.normalize( cls_name ) == defaults_eraser.normalize( inst ):
    240                  return templates.join( c_name
    241                                         , [ defaults_eraser.erase_recursive( key_type )
    242                                             , defaults_eraser.erase_recursive( mapped_type )] )
    243
    244
    245 -class container_traits_impl_t:
    246      """this class implements the functionality needed for convinient work with
    247      STD container classes.
    248
    249      Implemented functionality:
    250          - find out whether a declaration is STD container or not
    251          - find out container value( mapped ) type
    252
    253      This class tries to be useful as much, as possible. For example, for class
    254      declaration( and not definition ) it parsers the class name in order to
    255      extract all the information.
    256      """
    257 -    def __init__( self
    258                    , container_name
    259                    , element_type_index
    260                    , element_type_typedef
    261                    , defaults_remover
    262                    , key_type_index=None
    263                    , key_type_typedef=None ):
    264          """
    265          container_name - std container name
    266          element_type_index - position of value\\mapped type within template
    267            arguments list
    268          element_type_typedef - class typedef to the value\\mapped type
    269          key_type_index - position of key type within template arguments list
    270          key_type_typedef - class typedef to the key type
    271          """
    272          self._name = container_name
    273          self.remove_defaults_impl = defaults_remover
    274          self.element_type_index = element_type_index
    275          self.element_type_typedef = element_type_typedef
    276          self.key_type_index = key_type_index
    277          self.key_type_typedef = key_type_typedef
    278
    279 -    def name(self):
    280          return self._name
    281
    282 -    def get_container_or_none( self, type ):
    283          """returns reference to the class declaration or None"""
    284          type = type_traits.remove_alias( type )
    285          type = type_traits.remove_cv( type )
    286
    287          cls = None
    288          if isinstance( type, cpptypes.declarated_t ):
    289              cls = type_traits.remove_alias( type.declaration )
    290          elif isinstance( type, class_declaration.class_t ):
    291              cls = type
    292          elif isinstance( type, class_declaration.class_declaration_t ):
    293              cls = type
    294          else:
    295              return
    296
    297          if not cls.name.startswith( self.name() + '<' ):
    298              return
    299
    300          for ns in std_namespaces:
    301              if type_traits.impl_details.is_defined_in_xxx( ns, cls ):
    302                  return cls
    303
    304 -    def is_my_case( self, type ):
    305          """checks, whether type is STD container or not"""
    306          return bool( self.get_container_or_none( type ) )
    307
    308 -    def class_declaration( self, type ):
    309          """returns reference to the class declaration"""
    310          cls = self.get_container_or_none( type )
    311          if not cls:
    312              raise TypeError( 'Type "%s" is not instantiation of std::%s' % ( type.decl_string, self.name() ) )
    313          return cls
    314
    315 -    def is_sequence( self, type ):
    316          #raise exception if type is not container
    317          unused = self.class_declaration( type )
    318          return self.key_type_index is None
    319
    320 -    def is_mapping( self, type ):
    321          return not self.is_sequence( type )
    322
    323 -    def __find_xxx_type( self, type, xxx_index, xxx_typedef, cache_property_name ):
    324          cls = self.class_declaration( type )
    325          result = getattr( cls.cache, cache_property_name )
    326          if not result:
    327              if isinstance( cls, class_declaration.class_t ):
    328                  xxx_type = cls.typedef( xxx_typedef, recursive=False ).type
    329                  result = type_traits.remove_declarated( xxx_type )
    330              else:
    331                  xxx_type_str = templates.args( cls.name )[xxx_index]
    332                  result = type_traits.impl_details.find_value_type( cls.top_parent, xxx_type_str )
    333                  if None is result:
    334                      raise RuntimeError( "Unable to find out %s '%s' key\\value type."
    335                                          % ( self.name(), cls.decl_string ) )
    336              setattr( cls.cache, cache_property_name, result )
    337          return result
    338
    339 -    def element_type( self, type ):
    340          """returns reference to the class value\\mapped type declaration"""
    341          return self.__find_xxx_type( type
    342                                       , self.element_type_index
    343                                       , self.element_type_typedef
    344                                       , 'container_element_type')
    345
    346 -    def key_type( self, type ):
    347          """returns reference to the class key type declaration"""
    348          if not self.is_mapping( type ):
    349              raise TypeError( 'Type "%s" is not "mapping" container' % str( type ) )
    350          return self.__find_xxx_type( type
    351                                       , self.key_type_index
    352                                       , self.key_type_typedef
    353                                       , 'container_key_type' )
    354
    355 -    def remove_defaults( self, type_or_string ):
    356          """remove template defaults from a template class instantiation
    357
    358          For example:
    359              std::vector< int, std::allocator< int > >
    360          will become
    361              std::vector< int >
    362          """
    363          name = type_or_string
    364          if not isinstance( type_or_string, types.StringTypes ):
    365              name = self.class_declaration( type_or_string ).name
    366          if not self.remove_defaults_impl:
    367              return name
    368          no_defaults = self.remove_defaults_impl( name )
    369          if not no_defaults:
    370              return name
    371          else:
    372              return no_defaults
    373
    374  create_traits = container_traits_impl_t
    375  list_traits = create_traits( 'list'
    376                               , 0
    377                               , 'value_type'
    378                               , defaults_eraser.erase_allocator )
    379
    380  deque_traits = create_traits( 'deque'
    381                                , 0
    382                                , 'value_type'
    383                                , defaults_eraser.erase_allocator )
    384
    385  queue_traits = create_traits( 'queue'
    386                                , 0
    387                                , 'value_type'
    388                                , defaults_eraser.erase_container )
    389
    390  priority_queue_traits = create_traits( 'priority_queue'
    391                                         , 0
    392                                         , 'value_type'
    393                                         , defaults_eraser.erase_container_compare )
    394
    395  vector_traits = create_traits( 'vector'
    396                                 , 0
    397                                 , 'value_type'
    398                                 , defaults_eraser.erase_allocator )
    399
    400  stack_traits = create_traits( 'stack'
    401                                , 0
    402                                , 'value_type'
    403                                , defaults_eraser.erase_container )
    404
    405  map_traits = create_traits( 'map'
    406                              , 1
    407                              , 'mapped_type'
    408                              , defaults_eraser.erase_map_compare_allocator
    409                              , key_type_index=0
    410                              , key_type_typedef='key_type')
    411
    412  multimap_traits = create_traits( 'multimap'
    413                                   , 1
    414                                   , 'mapped_type'
    415                                   , defaults_eraser.erase_map_compare_allocator
    416                                   , key_type_index=0
    417                                   , key_type_typedef='key_type')
    418
    419
    420  hash_map_traits = create_traits( 'hash_map'
    421                                   , 1
    422                                   , 'mapped_type'
    423                                   , defaults_eraser.erase_hashmap_compare_allocator
    424                                   , key_type_index=0
    425                                   , key_type_typedef='key_type')
    426
    427
    428  hash_multimap_traits = create_traits( 'hash_multimap'
    429                                        , 1
    430                                        , 'mapped_type'
    431                                        , defaults_eraser.erase_hashmap_compare_allocator
    432                                        , key_type_index=0
    433                                        , key_type_typedef='key_type')
    434
    435  set_traits = create_traits( 'set'
    436                              , 0
    437                              , 'value_type'
    438                              , defaults_eraser.erase_compare_allocator)
    439
    440  multiset_traits = create_traits( 'multiset'
    441                                   , 0
    442                                   , 'value_type'
    443                                   , defaults_eraser.erase_compare_allocator )
    444
    445  hash_set_traits = create_traits( 'hash_set'
    446                                   , 0
    447                                   , 'value_type'
    448                                   , defaults_eraser.erase_hash_allocator )
    449
    450  hash_multiset_traits = create_traits( 'hash_multiset'
    451                                        , 0
    452                                        , 'value_type'
    453                                        , defaults_eraser.erase_hash_allocator )
    454
    455  container_traits = (
    456        list_traits
    457      , deque_traits
    458      , queue_traits
    459      , priority_queue_traits
    460      , vector_traits
    461      , stack_traits
    462      , map_traits
    463      , multimap_traits
    464      , hash_map_traits
    465      , hash_multimap_traits
    466      , set_traits
    467      , hash_set_traits
    468      , multiset_traits
    469      , hash_multiset_traits )
    470  """tuple of all STD container traits classes"""
    471
    472 -def find_container_traits( cls_or_string ):
    473      if isinstance( cls_or_string, types.StringTypes ):
    474          if not templates.is_instantiation( cls_or_string ):
    475              return None
    476          name = templates.name( cls_or_string )
    477          if name.startswith( 'std::' ):
    478              name = name[ len( 'std::' ): ]
    479          for cls_traits in container_traits:
    480              if cls_traits.name() == name:
    481                  return cls_traits
    482      else:
    483          for cls_traits in container_traits:
    484              if cls_traits.is_my_case( cls_or_string ):
    485                  return cls_traits
    486