From 771edacbffd58eb6a880e09984c78c93fcab5794 Mon Sep 17 00:00:00 2001 From: Edward Diener Date: Tue, 8 Oct 2019 15:50:08 -0400 Subject: [PATCH] Completed the emptiness topic. --- doc/topics/emptiness.html | 187 ++++++++++++++++++++++++-------------- 1 file changed, 121 insertions(+), 66 deletions(-) diff --git a/doc/topics/emptiness.html b/doc/topics/emptiness.html index 404d53a..518da0d 100644 --- a/doc/topics/emptiness.html +++ b/doc/topics/emptiness.html @@ -8,80 +8,135 @@ -

Variadic Macros

-
Variadic macros are supported by a number of compilers. They are - macros of the form:
+

Passing nothing

+
+ 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. +
-
#define SOME_MACRO(ZeroOrMoreParameters,...) macro expansion possible specifying __VA_ARGS__
+
+      #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,) */
-
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.
-
- The '...' must be the last parameter in the macro definition and there may - be 0 or more non-variadic parameters preceding it.
-
- 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.
+

Expanding to nothing

+
+ 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++.
-

Example - Creating and invoking a variadic macro.

-
#define INITIALIZE_INT_ARRAY(array_name,...) \ 
static int array_name[] = { __VA_ARGS__ }; \
/**/

INITIALIZE_INT_ARRAY(myname,45,789,33510,9346,2)
+
+      #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
- Preprocessor - Library Support
-
-
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.
-
- 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.
-
- 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.

- 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.
-
- 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.
+

Emptiness defined

+
+ 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.

+ 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.

+ 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. +
+

A C++20 solution for testing for emptiness

+
+ 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.

+ 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.

+ 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.

+ 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++.

+ 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.
See Also

© Copyright