mirror of
https://github.com/boostorg/preprocessor.git
synced 2025-07-29 11:47:14 +02:00
Completed the emptiness topic.
This commit is contained in:
@ -8,80 +8,135 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h4>Variadic Macros</h4>
|
||||
<div> Variadic macros are supported by a number of compilers. They are
|
||||
macros of the form: </div>
|
||||
<h4>Passing nothing</h4>
|
||||
<div>
|
||||
Although rarely desirable it has always been legal in C++ to pass nothing, aka no preprocessor tokens,
|
||||
as an argument when invoking a macro, whether the equivalent parameter be a regular parameter or a variadic one.
|
||||
</div>
|
||||
<div class="code">
|
||||
<pre>#define SOME_MACRO(ZeroOrMoreParameters,...) macro expansion possible specifying __VA_ARGS__</pre>
|
||||
<pre>
|
||||
#define SOME_MACRO(Parameter1,Parameter2) macro expansion using Parameter1 and Parameter2
|
||||
#define SOME_VARIADIC_MACRO(Parameter1,...) macro expansion using Parameter1 and __VA_ARGS__
|
||||
|
||||
SOME_MACRO(a,b) // Normal
|
||||
SOME_MACRO(a,) // Legal, second argument is empty
|
||||
SOME_MACRO(,b) // Legal, first argument is empty
|
||||
SOME_MACRO(a) // Preprocessor error, passing the wrong number of arguments
|
||||
|
||||
SOME_VARIADIC_MACRO(a,b,c,d) // Normal
|
||||
SOME_VARIADIC_MACRO(a,) // Legal, variadic argument is empty
|
||||
SOME_VARIADIC_MACRO(,b,c,d) // Legal, first argument is empty
|
||||
SOME_VARIADIC_MACRO(a) /* Preprocessor error in standard below C++20 level,
|
||||
but in C++20 exactly equivalent to SOME_VARIADIC_MACRO(a,) */</pre>
|
||||
</div>
|
||||
<div> The '...' in the parameter list represents the variadic data when the
|
||||
macro is invoked and the __VA_ARGS__ in the expansion represents the
|
||||
variadic data in the expansion of the macro. Variadic data is of the form
|
||||
of 1 or more preprocessor tokens separated by commas.<br>
|
||||
<br>
|
||||
The '...' must be the last parameter in the macro definition and there may
|
||||
be 0 or more non-variadic parameters preceding it.<br>
|
||||
<br>
|
||||
In the expansion of the macro __VA_ARGS__ may be specified 0 or more times
|
||||
to represent the variadic data. The variadic data in the expansion is a
|
||||
comma separated list of preprocessor tokens representing the variadic data
|
||||
which the invoker of the macro enters as the last arguments to the macro.<br>
|
||||
<h4>Expanding to nothing</h4>
|
||||
<div>
|
||||
Given certain arguments a macro might expand to nothing, aka no preprocessor tokens. This may
|
||||
happen more than the previous case of an argument to a macro being nothing because the expansion of a macro
|
||||
is often used to initialize some C++ construct, and C++ has some places where
|
||||
a part of a compile-time construct can be empty. However a macro which expands to nothing
|
||||
rarely occurs when a macro's expansion is used as an argument to another macro, although
|
||||
again it is perfectly legal C++.
|
||||
</div>
|
||||
<h4>Example<u> - Creating and invoking a variadic macro.</u></h4>
|
||||
<div class="code">
|
||||
<pre>#define INITIALIZE_INT_ARRAY(array_name,...) \ <br> static int array_name[] = { __VA_ARGS__ }; \ <br> /**/<br><br> INITIALIZE_INT_ARRAY(myname,45,789,33510,9346,2)</pre>
|
||||
<pre>
|
||||
#define ANOTHER_MACRO(Parameter1,Parameter2) /* expands to nothing when Parameter1 and Parameter2
|
||||
are numbers, otherwise expands to some preprocessing
|
||||
token, such as '1' */
|
||||
|
||||
int another_int = { ANOTHER_MACRO(x,y) }; // ANOTHER_MACRO Expands to 1
|
||||
int some_int = { ANOTHER_MACRO(1,2) }; // ANOTHER_MACRO Expands to nothing
|
||||
SOME_MACRO(ANOTHER_MACRO(x,y),z) // Normal, ANOTHER_MACRO Expands to 1
|
||||
SOME_MACRO(ANOTHER_MACRO(1,2),z) // Legal, first argument is empty as ANOTHER_MACRO Expands to nothing</pre>
|
||||
</div>
|
||||
<u> <span style="font-weight: bold;">Preprocessor
|
||||
Library Support<br>
|
||||
</span></u>
|
||||
<div>The library offers support for variadic macros for those
|
||||
compilers
|
||||
which support the feature. The library can automatically detect whether
|
||||
a compiler supports variadic macros and sets the macro
|
||||
BOOST_PP_VARIADICS accordingly to 1 if the compiler supports variadic
|
||||
macros or 0 if the compiler does not support variadic macros.<br>
|
||||
<br>
|
||||
The end-user can #define BOOST_PP_VARIADICS to 1 or 0 himself in a
|
||||
translation unit, before including any preprocessor header files, to
|
||||
prevent the library from attempting to detect whether the compiler
|
||||
supports variadic macros. This has the effect of manually turning on or
|
||||
off variadic macro support in the library. Of course if one manually
|
||||
turns on variadic macro support in the library, and one's compiler does
|
||||
not support variadic macros, functionality in the library which uses
|
||||
variadic macros will fail with error messages from the compiler.<br>
|
||||
<br>
|
||||
When BOOST_PP_VARIADICS is 1, the library offers some extended
|
||||
functionality
|
||||
by using variadic macros, and also offers extended support for working
|
||||
with variadic data.<br><br>
|
||||
<a name="vmvcquirk"></a>Visual C++ has a
|
||||
few quirks related to variadic macros which require the end-user to code
|
||||
slightly differently. When BOOST_PP_VARIADICS is 1 and Visual C++ is the
|
||||
compiler BOOST_PP_VARIADICS_MSVC is 1, else when BOOST_PP_VARIADICS is 1
|
||||
and Visual C++ is not the compiler BOOST_PP_VARIADICS_MSVC is 0. When
|
||||
BOOST_PP_VARIADICS is 0 then BOOST_PP_VARIADICS_MSVC is not defined.
|
||||
In this way the end-user, when using variadic macros, can test for the
|
||||
presence of Visual C++ as the compiler and code accordingly.<br>
|
||||
<br>
|
||||
Support for working with variadic
|
||||
data is largely centered on being able to convert variadic data to
|
||||
other library data types, since the
|
||||
functionality for working with those Boost preprocessor library data
|
||||
types is much greater than that for working with variadic data directly.<br>
|
||||
<h4>Emptiness defined</h4>
|
||||
<div>
|
||||
Passing nothing as a macro argument or a macro expanding to nothing I term as 'emptiness',
|
||||
as 'nothing' is too amorphous a term which can be used in too many other contexts for my liking.
|
||||
In the vast majority of cases when designing a macro for use emptiness is not a part of such
|
||||
a design, and passing emptiness as an argument or expanding to emptiness is not anything
|
||||
that someone writing a macro takes into account when he explains to other programmers
|
||||
how a macro should be used.<br><br>
|
||||
Other than the fact that macros are generally created so that some actual preprocessor
|
||||
data of a particular kind needs to be passed as arguments or gets generated as part of
|
||||
macro expansion when a macro is invoked, there is another very good reason why working
|
||||
with emptiness is not part of a macro's design: there has been no perfectly fail-safe
|
||||
way to test for emptiness during macro expansion, whether it be in creating macros
|
||||
using just the facilities of the C++ standard or using a 3rd party library, such as
|
||||
this Boost preprocessor library. When I say 'fail-safe' I mean that there has always
|
||||
been some argument input, no matter how small the number of potential cases, where a
|
||||
macro designed to test whether or not the preprocessor data passed to it as an argument
|
||||
when the macro is invoked is actually empty, fails in some
|
||||
way, with the failure normally occurring as a preprocessor error.<br><br>
|
||||
Of course this does not mean that the best macro designed to test for emptiness
|
||||
will not work correctly the vast majority of the time. It only means that there
|
||||
has been no guarantee that such a macro will work correctly all 100% of the time.
|
||||
Nonetheless there have been uses of testing for emptiness, when a macro documents
|
||||
what a particular argument should generally consist of, even if the test is not
|
||||
guaranteed to work 100% of the time if particular unexpected argument data does get passed.
|
||||
</div>
|
||||
<h4>A C++20 solution for testing for emptiness</h4>
|
||||
<div>
|
||||
The C++ standard committee recognized, in the upcoming specification for the C++20 standard,
|
||||
that a way of testing whether variadic data is empty or not in the expansion of a variadic
|
||||
macro would be very useful when designing certain types of macros. Because of this
|
||||
the C++20 standard added a preprocessor construct which could do this in a certain way
|
||||
for variadic data in the expansion of a variadic macro.
|
||||
The construct is called __VA_OPT__. I am not going to explain that construct here, as the
|
||||
documentation for it is part of the upcoming C++20 standard and is readily available on
|
||||
various C++ sites on the Internet, but the further upshot of adding the __VA_OPT__ construct
|
||||
to C++20 is that it is now possible to create a variadic macro which is 100% reliable in
|
||||
testing for emptiness whenever a compiler supports the __VA_OPT__ construct in its
|
||||
compilation of preprocessor code.<br><br>
|
||||
For such a macro to always work which tests for emptiness the code must know when
|
||||
the __VA_OPT__ construct is available. It is not enough to know that a compiler is
|
||||
working at the C++20 level, since as all C++ programmers know an adherence to a C++ standard
|
||||
level never guarantees that a particular compiler supports every aspect of that level.
|
||||
Happily there is a way to test whether a compiler supports the __VA_OPT__ construct
|
||||
as long as the compiler supports variadic macros, and that way has been openly published
|
||||
on the Internet although the actual macro code would not have been hard to create even if it had
|
||||
not publicly appeared. This library uses that code to test for __VA_OPT__ as a necessary prelude
|
||||
for creating a variadic macro which is 100% reliable in testing for emptiness.<br><br>
|
||||
The Boost Preprocessor macro for testing whether the __VA_OPT__ construct is supported during
|
||||
compilation is called BOOST_PP_VARIADIC_OPT, which is a function-like macro taking no parameters
|
||||
and returning 1 if the __VA_OPT__ construct is supported and 0 if it is not. The macro
|
||||
only returns 1 when variadic macros are supported, when the compiler is at the C++20 level,
|
||||
and when the __VA_OPT__ construct can be used according to the C++20 standard. In particular
|
||||
the macro needs the compiler to be working at the C++20 level despite the fact that at least
|
||||
one major compiler supports the __VA_OPT__ construct in some of its latest releases even
|
||||
when the compiler is being used at a C++ standard level below that of C++20. The reason this
|
||||
Boost preprocessor library requires the C++20 level is because that same major compiler
|
||||
can produce a warning, or even an error, when it even sees a macro using the __VA_OPT__
|
||||
construct at a level below C++20, even though it supports it, if other compiler options
|
||||
requiring strict adherence to the level of the C++ standard being used are passed on the
|
||||
command line. So taking a conservative approach the BOOST_PP_VARIADIC_OPT macros requires
|
||||
compilation at the C++20 level, along with variadic macro support, along with the testing code
|
||||
expanding to 1, in order to specify that __VA_OPT__ is supported.<br><br>
|
||||
The actual Boost Preprocessor library for testing for emptiness in C++20 mode is called
|
||||
BOOST_PP_CHECK_EMPTY. The macro is a variadic macro with a single variadic parameter. The
|
||||
macro only exists if our previous macro for testing for __VA_OPT__ called BOOST_PP_VARIADIC_OPT
|
||||
expands to 1 when invoked as BOOST_PP_VARIADIC_OPT(). If BOOST_PP_VARIADIC_OPT() expands to 0
|
||||
the BOOST_PP_CHECK_EMPTY macro does not exist at all in this library. The input to the macro
|
||||
can be any variadic data. If the data passed to the macro is empty, or if the data passed to
|
||||
the macro is not empty but when the data itself is expanded it is empty, the macro returns
|
||||
1, otherwise it returns 0. The macro works 100% of the time and is completely reliable no matter what
|
||||
preprocessor data is passed to it. But of course it only works when compiling at the C++20
|
||||
level with the __VA_OPT__ construct supported by the compiler. It solves an old problem
|
||||
that it has never been possible, prior to C++20, to provide a 100% reliable implementation
|
||||
of a macro which tests for emptiness in C++.<br><br>
|
||||
Eventually more C++ compilers will support C++20 and the __VA_OPT__ construct and more programmers
|
||||
will use compilers at the C++20 level. At that point the macro BOOST_PP_CHECK_EMPTY can be
|
||||
used reliably for testing emptiness in preprocessor data in macro code by all those programmers.
|
||||
This does not mean that designing macros with emptiness in mind needs to be done, much less
|
||||
considered, but that the possibility of doing so with complete reliability will be there if needed
|
||||
by the macro programmer. Along with the __VA_OPT__ construct as mandated by the C++20 standard
|
||||
the BOOST_PP_CHECK_EMPTY adds two more tools in the arsenal of macro programming, which is a good
|
||||
thing, while programmers who wanted to ignore any dealing with emptiness in macro code can continue to do so.
|
||||
</div>
|
||||
<b>See</b> <b>Also</b><br>
|
||||
<ul>
|
||||
<li><a href="../ref/variadics.html">BOOST_PP_VARIADICS</a></li>
|
||||
<li><a href="../headers/tuple.html">Tuple Macros</a><br>
|
||||
</li>
|
||||
<li><a href="../headers/variadic.html">Variadic
|
||||
Macros<br>
|
||||
</a></li>
|
||||
<li><a href="../ref/array_enum.html">BOOST_PP_ARRAY_ENUM</a></li>
|
||||
<li><a href="../ref/list_enum_r.html">BOOST_PP_LIST_ENUM</a></li>
|
||||
<li><a href="../ref/seq_enum.html">BOOST_PP_SEQ_ENUM</a></li>
|
||||
<li><a href="../ref/tuple_enum.html">BOOST_PP_TUPLE_ENUM</a></li>
|
||||
<li><a href="../ref/overload.html">BOOST_PP_OVERLOAD</a></li>
|
||||
<li><a href="../ref/variadic_opt.html">BOOST_PP_VARIADIC_OPT</a></li>
|
||||
<li><a href="../ref/check_empty.html">BOOST_PP_CHECK_EMPTY</a></li>
|
||||
</ul>
|
||||
<hr size="1">
|
||||
<div style="margin-left: 0px;"> <i><EFBFBD> Copyright
|
||||
|
Reference in New Issue
Block a user