...one of the most highly
regarded and expertly designed C++ library projects in the
world. — Herb Sutter and Andrei
An identifier in VMD is either of two lower-level preprocessor possibilities:
Here are some examples:
SOME_NAME _SOME_NAME SOME_123_NAME some_123_name sOMe_123_NAmE 2367 43e11 0 22 654792 0x1256
One of the difficulties with identifiers in preprocessor metaprogramming is safely testing for a particular one. VMD has a means of doing this within a particular constraint for the characters that serve as the input.
The constraint is that the beginning input character, ignoring any whitespace, passed as the input to test must be either:
and if the first character is not the left parenthesis of a tuple the remaining characters must be alphanumeric or an underscore until a space character or end of input occurs.
If this is not the case the behavior is undefined, and most likely a preprocessing error will occur.
Given the input:
's_anything' : can be tested 'S_anything' : can be tested 's_anYthiNg' : can be tested '_anything' : can be tested '_Anything' : can be tested '_anytHIng' : can be tested '24' : can be tested '245e2' : can be tested '(anything)' : can be tested, tuple '(anything) anything' : can be tested, tuple and further input 'anything anything' : can be tested, identifier followed by space character '%_anything' : undefined behavior and most likely a preprocessing error due to the constraint '(_anything' : undefined behavior and most likely a preprocessing error due to the constraint, since a single '(' does not form a tuple '44.3' : undefined behavior and most likely a preprocessing error due to the constraint since '.' is not alphanumeric
In VMD the only way an identifier can be identified in preprocessor input is by a process called registration. In order to 'register' an identifier to be recognized by VMD the end-user must create, for every identifier to be recognized, an object-like macro whose form is:
#define BOOST_VMD_REGISTER_identifier (identifier)
where 'identifier' is a particular identifier we wish to identify. This is called in VMD a registration macro.
It is recommended that such registration macros be created in a header file which can be included before the end-user uses the identifier macros of VMD.
If a particular registration macro occurs more than once it is not a preprocessing error, so duplicating a registration macro will not lead to any problems since each registration macro of the same name will have the exact same object-like macro expansion.
Within a given translation unit it could potentially happen that registration macros have been included by header files which a particular end-user of VMD has not created. This should also not lead to particular problems since registration is a process for adding identifiers for any particular translation unit. As we shall see VMD has macros for not only finding any identifier in preprocessor input but for also finding any particular identifier in preprocessor input.
The specific macro used to test for an identifier in VMD is called BOOST_VMD_IS_IDENTIFIER. The macro takes a required parameter of variadic data which is the input against which to test.
When we invoke BOOST_VMD_IS_IDENTIFIER it returns 1 if the input represents any registered identifier, otherwise it returns 0.
As an example:
#include <boost/vmd/is_identifier.hpp> #define BOOST_VMD_REGISTER_yellow (yellow) #define BOOST_VMD_REGISTER_green (green) #define BOOST_VMD_REGISTER_blue (blue) BOOST_VMD_IS_IDENTIFIER(some_input) // returns 1 if 'some_input' is 'yellow','green', or 'blue' BOOST_VMD_IS_IDENTIFIER(some_input) // returns 0 if 'some_input' is 'purple'
Only registered identifiers can be found in VMD as identifiers.
Although registering an identifier allows VMD to recognize the string of characters as a VMD identifier, the ability to detect a particular identifier needs the end-user to define another macro:
where 'identifier' is a particular identifier we wish to detect. This object-like macro expands to no output.
Like the registration macro multiple detection macros of the same identifier in a translation unit does not cause a compiler problem since the exact same object-like macro occurs.
The term for creating this macro is that we have potentially 'pre-detected' the identifier and I will use the term pre-detected as the process of creating the BOOST_VMD_DETECT macro.
The ability to detect that a VMD identifier is a particular identifier is used in VMD macros when data is compared for equality/inequality as well as when we want to match an identifier against a set of other identifiers. These situations will be explained later in the documentation when the particular macro functionality is discussed. If the programmer never uses the functionality which these situations encompass there is no need to use pre-detection for a registered identifier.
The reason that the identifier constraints mentioned above exist is that the technique for parsing identifiers, once it is determined that the input being parsed does not begin with a set of parentheses, uses preprocessor concatenation in its parsing. This technique involves the preprocessor '##' operator to concatenate input, and examine the results of that concatenation.
When preprocessor concatenation is used the result of the concatenation must be a valid preprocessing token, else the behavior of the preprocessor is undefined. In C++ 'undefined behavior' in general means that anything can happen. In practical use when preprocessor concatenation does not produce a valid preprocessing token, a compiler is most likely to generate a preprocessing error. If the compiler chooses not to issue a preprocessing error the outcome will always be correct and mean that parsing an identifier will fail. But because the outcome is undefined behavior there is no absolute way that the programmer can determine what the outcome will be when preprocessor concatenation is used and the input being parsed contains preprocessor input which does not meet the constraints for parsing an identifier mentioned at the beginning of this topic.
In this documentation I will be using the abbreviation 'UB' as the shortened form of 'undefined behavior' to denote the particular occurrence where VMD attempts to parse preprocessor input using preprocessor concatenation and undefined behavior will occur.
To use the BOOST_VMD_IS_IDENTIFIER macro either include the general header:
or include the specific header: