C++ Type Lists

Template Meta Programming is introduced in the first of these articles. The Meta Programming Language (MPL) evolved to handle those concepts as is implemented in boost.mpl. It helps us to create order out of disorder.

Boost MPL: type lists

C++ 11

C++ 11 has changed the playing field. The addition of variadic templates with their associated parameter packs added a compile-time list of types structure directly into the language. With C++ 11, type lists are as easy as …

// C++11
template<class... T> struct type_list {};

Integer Type Definitions

C++ provides strong typing: int, double, wchar_t. You already know about those and use them in your code. C++ lets us define new types: if you're writing application code for MicroStation then you've almost certainly used DPoint3d, defined as a struct in one of the MDL header files. Those header files are delivered wih the MicroStation Software Developer Kit (SDK).

What you may find astonishing is that, using a template class, you can create new types from integer values. boost::mpl::int_ provides that remarkable conversion. Use it like this …

typedef boost::mpl::int_<8> eight;
ASSERT (eight::value == 8);

What can I do with that integral constant wrapper, you ask? Well, you can now ask questions about types at compile time, before you start to ask questions about values at run time.

The C++ type created by that template class is unique for each integer value. That is, int_<8> is a different type to int_<9>.

If you had the appropriate decision-making tools, you could modify your program at compile-time based on type analysis. Template metaprogramming provides the toolset to perform that analysis.

MicroStation DGN Element Types

If you've done any MDL programming, you're probably familiar with the header file mselems.h delivered with the MicroStation SDK. Among other things, that header introduces a list of element types. Those used to be macros (e.g. #define  CELL_HEADER_ELM  2), but have been refined for MicroStation V8i to enum MSElementTypes.

Here's an abbreviated extract from that enumeration …

enum MSElementTypes
    {
    CELL_LIB_ELM                    = 1,
    CELL_HEADER_ELM                 = 2,
    LINE_ELM                        = 3,
    LINE_STRING_ELM                 = 4,
     … 
    REFERENCE_OVERRIDE_ELM          = 108,
    NAMED_GROUP_HDR_ELM             = 110,
    NAMED_GROUP_COMPONENT_ELM       = 111,
    };

We can use MPL to create a set of element types derived from the values in enum MSElementTypes. For example …

typedef boost::mpl::int_<CELL_HEADER_ELM>               CellType;
typedef boost::mpl::int_<LINE_STRING_ELM>               LineStringType;

We've created a new C++ type named CellType and another called LineStringType. CellType and LineStringType are distinct C++ types. You can extract the integer value from each type using the meta programming ::value operator …

// Compare integer values
ASSERT (CellType::value == CELL_HEADER_ELM);
ASSERT (LineStringType::value == LINE_STRING_ELM);

We can create an element type definition for each MicroStation element in that enumeration. Now we can make compile-time decisions based on element type. We've written a header file you can include in your code to try this out.

Element Type Lists

Abstracted Types

Template metaprogramming lets us assemble lists of types. Just as you might build a list of integer values at run-time, you can create a list of types at compile-time. Here's an example from the MPL book …

typedef boost::mpl::vector <char,short,int,long,float,double> types;

mpl::vector is a class whose purpose is to store a list of six types. There are MPL functions that let you examine that list, search for a type, insert a type and eliminate a type. For example, here's how to find the position of the long type in that list …

typedef boost::mpl::find <types, long>::type long_pos;

That metafunction finds the first member of the list that is identical to long. If no matching type exists, mpl::find returns the sequence past-the-end iterator. You can get that value using the mpl::end metafunction …

typedef boost::mpl::end <types>::type finish;

Our header file creates some MDL element type lists. We use them to collect the types of elements that share common traits. For example, element type list AreaTypes includes those types of element that have the Area and Perimeter traits. You would expect an element having those traits to be quantifiable by its area and perimeter …

typedef boost::mpl::vector <ShapeType,
                            ComplexShapeType,
                            EllipseType>           AreaTypes;

As you can see, we have decided that MicroStation shape element, complex shape element and ellipse element share the traits AreaTypes in having area and perimeter. We'll show you later, in another article, how you can use those traits to design classes that have compile-time polymorphism (i.e. an EllipseElm class that can tell you its area but not its text content, and a TextElm class that can tell you its text content but not its area).

Analysing Element Type Lists

We may want to analyse an element type list to see if contains a particular element type. For example, does list AreaTypes include a shape element?

Using the MPL it's straightforward to find an element type …

typedef typename boost::mpl::find<ElemTypeList, ElemType>::type type_pos;

The result of applying that function is type_pos. But, what is type_pos? The answer to that question is: type_pos is the C++ type that results from a search through the MPL list. As a type, all we can do is compare it to another type. Here's a metafunction IsTypeInList that does exactly that …

//	Metafunction implemented by template class IsTypeInList
template <typename ElemTypeList, typename ElemType>
struct IsTypeInList
{
	typedef typename boost::mpl::find   <ElemTypeList, ElemType >::type type_pos;
	typedef typename boost::mpl::end    <ElemTypeList >::type finish;
	typedef typename boost::mpl::not_   <boost::is_same <type_pos, finish>  >::type type;
	typedef typename type::value_type value_type;
	static const bool value = type::value;
};

MPL Conventions

We observe several conventions of the Boost MPL in that metafunction. When we observe those conventions, our metafunctions are useable by other metafunctions. To meet those conventions, we supply some type definitions that match the MPL expectations: type and value_type. Because, as you'll see later, we want to use the result of our metafunctions as a boolean operator, we provide a value whose type, in this example, is a C++ bool.

Element Types Header File

ElementTypes.h creates a unique C++ type for each element typed defined in the MDL enumeration enum MSElementTypes in file mselems.h. If you're a MicroStation developer, you already have that header file delivered with the MicroStation SDK. You'll find it in the  …\MicroStation\mdl\include folder.

You can download ElementTypes.h. We've included another header file that you may find useful (UndefineMdlMacrosThatInterfereWithBoost.h).


Related Articles about Meta Programming

Index
MPL: type lists Type Lists and the MPL
Object Inheritance Conditional Inheritance and the MPL
Polymorphic Element Classes Polymorphic Classes for MicroStation Elements
ElementPolymorphicDynamic.h Dynamic Polymorphic Element header file overview
ElementPolymorphicStatic.h Static Polymorphic Element header file overview
Element Factory Element Factory
Development Tool Versions

Questions

Post questions about MicroStation programming to the MicroStation Programming Forum.