123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- [/
- (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]
|