Consider a world of programming without the ability to define types. Without types, OO programming crumbles to ashes, C#, Java and Visual Basic do not even exist. C++ becomes a super set of C with only exceptions and template functions as a major language feature. Template meta programming in C++ is no longer possible. All you can do in .Net framework is work with C or write your main function in IL and find some functional language to work with. Writing even a single line in SmallTalk is impossible as the act to declaring a class is a message to Class object to define that class. Even in C, you cannot play with structures, enumerations and bit fields, because in essence, they are all user defined types. Functional languages rise to the surface, Google’s map-reduce is seen as the solution to every problem.
But fortunately, this was only a nightmare. We do live in a world with happy classes, structures, enumerations, unions and a decent meta programmable language. From a compiler writer’s point of view, the notion of meta programmability is enough to give him nightmares, no decency on his part. Before we start discussing the classes present in CppCodProvider for encapsulating types, I should point out that some of them still have incomplete implementations. There are so many things left out that they merit discussion before we do anything else.
· It is very difficult to partially specialize a user-defined type.
· Union does not write anything at this point.
· It is not possible to initialize enumerators with values.
· A user defined type does not write anything at this point.
· Pointers to function, member function and member variables are very hard to work with.
· Friends cannot be declared at this point.
· Forward declarations are not possible.
With all that said, let us move on to the types contained within. Perhaps the simplest type is the Enumeration. An enumeration is a set of named constants that have integral values, either explicitly assigned or implicitly generated by the compiler. The peculiarity of C++ enumerations is that they do not define a lexical scope. Enumerators appear in the same lexical scope as the enumeration itself. It is initialized with a name (optional) and a collection of enumerators.
Next up, it is time to represent a built-in type using BasicType class. This class checks that the name provided in indeed just a built-in type and nothing else, otherwise, its constructor throws a std::invalid_argument exception. A Union encapsulates a union in code and provides access to its member variables, functions and operators. These additional attributes are only present if a name was given to Union’s constructor because unnamed unions can only have member variables.
To represent parameters in a template declaration, TemplateParameter is used as the base class. Three different classes derive from it to capture three different ways of declaring parameters in a template. They are NonTypeParameter to represent a built-in type, TypedParameter, to represent an arbitrary type and TemplateTemplateParameter to represent a template template parameter in the declaration. NonTypeParameter has one additional method IsIntegral for the fact that a non-type parameter can be an integral type or a pointer/reference to floating point types.
The big kahuna for types is the UserDefineType. This represents either a class or structure in code. A number of members are presents: member functions; member operators; member typedefinitions; member enumerations; member variables; member types; constructors; and an optional destructor. Except for the destructor, all these members are exposed as homogenous collections that can have any number of items in them. Additionally, a UserDefinedType can have any number of bases and templates parameters. Base classes are specified using BaseTypes member function which returns a collection. Similarly, template parameters are accessed through TemplateParameters which returns a TemplateParameterCollection. If the user-defined type has some template parameters, it is also possible to partially or completely specialize it on those parameters. Some utility functions let you investigate whether the type is abstract; has template parameters; is specialized if it has template parameters; has its constructors declared private, in other words is sealed.
Now, let me show you the flip side of the coin, the process of instantiating a user-defined type. If it has template parameter, you have to supply template arguments to it during instantiation. This taken care of by instantiate and its overload which takes a StringCollection& as an argument. Also, you need fully elaborated name of a user-defined type if you define any member function out of class. For this purpose, UserDefinedType has writeelaboratedname in its public interface.
A member user-defined type needs some additional processing before it can be written, such as: if it is being written out of class, it needs to include <containing class elaborated name>::<member type name> before its definition; if the containing class is one of the bases, member user-defined type must be defined at the same lexical scope as the containing class; if both classes have template parameters, they cannot be merged but must be written in two parts. This means that the public interface of MemberType is same as that of UserDefinedType, but the internals are very different. There is a lot more going on behind the scenes than what appear in front of the veil.
Finally, two more classes worth mentioning are the FunctionPtr and the MemFun to represents pointer to a function and a member function respectively. Note these classes are going revision. their interface and representation are still not decided. There is also a class PointerToMember but it is so incomplete there is no point further discussing it.