...one of the most highly
regarded and expertly designed C++ library projects in the
world. — Herb Sutter and Andrei
Previous sections of this documentation have explained how VMD can be used to parse VMD data types, as well as recognize emptiness.
Another area of functionality of VMD involves useful variadic macros, based on the previous functionality, which expands on similar macros already in Boost PP. These variadic macros can be divided into sections illustrating these areas of behavior:
Sub-sections for each of these now follow in the documentation.
The VMD macros for identifying data types work best when the macro logic can take different paths depending on the type of data being passed for a macro parameter. But occasionally the preprocessor metaprogrammer wants to simply verify that the macro parameter data is of the correct data type, else a preprocessing error should be generated to notify the programmer invoking the macro that the data passed is the incorrect type.
The Boost PP library has a macro which produces a preprocessing error when the condition passed to it is 0. This macro is called BOOST_PP_ASSERT. The macro produces a preprocessor error by forcing a call to an internal macro with the wrong number of arguments. According to the C++ standard this should always cause an immediate preprocessing error for conforming compilers.
Unfortunately VC++ with its default preprocessor will only produce a warning when the wrong number of arguments are passed to a macro. Therefore the BOOST_PP_ASSERT macro does not produce a preprocessing error using VC++ with its default preprocessor. Amazingly enough there appears to be no other way in which VC++ with its default preprocessor can be forced to issue a preprocessing error by invoking a macro ( if you find one please tell me about it ). However one can create invalid C++ as the output from a macro invocation which causes VC++ to produce a compiler error when the VC++ compiler later encounters the construct.
This is what the macro BOOST_VMD_ASSERT does. It takes the same conditional argument as BOOST_PP_ASSERT and it calls BOOST_PP_ASSERT when not used with VC++ with its default preprocessor, otherwise if the condition is 0 it generates a compiler error by generating invalid C++ when used with VC++ with its default preprocessor. The compiler error is generated by producing invalid C++ whose form is:
typedef char BOOST_VMD_ASSERT_ERROR[-1];
By passing a second optional argument, whose form is a preprocessing identifier, to BOOST_VMD_ASSERT you can generate the invalid C++ for VC++ with its default preprocessor, if the first argument is 0, of the form:
typedef char optional_argument[-1];
instead. This may give a little more clarity, if desired, to the C++ error generated.
If the first conditional argument is not 0, BOOST_VMD_ASSERT produces no output.
To use the BOOST_VMD_ASSERT macro either include the general header:
or include the specific header:
The data types have their own assertion macros. These are largely just shortcuts for passing the result of the identifying macros to BOOST_VMD_ASSERT. These assertion macros are:
Each of these macros take as parameters the exact same argument as their corresponding identifying macros. But instead of returning non-zero or 0, each of these macros produce a compiler error if the type of the input is not correct.
Each of these macros only check for its assertion when the macro BOOST_VMD_ASSERT_DATA is set to 1. By default BOOST_VMD_ASSERT_DATA is only set to 1 in compiler debug mode. The programmer can manually set BOOST_VMD_ASSERT_DATA to 1 prior to using one the data types assert macros if he wishes.
To use the individual BOOST_VMD_ASSERT_... macros either include the general header:
or include the specific header:
#include <boost/vmd/assert_is_empty.hpp> // BOOST_VMD_ASSERT_IS_EMPTY #include <boost/vmd/assert_is_identifier.hpp> // BOOST_VMD_ASSERT_IS_IDENTIFIER #include <boost/vmd/assert_is_number.hpp> // BOOST_VMD_ASSERT_IS_NUMBER #include <boost/vmd/assert_is_array.hpp> // BOOST_VMD_ASSERT_IS_ARRAY #include <boost/vmd/assert_is_list.hpp> // BOOST_VMD_ASSERT_IS_LIST #include <boost/vmd/assert_is_seq.hpp> // BOOST_VMD_ASSERT_IS_SEQ #include <boost/vmd/assert_is_tuple.hpp> // BOOST_VMD_ASSERT_IS_TUPLE #include <boost/vmd/assert_is_type.hpp> // BOOST_VMD_ASSERT_IS_TYPE
The VC++ compiler with its default preprocessor has a quirk when dealing with BOOST_VMD_ASSERT and the data type assert macros. If you invoke one of the assert macros within another macro which would normally generate output preprocessor tokens, it is necessary when using VC++ with its default preprocessor to concatenate the result of the assert macro to whatever other preprocessor data is being generated, even if the assert macro does not generate an error.
As a simple example let us suppose we have a macro expecting a tuple and generating 1 if the tuple has more than 2 elements, otherwise it generates 0. Ordinarily we could write:
#include <boost/preprocessor/comparison/greater.hpp> #include <boost/preprocessor/control/iif.hpp> #include <boost/preprocessor/tuple/size.hpp> #include <boost/vmd/assert_is_tuple.hpp> #define AMACRO(atuple) \ BOOST_VMD_ASSERT_IS_TUPLE(atuple) \ BOOST_PP_IIF(BOOST_PP_GREATER(BOOST_PP_TUPLE_SIZE(atuple), 2),1,0)
but for VC++ with its default preprocessor we must write
#include <boost/preprocessor/cat.hpp> #include <boost/preprocessor/comparison/greater.hpp> #include <boost/preprocessor/control/iif.hpp> #include <boost/preprocessor/tuple/size.hpp> #include <boost/vmd/assert_is_tuple.hpp> #define AMACRO(atuple) \ BOOST_PP_CAT \ ( \ BOOST_VMD_ASSERT_IS_TUPLE(atuple), \ BOOST_PP_IIF(BOOST_PP_GREATER(BOOST_PP_TUPLE_SIZE(atuple), 2),1,0) \ )
VC++ with its default preprocessor does not work correctly in the first instance, erroneously getting confused as far as compiler output is concerned. But by using BOOST_PP_CAT in the second condition VC++ with its default preprocessor will work correctly with VMD assertions.