176 lines
7.2 KiB
Plaintext
176 lines
7.2 KiB
Plaintext
[/
|
|
(C) Copyright Edward Diener 2011-2015
|
|
Distributed under the Boost Software License, Version 1.0.
|
|
(See accompanying file LICENSE_1_0.txt or copy at
|
|
http://www.boost.org/LICENSE_1_0.txt).
|
|
]
|
|
|
|
[section:vmd_identifier Identifiers]
|
|
|
|
An identifier in VMD is either of two lower-level preprocessor possibilities:
|
|
|
|
* a preprocessing token 'identifier', which is essentially a sequence
|
|
of alphanumeric characters and the underscore
|
|
character with the first character not being a numeric character.
|
|
* a preprocessing token 'pp-number' that is an integral literal token.
|
|
|
|
Here are some examples:
|
|
|
|
SOME_NAME
|
|
_SOME_NAME
|
|
SOME_123_NAME
|
|
some_123_name
|
|
sOMe_123_NAmE
|
|
2367
|
|
43e11
|
|
0
|
|
22
|
|
654792
|
|
0x1256
|
|
|
|
[heading Problem testing any identifier]
|
|
|
|
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:
|
|
|
|
* an identifier character, ie. an alphanumeric or an underscore
|
|
* the left parenthesis of a tuple
|
|
|
|
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
|
|
|
|
[heading Identifying an identifier]
|
|
|
|
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.
|
|
|
|
[heading Testing for an identifier macro]
|
|
|
|
The specific macro used to test for an identifier in VMD is called BOOST_VMD_IS_IDENTIFIER.
|
|
The macro takes one required parameter 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'
|
|
|
|
Essentially only registered identifiers can be found in VMD as identifiers.
|
|
|
|
[heading Detecting a particular identifier]
|
|
|
|
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:
|
|
|
|
#define BOOST_VMD_DETECT_identifier_identifier
|
|
|
|
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.
|
|
|
|
[heading Parsing identifiers and undefined behavior]
|
|
|
|
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 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.
|
|
|
|
[heading Usage]
|
|
|
|
To use the BOOST_VMD_IS_IDENTIFIER macro either include the general header:
|
|
|
|
#include <boost/vmd/vmd.hpp>
|
|
|
|
or include the specific header:
|
|
|
|
#include <boost/vmd/is_identifier.hpp>
|
|
|
|
[endsect]
|