...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
VMD is able to determine whether or not preprocessing input is a given Boost PP data type. The VMD macros to do this are:
Each of these macros take a single parameter as input and return 1 if the parameter is the appropriate data type and 0 if it is not.
Both an array and a non-empty list are also a tuple. So if one has:
#define ANARRAY (3,(a,b,c)) #define ALIST (a,(b,(c,BOOST_PP_NIL))) #define ATUPLE (a,b,c) #define ASEQ (a)(b)(c)
then
#include <boost/vmd/is_tuple.hpp> BOOST_VMD_IS_TUPLE(ANARRAY) returns 1 BOOST_VMD_IS_TUPLE(ALIST) returns 1 BOOST_VMD_IS_TUPLE(ATUPLE) returns 1 BOOST_VMD_IS_TUPLE(ASEQ) returns 0
A list whose first element is the number 2 and whose second element is not the end-of-list marker BOOST_PP_NIL is also an array. So if one has:
#define ALIST (2,(3,BOOST_PP_NIL)) #define ALIST2 (2,(3,(4,BOOST_PP_NIL))) #define ALIST3 (2,BOOST_PP_NIL) #include <boost/vmd/is_array.hpp> #include <boost/vmd/is_list.hpp> BOOST_VMD_IS_LIST(ALIST) returns 1 BOOST_VMD_IS_LIST(ALIST2) returns 1 BOOST_VMD_IS_LIST(ALIST3) returns 1 BOOST_VMD_IS_ARRAY(ALIST) returns 1 BOOST_VMD_IS_ARRAY(ALIST2) returns 1 BOOST_VMD_IS_ARRAY(ALIST3) returns 0
A single element tuple is also a one element seq. So if one has:
#define ASE_TUPLE (a)
then
#include <boost/vmd/is_seq.hpp> #include <boost/vmd/is_tuple.hpp> BOOST_VMD_IS_TUPLE(ASE_TUPLE) returns 1 BOOST_VMD_IS_SEQ(ASE_TUPLE) returns 1
The form of an array is a two element tuple, where the first element is a number and the second element is a tuple. The number specifies the size of the tuple. Since when using variadic macros it is never necessary to specify the size of a tuple, an array is largely obsolete. However VMD still supports it.
The problem when testing for an array is that if the first element does not obey the constraint on testing for a number, you will get UB.
#include <boost/vmd/is_array.hpp> #include <boost/vmd/is_tuple.hpp> #define A_TUPLE (&anything,(1,2)) BOOST_VMD_IS_ARRAY(A_TUPLE) will give UB due to the constraint BOOST_VMD_IS_TUPLE(A_TUPLE) will return 1
When VMD attempts to parse for an array, as it does when the BOOST_VMD_IS_ARRAY is used, if first looks to see if the syntax represents a tuple with two elements. Next it looks to see if the second element itself is a tuple. Finally if it is satisfied that the previous checks are valid it tests whether the first element is a number or not. It is in this final test, that the first element is a valid number, where the UB could occur as explained in the topic 'Numbers'.
The form of a non-empty list is a two element tuple, where the first element is the head of the list and can be anything and the second element is itself a list or the end-of-list identifier BOOST_PP_NIL.
The problem when testing for a list is that if the second element does not obey the constraint on testing for an identifier, since BOOST_PP_NIL is an identifier and is tested as such, you will get UB.
#include <boost/vmd/is_list.hpp> #include <boost/vmd/is_tuple.hpp> #define A_TUPLE (element,&anything) BOOST_VMD_IS_LIST(A_TUPLE) will give UB due to the constraint BOOST_VMD_IS_TUPLE(A_TUPLE) will return 1
The form of an empty list is the identifier BOOST_PP_NIL. Therefore:
#include <boost/vmd/is_identifier.hpp> #include <boost/vmd/is_list.hpp> #define A_BAD_EMPTY_LIST &BOOST_PP_NIL BOOST_VMD_IS_LIST(A_BAD_EMPTY_LIST) will give UB due to the constraint BOOST_VMD_IS_IDENTIFIER(A_BAD_EMPTY_LIST) will give UB due to the constraint
When VMD attempts to parse for a list, as it does when the BOOST_VMD_IS_LIST is used, if first looks to see if the syntax represents a tuple with two elements. If it is not a tuple with two elements it will check for the end-of-list. If it is a tuple with two elements it looks to see if the second element is a list. In both these paths it must always eventually check for the end-of-list notation BOOST_PP_NIL, which is an identifier in VMD. It is in this final test, that the end-of-list notation exists as a VMD identifier, where the UB could occur as explained in the topic 'Identifiers'.
As has previously been mentioned a single element tuple is also a one element seq.
However, as will be discussed later in the documentation, when VMD has to determine the type of such data, it always returns it as a tuple ( BOOST_VMD_TYPE_TUPLE ).
If our data consists of more than one consecutive tuple of a single element the data is a seq:
#include <boost/vmd/is_seq.hpp> #include <boost/vmd/is_tuple.hpp> #define ST_DATA (somedata)(some_other_data) BOOST_VMD_IS_SEQ(ST_DATA) will return 1 BOOST_VMD_IS_TUPLE(ST_DATA) will return 0
However if the data consists of a mixture we need to distinguish how VMD parses the data. The rule is that VMD always parses a single element tuple as a tuple unless it is followed by one or more single element tuples, in which case it is a seq.
#define ST_DATA (somedata)(element1,element2)
VMD parses the above data as 2 consecutive tuples. The first tuple is the single element tuple '(somedata)' and the second tuple is the multi element tuple '(element1,element2)'.
#define ST_DATA (element1,element2)(somedata)
VMD parses the above data as 2 consecutive tuples. The first tuple is the multi element tuple '(element1,element2)' and the second tuple is the single element tuple '(somedata)'.
#define ST_DATA (somedata)(some_other_data)(element1,element2)
VMD parses the above data as a seq followed by a tuple. The seq is '(somedata)(some_other_data)' and the tuple is '(element1,element2)'.
An array and a list can be empty.
An empty array has the form '(0,())', and is a perfectly valid array.
You can test for an empty array using the macro BOOST_VMD_IS_EMPTY_ARRAY.
#include <boost/vmd/is_array.hpp> #include <boost/vmd/is_empty_array.hpp> #define AN_ARRAY (1,(1)) #define AN_EMPTY_ARRAY (0,()) BOOST_VMD_IS_ARRAY(AN_ARRAY) will return 1 BOOST_VMD_IS_ARRAY(AN_EMPTY_ARRAY) will return 1 BOOST_VMD_IS_EMPTY_ARRAY(AN_EMPTY_ARRAY) will return 1 BOOST_VMD_IS_EMPTY_ARRAY() will return 0 BOOST_VMD_IS_EMPTY_ARRAY(AN_ARRAY) will return 0
An empty list has the form 'BOOST_PP_NIL', and is a perfectly valid list.
You can test for an empty list using the macro BOOST_VMD_IS_EMPTY_LIST.
#include <boost/vmd/is_empty_list.hpp> #include <boost/vmd/is_list.hpp> #define A_LIST (1,BOOST_PP_NIL) #define AN_EMPTY_LIST BOOST_PP_NIL BOOST_VMD_IS_LIST(A_LIST) will return 1 BOOST_VMD_IS_LIST(AN_EMPTY_LIST) will return 1 BOOST_VMD_IS_EMPTY_LIST(AN_EMPTY_LIST) will return 1 BOOST_VMD_IS_EMPTY_LIST() will return 0 BOOST_VMD_IS_EMPTY_LIST(A_LIST) will return 0
Neither seqs or tuples can be empty when using Boost PP. Because of this if you convert from an empty array or list to a seq or tuple using Boost PP macros to do so you will get undefined behavior.
The syntax '()', which is called an empty parenthesis, is neither a zero-element seq or a tuple consisting of no elements. Rather it is either a one-element seq whose content is emptiness or a single-element tuple whose content is emptiness.
VMD supports the syntax of an empty parenthesis. You can test for it using the macro BOOST_VMD_IS_PARENS_EMPTY.
#include <boost/vmd/is_parens_empty.hpp> #include <boost/vmd/is_seq.hpp> #include <boost/vmd/is_tuple.hpp> #define EMPTY_PARENS () #define TUPLE (0) #define SEQ (0)(1) BOOST_VMD_IS_TUPLE(EMPTY_PARENS) will return 1 BOOST_VMD_IS_SEQ(EMPTY_PARENS) will return 1 BOOST_VMD_IS_PARENS_EMPTY(EMPTY_PARENS) will return 1 BOOST_VMD_IS_PARENS_EMPTY() will return 0 BOOST_VMD_IS_PARENS_EMPTY(TUPLE) will return 0 BOOST_VMD_IS_PARENS_EMPTY(SEQ) will return 0
The VC++8 compiler ( Visual Studio 2005 ), which is the oldest VC++ version which VMD supports, has trouble working with the empty parenthesis syntax. Therefore if you have to use VC++8 avoid its use, otherwise you should be fine using it if you desire.
When using variadic macros, the fact that an array can be empty is its only advantage over a tuple. Otherwise using a tuple is always easier since the syntax is simpler; you never have to notate the tuple's size.
Since VMD fully supports passing and returning emptiness you could use a tuple instead of an array in all situations and simply pass or return emptiness to represent an "empty" tuple, and as an equivalent to an empty array.
This notion of using emptiness to represent an "empty" tuple can also be extended to using emptiness to represent an "empty" seq. However functionality in Boost PP will not recognize emptiness as an empty tuple or seq, nor can you work with emptiness to represent an empty tuple or empty seq using the Boost PP functionality for a tuple or a seq. For a solution to using emptiness to represent an "empty" tuple or an "empty" seq VMD has functionality which will be explained when we look at our last area of functionality in VMD, useful variadic macros not in Boost PP.
You can use the general header file:
#include <boost/vmd/vmd.hpp>
or you can use individual header files for each of these macros. The individual header files are:
#include <boost/vmd/is_array.hpp> // for the BOOST_VMD_IS_ARRAY macro #include <boost/vmd/is_list.hpp> // for the BOOST_VMD_IS_LIST macro #include <boost/vmd/is_seq.hpp> // for the BOOST_VMD_IS_SEQ macro #include <boost/vmd/is_tuple.hpp> // for the BOOST_VMD_IS_TUPLE macro. #include <boost/vmd/is_empty_array.hpp> // for the BOOST_VMD_IS_EMPTY_ARRAY macro. #include <boost/vmd/is_empty_list.hpp> // for the BOOST_VMD_IS_EMPTY_LIST macro. #include <boost/vmd/is_parens_empty.hpp> // for the BOOST_VMD_IS_PARENS_EMPTY macro.