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.
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 {};
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.
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.
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).
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;
};
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
.
type
returns the C++ type computed by this metafunction
value_type
returns the C++ type computed by this metafunction
value
returns a bool
value true
or false
that we can use in run-time decisions
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
).
Post questions about MicroStation programming to the MicroStation Programming Forum.