Package pygccxml :: Package declarations :: Module class_declaration |

# Source Code for Module pygccxml.declarations.class_declaration

      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 classes, that describes C++ classes
      8
      9  This modules contains definition for next C++ declarations:
     10      - class definition
     11      - class declaration
     12      - small helper class for describing C++ class hierarchy
     13  """
     14
     15  import scopedef
     16  import itertools
     17  import compilers
     18  import algorithm
     19  import declaration
     20  import dependencies
     21  from pygccxml import utils
     22
     23 -class ACCESS_TYPES:
     24      """class that defines "access" constants"""
     25      PUBLIC = "public"
     26      PRIVATE = "private"
     27      PROTECTED = "protected"
     28      ALL = [ PUBLIC, PRIVATE, PROTECTED ]
     29
     30 -class CLASS_TYPES:
     31      """class that defines "class" type constants"""
     32      CLASS = "class"
     33      STRUCT = "struct"
     34      UNION = "union"
     35      ALL = [ CLASS, STRUCT, UNION ]
     36
     37 -def get_partial_name( name ):
     38      import templates
     39      import container_traits #prevent cyclic dependencies
     40      ct = container_traits.find_container_traits( name )
     41      if ct:
     42          return ct.remove_defaults( name )
     43      elif templates.is_instantiation( name ):
     44          tmpl_name, args = templates.split( name )
     45          for i, arg_name in enumerate( args ):
     46              args[i] = get_partial_name( arg_name.strip() )
     47          return templates.join( tmpl_name, args )
     48      else:
     49          return name
     50
     51
     52 -class hierarchy_info_t( object ):
     53      """describes class relationship"""
     54 -    def __init__(self, related_class=None, access=None, is_virtual=False ):
     55          """creates class that contains partial information about class relationship"""
     56          if related_class:
     57              assert( isinstance( related_class, class_t ) )
     58          self._related_class = related_class
     59          if access:
     60              assert( access in ACCESS_TYPES.ALL)
     61          self._access=access
     62          self._is_virtual = is_virtual
     63
     64 -    def __eq__(self, other):
     65          if not isinstance( other, hierarchy_info_t ):
     66              return False
     67          return algorithm.declaration_path( self.related_class ) == algorithm.declaration_path( other.related_class ) \
     68                 and self.access == other.access \
     69                 and self.is_virtual == other.is_virtual
     70
     71 -    def __ne__( self, other):
     72          return not self.__eq__( other )
     73
     74 -    def __lt__(self, other):
     75          if not isinstance( other, self.__class__ ):
     76              return self.__class__.__name__ < other.__class__.__name__
     77          return ( algorithm.declaration_path( self.related_class ), self.access, self.is_virtual  ) \
     78                 < ( algorithm.declaration_path( other.related_class ), other.access, self.is_virtual )
     79
     80 -    def _get_related_class(self):
     81          return self._related_class
     82 -    def _set_related_class(self, new_related_class):
     83          if new_related_class:
     84              assert( isinstance( new_related_class, class_t ) )
     85          self._related_class = new_related_class
     86      related_class = property( _get_related_class, _set_related_class
     87                                , doc="reference to base or derived L{class<class_t>}")
     88
     89 -    def _get_access(self):
     90          return self._access
     91 -    def _set_access(self, new_access):
     92          assert( new_access in ACCESS_TYPES.ALL )
     93          self._access = new_access
     94      access = property( _get_access, _set_access )
     95      access_type = property( _get_access, _set_access
     96                              , doc="describes L{hierarchy type<ACCESS_TYPES>}")
     97
     98      #TODO: check whether GCC XML support this and if so parser this information
     99 -    def _get_is_virtual(self):
    100          return self._is_virtual
    101 -    def _set_is_virtual(self, new_is_virtual):
    102          self._is_virtual = new_is_virtual
    103      is_virtual = property( _get_is_virtual, _set_is_virtual
    104                             , doc="indicates whether the inheritance is virtual or not")
    105
    106
    107 -class class_declaration_t( declaration.declaration_t ):
    108      """describes class declaration"""
    109 -    def __init__( self, name='' ):
    110          """creates class that describes C++ class declaration( and not definition )"""
    111          declaration.declaration_t.__init__( self, name )
    112          self._aliases = []
    113          self._container_traits = None
    114          self._container_traits_set = False
    115
    116 -    def _get__cmp__items(self):
    117          """implementation details"""
    118          return []
    119
    120 -    def i_depend_on_them( self, recursive=True ):
    121          return []
    122
    123 -    def _get_aliases(self):
    124          return self._aliases
    125 -    def _set_aliases( self, new_aliases ):
    126          self._aliases = new_aliases
    127      aliases = property( _get_aliases, _set_aliases
    128                           , doc="List of L{aliases<typedef_t>} to this instance")
    129
    130      @property
    131 -    def container_traits( self ):
    132          """reference to L{container traits<container_traits.py>} or None"""
    133          if self._container_traits_set == False:
    134              import container_traits #prevent cyclic dependencies
    135              self._container_traits_set = True
    136              self._container_traits = container_traits.find_container_traits( self )
    137          return self._container_traits
    138
    139 -    def _get_partial_name_impl( self ):
    140          return get_partial_name( self.name )
    141
    142 -class class_t( scopedef.scopedef_t ):
    143      """describes class definition"""
    144
    145      USE_DEMANGLED_AS_NAME = True
    146 -    def __init__( self, name='', class_type=CLASS_TYPES.CLASS, is_abstract=False ):
    147          """creates class that describes C++ class definition"""
    148          scopedef.scopedef_t.__init__( self, name )
    149          if class_type:
    150              assert( class_type in CLASS_TYPES.ALL )
    151          self._class_type = class_type
    152          self._bases = []
    153          self._derived = []
    154          self._is_abstract = is_abstract
    155          self._public_members = []
    156          self._private_members = []
    157          self._protected_members = []
    158          self._aliases = []
    159          self._byte_size = 0
    160          self._byte_align = 0
    161          self._container_traits = None
    162          self._container_traits_set = False
    163          self._recursive_bases = None
    164          self._recursive_derived = None
    165
    166 -    def _get_name_impl( self ):
    167          if not self._name: #class with empty name
    168              return self._name
    169          elif class_t.USE_DEMANGLED_AS_NAME and self.demangled and 'GCC' in self.compiler:
    170              if not self.cache.demangled_name:
    171                  fname = algorithm.full_name( self.parent )
    172                  if fname.startswith( '::' ) and not self.demangled.startswith( '::' ):
    173                      fname = fname[2:]
    174                  if self.demangled.startswith( fname ):
    175                      tmp = self.demangled[ len( fname ): ] #demangled::name
    176                      if tmp.startswith( '::' ):
    177                          tmp = tmp[2:]
    178                      if '<' not in tmp and '<' in self._name:
    179                          #we have template class, but for some reason demangled
    180                          #name doesn't contain any template
    181                          #This happens for std::string class, but this breaks
    182                          #other cases, because this behaviour is not consistent
    183                          self.cache.demangled_name = self._name
    184                          return self.cache.demangled_name
    185                      else:
    186                          self.cache.demangled_name = tmp
    187                          return tmp
    188                  else:
    189                      self.cache.demangled_name = self._name
    190                      return self._name
    191              else:
    192                  return self.cache.demangled_name
    193          else:
    194              return self._name
    195
    196 -    def __str__(self):
    197          name = algorithm.full_name(self)
    198          if name[:2]=="::":
    199              name = name[2:]
    200          return "%s [%s]"%(name, self.class_type)
    201
    202 -    def _get__cmp__scope_items(self):
    203          """implementation details"""
    204          return [ self.class_type
    205                   , self._sorted_list( [ algorithm.declaration_path( base.related_class ) for base in self.bases ] )
    206                   , self._sorted_list( [ algorithm.declaration_path( derive.related_class ) for derive in self.derived ] )
    207                   , self.is_abstract
    208                   , self._sorted_list( self.public_members )
    209                   , self._sorted_list( self.private_members )
    210                   , self._sorted_list( self.protected_members ) ]
    211
    212 -    def __eq__(self, other):
    213          if not scopedef.scopedef_t.__eq__( self, other ):
    214              return False
    215          return self.class_type == other.class_type \
    216                 and self._sorted_list( [ algorithm.declaration_path( base.related_class ) for base in self.bases ] ) \
    217                     == other._sorted_list( [ algorithm.declaration_path( base.related_class ) for base in other.bases ] ) \
    218                 and self._sorted_list( [ algorithm.declaration_path( derive.related_class ) for derive in self.derived ] ) \
    219                     == other._sorted_list( [ algorithm.declaration_path( derive.related_class ) for derive in other.derived ] ) \
    220                 and self.is_abstract == other.is_abstract \
    221                 and self._sorted_list( self.public_members ) \
    222                     == other._sorted_list( other.public_members ) \
    223                 and self._sorted_list( self.private_members ) \
    224                     == other._sorted_list( other.private_members ) \
    225                 and self._sorted_list( self.protected_members ) \
    226                     == self._sorted_list( other.protected_members )
    227
    228 -    def _get_class_type(self):
    229          return self._class_type
    230 -    def _set_class_type( self, new_class_type):
    231          if new_class_type:
    232              assert( new_class_type in CLASS_TYPES.ALL )
    233          self._class_type = new_class_type
    234      class_type = property( _get_class_type, _set_class_type
    235                             , doc="describes class L{type<CLASS_TYPES>}")
    236
    237 -    def _get_bases(self):
    238          return self._bases
    239 -    def _set_bases( self, new_bases ):
    240          self._bases = new_bases
    241      bases = property( _get_bases, _set_bases
    242                        , doc="list of L{base classes<hierarchy_info_t>}")
    243
    244      @property
    245 -    def recursive_bases(self):
    246          """list of all L{base classes<hierarchy_info_t>}"""
    247          if self._recursive_bases is None:
    248              to_go = self.bases[:]
    249              all_bases = []
    250              while to_go:
    251                  base = to_go.pop()
    252                  if base not in all_bases:
    253                      all_bases.append( base )
    254                      to_go.extend( base.related_class.bases )
    255              self._recursive_bases = all_bases
    256          return self._recursive_bases
    257
    258 -    def _get_derived(self):
    259          return self._derived
    260 -    def _set_derived( self, new_derived ):
    261          self._derived = new_derived
    262      derived = property( _get_derived, _set_derived
    263                          , doc="list of L{derived classes<hierarchy_info_t>}")
    264
    265      @property
    266 -    def recursive_derived(self):
    267          """list of all L{derive classes<hierarchy_info_t>}"""
    268          if self._recursive_derived is None:
    269              to_go = self.derived[:]
    270              all_derived = []
    271              while to_go:
    272                  derive = to_go.pop()
    273                  if derive not in all_derived:
    274                      all_derived.append( derive )
    275                      to_go.extend( derive.related_class.derived )
    276              self._recursive_derived = all_derived
    277          return self._recursive_derived
    278
    279 -    def _get_is_abstract(self):
    280          if self.compiler == compilers.MSVC_PDB_9:
    281              #prevent cyclic dependencies
    282              import calldef
    283              import function_traits
    284              from matchers import virtuality_type_matcher_t as vtmatcher_t
    285              filter_pv = vtmatcher_t( calldef.VIRTUALITY_TYPES.PURE_VIRTUAL )
    286              if self.calldefs( filter_pv, recursive=False, allow_empty=True ):
    287                  return True
    288              filter_npv = vtmatcher_t( calldef.VIRTUALITY_TYPES.VIRTUAL ) \
    289                           | vtmatcher_t( calldef.VIRTUALITY_TYPES.NOT_VIRTUAL )
    290              pv_calldefs = []
    291              npv_calldefs = []
    292
    293              npv_calldefs.extend( self.calldefs( filter_npv, recursive=False, allow_empty=True ) )
    294              for base in self.recursive_bases:
    295                  cls = base.related_class
    296                  pv_calldefs.extend( cls.calldefs( filter_pv, recursive=False, allow_empty=True ) )
    297                  npv_calldefs.extend( cls.calldefs( filter_npv, recursive=False, allow_empty=True ) )
    298
    299              for pure_virtual in pv_calldefs:
    300                  impl_found = filter( lambda f: function_traits.is_same_function( pure_virtual, f )
    301                                       , npv_calldefs )
    302                  if not impl_found:
    303                      return True
    304              return False
    305          else:
    306              return self._is_abstract
    307 -    def _set_is_abstract( self, is_abstract ):
    308          self._is_abstract = is_abstract
    309      is_abstract = property( _get_is_abstract, _set_is_abstract
    310                              ,doc="describes whether class abstract or not" )
    311
    312 -    def _get_public_members(self):
    313          return self._public_members
    314 -    def _set_public_members( self, new_public_members ):
    315          self._public_members = new_public_members
    316      public_members = property( _get_public_members, _set_public_members
    317                                 , doc="list of all public L{members<declaration_t>}")
    318
    319 -    def _get_private_members(self):
    320          return self._private_members
    321 -    def _set_private_members( self, new_private_members ):
    322          self._private_members = new_private_members
    323      private_members = property( _get_private_members, _set_private_members
    324                                  , doc="list of all private L{members<declaration_t>}")
    325
    326 -    def _get_protected_members(self):
    327          return self._protected_members
    328 -    def _set_protected_members( self, new_protected_members ):
    329          self._protected_members = new_protected_members
    330      protected_members = property( _get_protected_members, _set_protected_members
    331                                    , doc="list of all protected L{members<declaration_t>}" )
    332
    333 -    def _get_aliases(self):
    334          return self._aliases
    335 -    def _set_aliases( self, new_aliases ):
    336          self._aliases = new_aliases
    337      aliases = property( _get_aliases, _set_aliases
    338                           , doc="List of L{aliases<typedef_t>} to this instance")
    339
    340 -    def _get_byte_size(self):
    341          return self._byte_size
    342 -    def _set_byte_size( self, new_byte_size ):
    343          self._byte_size = new_byte_size
    344      byte_size = property( _get_byte_size, _set_byte_size
    345                            , doc="Size of this class in bytes @type: int")
    346
    347 -    def _get_byte_align(self):
    348          if self.compiler == compilers.MSVC_PDB_9:
    349              compilers.on_missing_functionality( self.compiler, "byte align" )
    350          return self._byte_align
    351 -    def _set_byte_align( self, new_byte_align ):
    352          self._byte_align = new_byte_align
    353      byte_align = property( _get_byte_align, _set_byte_align
    354                            , doc="Alignment of this class in bytes @type: int")
    355
    356 -    def _get_declarations_impl(self):
    357          return self.get_members()
    358
    359 -    def get_members( self, access=None):
    360          """
    361          returns list of members according to access type
    362
    363          If access equals to None, then returned list will contain all members.
    364          You should not modify the list content, otherwise different optimization
    365          data will stop work and may to give you wrong results.
    366
    367          @param access: describes desired members
    368          @type access: L{ACCESS_TYPES}
    369
    370          @return: [ members ]
    371          """
    372          if access == ACCESS_TYPES.PUBLIC:
    373              return self.public_members
    374          elif access == ACCESS_TYPES.PROTECTED:
    375              return self.protected_members
    376          elif access == ACCESS_TYPES.PRIVATE:
    377              return self.private_members
    378          else:
    379              all_members = []
    380              all_members.extend( self.public_members )
    381              all_members.extend( self.protected_members )
    382              all_members.extend( self.private_members )
    383              return all_members
    384
    385 -    def adopt_declaration( self, decl, access ):
    386          """adds new declaration to the class
    387
    388          @param decl: reference to a L{declaration<declaration_t>}
    389
    390          @param access: member access type
    391          @type access: L{ACCESS_TYPES}
    392          """
    393          if access == ACCESS_TYPES.PUBLIC:
    394              self.public_members.append( decl )
    395          elif access == ACCESS_TYPES.PROTECTED:
    396              self.protected_members.append( decl )
    397          elif access == ACCESS_TYPES.PRIVATE:
    398              self.private_members.append( decl )
    399          else:
    400              raise RuntimeError( "Invalid access type: %s." % access )
    401          decl.parent = self
    402          decl.cache.reset()
    403          decl.cache.access_type = access
    404
    405 -    def remove_declaration( self, decl ):
    406          """
    407          removes decl from  members list
    408
    409          @param decl: declaration to be removed
    410          @type decl: L{declaration_t}
    411          """
    412          container = None
    413          access_type = self.find_out_member_access_type( decl )
    414          if access_type == ACCESS_TYPES.PUBLIC:
    415              container = self.public_members
    416          elif access_type == ACCESS_TYPES.PROTECTED:
    417              container = self.protected_members
    418          else: #decl.cache.access_type == ACCESS_TYPES.PRVATE
    419              container = self.private_members
    420          del container[ container.index( decl ) ]
    421          decl.cache.reset()
    422
    423 -    def find_out_member_access_type( self, member ):
    424          """
    425          returns member access type
    426
    427          @param member: member of the class
    428          @type member: L{declaration_t}
    429
    430          @return: L{ACCESS_TYPES}
    431          """
    432          assert member.parent is self
    433          if not member.cache.access_type:
    434              access_type = None
    435              if member in self.public_members:
    436                  access_type = ACCESS_TYPES.PUBLIC
    437              elif member in self.protected_members:
    438                  access_type = ACCESS_TYPES.PROTECTED
    439              elif member in self.private_members:
    440                  access_type = ACCESS_TYPES.PRIVATE
    441              else:
    442                  raise RuntimeError( "Unable to find member within internal members list." )
    443              member.cache.access_type = access_type
    444              return access_type
    445          else:
    446              return member.cache.access_type
    447
    448 -    def __find_out_member_dependencies( self, access_type ):
    449          members = self.get_members( access_type )
    450          answer = []
    451          map( lambda mem: answer.extend( mem.i_depend_on_them(recursive=True) ), members )
    452          member_ids = set( map( lambda m: id( m ), members ) )
    453          for dependency in answer:
    454              if id( dependency.declaration ) in member_ids:
    455                  dependency.access_type = access_type
    456          return answer
    457
    458 -    def i_depend_on_them( self, recursive=True ):
    459          report_dependency = lambda *args: dependencies.dependency_info_t( self, *args )
    460
    461          answer = []
    462
    463          map( lambda base: answer.append( report_dependency( base.related_class, base.access_type, "base class" ) )
    464               , self.bases )
    465
    466          if recursive:
    467              map( lambda access_type: answer.extend( self.__find_out_member_dependencies( access_type ) )
    468                   , ACCESS_TYPES.ALL )
    469
    470          return answer
    471
    472      @property
    473 -    def container_traits( self ):
    474          """reference to L{container traits<container_traits.py>} or None"""
    475          if self._container_traits_set == False:
    476              import container_traits #prevent cyclic dependencies
    477              self._container_traits_set = True
    478              self._container_traits = container_traits.find_container_traits( self )
    479          return self._container_traits
    480
    481 -    def find_copy_constructor( self ):
    482          copy_ = self.constructors( lambda x: x.is_copy_constructor, recursive=False, allow_empty=True )
    483          if copy_:
    484              return copy_[0]
    485          else:
    486              return None
    487
    488 -    def find_trivial_constructor( self ):
    489          trivial = self.constructors( lambda x: x.is_trivial_constructor, recursive=False, allow_empty=True )
    490          if trivial:
    491              return trivial[0]
    492          else:
    493              return None
    494
    495 -    def _get_partial_name_impl( self ):
    496          import type_traits #prevent cyclic dependencies
    497          if type_traits.is_std_string( self ):
    498              return 'string'
    499          elif type_traits.is_std_wstring( self ):
    500              return 'wstring'
    501          else:
    502              return get_partial_name( self.name )
    503
    504 -    def find_noncopyable_vars( self ):
    505          """returns list of all noncopyable variables"""
    506          import type_traits as tt#prevent cyclic dependencies
    507          logger = utils.loggers.cxx_parser
    508          mvars = self.vars( lambda v: not v.type_qualifiers.has_static, recursive=False, allow_empty=True )
    509          noncopyable_vars = []
    510          for mvar in mvars:
    511              type_ = tt.remove_reference( mvar.type )
    512              if tt.is_const( type_ ):
    513                  no_const = tt.remove_const( type_ )
    514                  if tt.is_fundamental( no_const ) or tt.is_enum( no_const):
    515                      logger.debug( "__contains_noncopyable_mem_var - %s - TRUE - containes const member variable - fundamental or enum" % self.decl_string )
    516                      noncopyable_vars.append( mvar )
    517                  if tt.is_class( no_const ):
    518                      logger.debug( "__contains_noncopyable_mem_var - %s - TRUE - containes const member variable - class" % self.decl_string )
    519                      noncopyable_vars.append( mvar )
    520                  if tt.is_array( no_const ):
    521                      logger.debug( "__contains_noncopyable_mem_var - %s - TRUE - containes const member variable - array" % self.decl_string )
    522                      noncopyable_vars.append( mvar )
    523              if tt.class_traits.is_my_case( type_ ):
    524                  cls = tt.class_traits.get_declaration( type_ )
    525                  if tt.is_noncopyable( cls ):
    526                      logger.debug( "__contains_noncopyable_mem_var - %s - TRUE - containes member variable - class that is not copyable" % self.decl_string )
    527                      noncopyable_vars.append( mvar )
    528          logger.debug( "__contains_noncopyable_mem_var - %s - false - doesn't contains noncopyable members" % self.decl_string )
    529          return noncopyable_vars
    530
    531
    532  class_types = ( class_t, class_declaration_t )
    533