...one of the most highly
regarded and expertly designed C++ library projects in the
world. — Herb Sutter and Andrei
The VMD library provides the ability to create a macro which takes different types of parameters and can therefore generate different output depending on the parameter types as well as their values.
This is equivalent to the way that overloaded functions provide the ability for a singularly named function to provide different functionality depending on the parameter types.
In the case of macros, where more than one macro of the same name but different macro expansion is not allowed, a single macro name can create different expansions.
As a simple example:
#include <boost/preprocessor/control/iif.hpp> #include <boost/vmd/is_seq.hpp> #include <boost/vmd/is_tuple.hpp> #define AMACRO(param) \ BOOST_PP_IIF \ ( \ BOOST_VMD_IS_SEQ(param), \ Seq, \ BOOST_PP_IIF \ ( \ BOOST_VMD_IS_TUPLE(param), \ Tuple, \ Unknown \ ) \ )
If the param passed is a seq the output of the macro is 'Seq'. If the param passed is a tuple the output of the macro is 'Tuple'. Otherwise the output of the macro is 'Unknown'.
Obviously much more complicated cases can be created in which the types and values of various parameters are parsed in order to produce variable macro output depending on the input. Using variadic macros, macros with variable numbers and types of arguments give the macro programmer even greater freedom to design macros with flexibility.
Another feature of the VMD library is the ability to parse identifiers. VMD can recognize general identifiers as well as recognize specific identifiers, which have been registered. Once an identifier is registered VMD can recognize it as part of VMD sequences. Furthermore VMD can compare identifiers for equality or inequality once a specific identifier has been pre-detected using VMD's system for pre-detecting identifiers.
As another simple example:
#include <boost/preprocessor/control/iif.hpp> #include <boost/vmd/is_general_identifier.hpp> #define AMACRO1(param) \ BOOST_PP_IIF \ ( \ BOOST_VMD_IS_GENERAL_IDENTIFIER(param), \ AMACRO1_IDENTIFIER, \ AMACRO1_NO_IDENTIFIER \ ) \ (param) #define AMACRO1_IDENTIFIER(param) AMACRO1_ ## param #define AMACRO1_NO_IDENTIFIER(param) Parameter is not an identifier #define AMACRO1_NAME Identifier is a NAME #define AMACRO1_ADDRESS Identifier is an ADDRESS
Here we use VMD's ability to recoghnize general identifiers to determine and handle a particular identifier we may be expecting as a macro parameter. If the input to 'AMACRO1' is 'NAME' the output is 'Identifier is a NAME'. If the input to 'AMACRO1' is 'ADDRESS' the output is 'Identifier is an ADDRESS'. Otherwise the output is 'Parameter is not an identifier'.
Identifier registration and pre-detection makes things clearer, allowing us to detect within VMD whether macro input matches a particular identifier. Using the same setup as our previous example, but with identifier registration and pre-detection:
#include <boost/preprocessor/control/iif.hpp> #include <boost/vmd/is_identifier.hpp> #define BOOST_VMD_REGISTER_NAME (NAME) #define BOOST_VMD_DETECT_NAME_NAME #define BOOST_VMD_REGISTER_ADDRESS (ADDRESS) #define BOOST_VMD_DETECT_ADDRESS_ADDRESS #define AMACRO2(param) \ BOOST_PP_IIF \ ( \ BOOST_VMD_IS_IDENTIFIER(param,NAME), \ AMACRO2_NAME, \ BOOST_PP_IIF \ ( \ BOOST_VMD_IS_IDENTIFIER(param,ADDRESS), \ AMACRO2_ADDRESS, \ AMACRO2_NO_IDENTIFIER \ ) \ ) \ (param) #define AMACRO2_NO_IDENTIFIER(param) Parameter is not a NAME or ADDRESS identifier #define AMACRO2_NAME(param) Identifier is a NAME #define AMACRO2_ADDRESS(param) Identifier is an ADDRESS
If the input to 'AMACRO2' is 'NAME' the output is 'Identifier is a NAME'. If the input to 'AMACRO2' is 'ADDRESS' the output is 'Identifier is an ADDRESS'. Otherwise the output is 'Parameter is not a NAME or ADDRESS identifier'.
The VMD library also has 2 different subtypes of identifiers which can always be recognized. The first are numbers, equivalent to the number in Boost PP, numeric values with a range of 0-BOOST_PP_LIMIT_MAG. The second are v-types, which are identifiers starting with BOOST_VMD_TYPE_ followed by a name for the type of data. As an example, the v-type of a Boost PP tuple is BOOST_VMD_TYPE_TUPLE and the v-type of a v-type itself is BOOST_VMD_TYPE_TYPE. All data types have their own v-type identifier; types are recognized by the VMD macros and may be passed as input data just like any other of the types of data VMD recognizes.
The VMD identifier system even has a way, to be explained later, for the end-user to create his own subtype identifiers.
Another reason to use VMD is that VMD understands 'sequences' of the VMD data types. You can have a sequence of data types and VMD can convert the sequence to any of the Boost PP data types, or access any individual data type in a sequence. Idewntifiers in VMD sequences must be registered to be recognized.
#include <boost/vmd/elem.hpp> #include <boost/vmd/to_tuple.hpp> #define BOOST_VMD_REGISTER_NAME (NAME) #define ASEQUENCE (1,2) NAME 147 BOOST_VMD_TYPE_NUMBER (a)(b) BOOST_VMD_TO_TUPLE(ASEQUENCE) BOOST_VMD_ELEM(2,ASEQUENCE)
Our first expansion
BOOST_VMD_TO_TUPLE(ASEQUENCE) returns the tuple:
Our second expansion
returns the sequence element:
Sequences give the macro programmer the ability to accept input data from the user which may more closely mimic C++ constructs.
Another reason to use VMD is that VMD understands data types. Besides specifically asking if a particular input is a particular data type, you can use the macro BOOST_VMD_GET_TYPE to retrieve the type of any VMD data.
#include <boost/vmd/get_type.hpp> BOOST_VMD_GET_TYPE((1,2)) // expands to BOOST_VMD_TYPE_TUPLE BOOST_VMD_GET_TYPE(235) // expands to BOOST_VMD_TYPE_NUMBER
There is still much more of VMD functionality but hopefully this brief introduction of what VMD can do will interest you so that you will read on to understand VMD's functionality for the macro programmer.