123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- [/
- (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_test_empty Emptiness]
- [heading Passing empty arguments]
- It is possible to pass an empty argument to a macro.
- The official terminology for this in the C++ standard is an argument
- "consisting of no preprocessing tokens".
- Let us consider a number of cases without worrying too much
- what the macro output represents.
- Consider these two function-like macros:
-
- #define SMACRO() someoutput
- #define EMACRO(x) otheroutput x
-
- The first macro takes no parameters so invoking it must always be done by
-
- SMACRO()
-
- and passing any arguments to it would be invalid.
- The second macro takes a single parameter. it can be evoked as
- EMACRO(somedata)
-
- but it also can be invoked as
- EMACRO()
-
- In the second invocation of EMACRO we are passing an empty argument to the macro.
- Similarly for any macro having 1 or more parameters, an empty argument
- can be validly passed for any of the parameters, as in
- #define MMACRO(x,y,z) x y z
-
- MMACRO(1,,2)
-
- An empty argument is an argument even if we are passing nothing.
- Because an empty argument can be passed for a given parameter of
- a macro does not mean one should do so. Any given macro will specify what each
- argument to a macro should represent, and it is has normally been very rare to encounter
- a macro which specifies that an empty argument can logically be passed for a given
- argument. But from the perspective of standard C++ it is perfectly valid to
- pass an empty argument for a macro parameter.
- The notion of passing empty arguments can be extended to passing empty data which
- "consists of no preprocessing tokens" in slightly more complicated situations.
- It is possible to pass empty data as an argument to a variadic macro in the form of
- variadic macro data, as in
- #define VMACRO(x,...) x __VA_ARGS__
-
- invoked as
- VMACRO(somedata,)
-
- Here one passes empty data as the variadic macro data and it is perfectly valid C++.
- Please notice that this different from
- VMACRO(somedata)
-
- which is not valid C++, prior to C++20, since something must be passed for the variadic argument.
- In C++20 the above invocation is valid and is exactly the same as in our previous example of
- 'VMACRO(somedata,)' where one passes empty data as the variadic macro data. Similarly one could
- invoke the macro as
- VMACRO(somedata,vdata1,,vdata3)
-
- where one is passing variadic macro data but an element in the variadic macro data is empty.
- Furthermore if we are invoking a macro which expects a Boost PP data type, such as
- a tuple, we could also validly pass empty data for all or part of the data in a tuple,
- as in
- #define TMACRO(x,atuple) x atuple
-
- TMACRO(somedata,())
-
- In this case we are passing a 1 element tuple
- where the single element itself is empty.
-
- or
- TMACRO(somedata,(telem1,,telem2,teleem3))
-
- In this case we are passing a 4 element tuple where
- the second element is empty.
-
- Again either invocation is valid C++ but it is not necessarily what the
- designed of the macro has desired, even if in both cases the macro designer
- has specified that the second parameter must be a tuple for the macro
- to work properly.
-
- [heading Returning emptiness]
- Similar to passing empty arguments in various ways to a macro,
- the data which a macro returns ( or 'generates' may be a better term )
- could be empty, in various ways. Again I am not necessarily promoting
- this idea as a common occurrence of macro design but merely pointing it
- out as valid C++ preprocessing.
- #define RMACRO(x,y,z)
-
- RMACRO(data1,data2,data3)
-
- It is perfectly valid C++ to return "nothing" from a macro invocation.
- In fact a number of macros in Boost PP do that based on the preprocessor
- metaprogramming logic of the macro, and are documented as such.
- Similarly one could return nothing as part or all of a Boost PP
- data type or even as part of variadic macro data.
- #define TRETMACRO(x,y,z) ()
- #define TRETMACRO1(x,y,z) (x,,y,,z)
- #define VRETMACRO(x,y,z) x,,y,,z
- Here again we are returning something but in terms of a Boost PP
- tuple or in terms of variadic data, we have elements which are empty.
- [heading Emptiness in preprocessor metaprogramming]
- In the examples given above where "emptiness" in one form of another
- is passed as arguments to a macro or returned from a macro, the examples
- I have given were created as simplified as possible to illustrate my points.
- In actual preprocessor metaprogramming, using Boost PP, where complicated logic
- is used to generate macro output based on the arguments to a macro, it might be
- useful to allow and work with empty data if one were able to test for the fact
- that data was indeed empty.
- [heading Testing for empty data]
- Currently Boost PP has an undocumented macro for testing whether
- a parameter is empty of not, written without the use of variadic
- macros. The macro is called BOOST_PP_IS_EMPTY. The macro is by its nature flawed,
- since there is no generalized way of determining whether or not a
- parameter is empty using the C++ preprocessor prior to C++20. But the macro will
- work given input limited in various ways or if the input is actually empty.
- Paul Mensonides, the developer of Boost PP and the BOOST_PP_IS_EMPTY macro
- in that library, also wrote a better macro using variadic
- macros, for determining whether or not a parameter is empty or not, which
- he published on the Internet in response to a discussion about emptiness.
- This macro is also not perfect, since there is no perfect solution prior to C++20,
- but will work correctly with almost all input. I have adapted his code
- for VMD and developed my own very slightly different code.
- The macro is called [macroref BOOST_VMD_IS_EMPTY] and will return 1 if its input
- is empty or 0 if its input is not empty. The macro
- is a variadic macro which make take any input
- [footnote For VC++ 8 the input is not variadic data but a single parameter].
- [heading Macro Flaw with a standard C++ compiler]
- The one situation prior to C++20 where the macro does not work properly is if
- its input resolves to a function-like macro name or a sequence of preprocessor tokens ending with
- a function-like macro name and the function-like macro takes two or more parameters.
- Here is a simple example:
- #include <boost/vmd/is_empty.hpp>
-
- #define FMACRO(x,y) any_output
-
- BOOST_VMD_IS_EMPTY(FMACRO)
- BOOST_VMD_IS_EMPTY(some_input FMACRO)
-
- In the first case the name of a function-like macro is being passed to
- BOOST_VMD_IS_EMPTY while in the second case a sequence of preprocessing tokens is being
- passed to BOOST_VMD_IS_EMPTY ending with the name of a function-like macro.
- The function-like macro also has two ( or more ) parameters. In both the
- cases above a compiler error will result from the use of BOOST_VMD_IS_EMPTY.
- Please note that these two problematical cases are not the same as passing
- an invocation of a function-like macro name to BOOST_VMD_IS_EMPTY, as in
- #include <boost/vmd/is_empty.hpp>
-
- BOOST_VMD_IS_EMPTY(FMACRO(arg1,arg2))
- BOOST_VMD_IS_EMPTY(someinput FMACRO(arg1,arg2))
-
- which always works correctly, unless of course a particular function-like macro
- invocation resolves to either of our two previous situations.
- Another situation where the macro may not work properly is if the previously mentioned
- function-like macro takes a single parameter but creates an error when the argument
- passed is empty. An example of this would be:
- #define FMACRO(x) BOOST_PP_CAT(+,x C);
-
- When nothing is passed to FMACRO undefined behavior will occur since attempting to concatenate
- '+' to ' C' is UB in C++ preprocessor terms.
- So for a standard conforming compiler, prior to C++20, we have essentially two corner cases where
- the BOOST_VMD_IS_EMPTY does not work and, when it does not work it, produces a
- compiler error rather than an incorrect result. Essentially what is desired for maximum
- safety is that we never pass input ending with the name of a function-like macro name when
- testing for emptiness.
- [heading Macro Flaw with Visual C++]
- The VC++ preprocessor is not a standard C++ conforming preprocessor in at least two
- relevant situations to our discussion of emptiness. These situations combine to create
- a single corner case which causes the BOOST_VMD_IS_EMPTY macro to not work properly
- using VC++ when the input resolves to a function-like macro name.
- The first situation, related to our discussion of emptiness, where the VC++ preprocessor
- is not a standard C++ conforming preprocessor is that if a macro taking 'n' number of parameters is invoked
- with 0 to 'n-1' parameters, the compiler does not give an error, but only a warning.
- #define FMACRO(x,y) x + y
-
- FMACRO(1)
-
- should give a compiler error, as it does when using a C++ standard-conforming
- compiler, but when invoked using VC++ it only gives a warning
- and VC++ continues macro substitution with 'y' as a placemarker preprocessing token.
- This non-standard conforming action actually eliminates the case where BOOST_VMD_IS_EMPTY
- does not work properly with a standard C++ conforming compiler. But of course it has the
- potential of producing incorrect output in other macro processing situations unrelated
- to the BOOST_VMD_IS_EMPTY invocation, where a compiler error should occur.
- A second general situation, related to our discussion of emptiness, where the VC++ preprocessor
- is not a standard C++ conforming preprocessor is that the expansion of a macro works incorrectly
- when the expanded macro is a function-like macro name followed by a function-like macro invocation,
- in which case the macro re-expansion is erroneously done more than once. This latter case can be
- seen by this example:
- #define FMACRO1(parameter) FMACRO3 parameter()
- #define FMACRO2() ()
- #define FMACRO3() 1
-
- FMACRO1(FMACRO2)
-
- should expand to:
-
- FMACRO3()
-
- but in VC++ it expands to:
-
- 1
- where after initially expanding the macro to:
- FMACRO3 FMACRO2()
-
- VC++ erroneously rescans the sequence of preprocessing tokens more than once rather than
- rescan just one more time for more macro names.
- What these two particular preprocessor flaws in the VC++ compiler mean is that although
- BOOST_VMD_IS_EMPTY does not fail with a compiler error in the same case as with
- a standard C++ conforming compiler given previously, it fails by giving
- the wrong result in another situation.
- The failing situation is:
- when the input to BOOST_VMD_IS_EMPTY resolves to only a function-like macro
- name, and the function-like macro, when passed a single empty argument, expands to
- a Boost PP tuple, BOOST_VMD_IS_EMPTY will erroneously return 1 when using the Visual C++
- compiler rather than either give a preprocessing error or return 0.
- Here is an example of the failure:
- #include <boost/vmd/is_empty.hpp>
-
- #define FMACRO4() ( any_number_of_tuple_elements )
- #define FMACRO5(param) ( any_number_of_tuple_elements )
- #define FMACRO6(param1,param2) ( any_number_of_tuple_elements )
-
- BOOST_VMD_IS_EMPTY(FMACRO4) // erroneously returns 1, instead of 0
- BOOST_VMD_IS_EMPTY(FMACRO5) // erroneously returns 1, instead of 0
- BOOST_VMD_IS_EMPTY(FMACRO6) // erroneously returns 1, instead of generating a preprocessing error
-
- As with a standard C++ conforming compiler prior to C++20, we have a rare corner case where
- the BOOST_VMD_IS_EMPTY will not work properly, but unfortunately in this very
- similar but even rarer corner case with VC++, we will silently get an incorrect result
- rather than a compiler error.
- I want to reiterate that for all compilers prior to C++20 there is no perfect solution
- in C++ to the detection of emptiness even for a C++ compiler whose preprocessor is completely
- conformant, which VC++ obviously is not.
- [heading Testing emptiness in C++20 mode]
- A few compilers can currently operate in C++20 mode, by which I mean that you
- can pass a compiler flag when compiling with such a compiler which enforces the
- upcoming C++20 standard. One of the features of the C++20 standard is the
- addition of a preprocessor construct called __VA_OPT__. Because of the
- specification of how the __VA_OPT__ construct works in C++20, it is now possible
- to have the BOOST_VMD_IS_EMPTY macro work perfectly to test for emptiness without
- any of the flaws that exist in the macro for levels of the C++ standard before
- C++20. But the macro will only do a 100% reliable test for emptiness when the
- compiler is compiling in C++20 mode. For all levels of the C++ standard before
- C++20, such as C++98, C++03, C++11, C++14, and C++17, the testing for emptiness
- has the corner cases which prevent it from wroking perfectly which have already
- been discussed.
- Furthermore in C++20 mode it is possible that a compiler still does not yet
- support the __VA_OPT__ construct, even though it is part of the C++20 standard.
- Luckily it is possible to test whether or not a compiler supports the __VA_OPT__
- construct in C++20 mode, and the macro implementation of BOOST_VMD_IS_EMPTY
- does that before using the construct to provide a perfectly reliable
- implementation for testing emptiness.
- The result of all this is that when a compiler is compiling source using
- the C++20 standard, and supports the C++20 __VA_OPT__ preprocessor construct,
- the implementation provides a completely reliable way of testing for emptiness
- using the BOOST_VMD_IS_EMPTY macro. Otherwise the BOOST_VMD_IS_EMPTY macro
- has the corner cases previously discussed which make the macro less than
- 100% reliable in testing for emptiness. The good news of course is that
- more compilers will be implementaing the C++20 standard and more C++
- programmers will be using the C++20 standard to compile their code.
- The programmer may know whether the compiler is being used in C++20 mode
- from the command line parameters he passes to the compiler, and the programmer
- may know whether the compiler in C++20 mode supports the __VA_OPT__ construct
- of C++20 from the compiler's documentation. But from the preprocessor programming
- perspective it would be good to find out using a macro whether or not C++20 mode
- with the __VA_OPT__ construct is being used so that the BOOST_VMD_IS_EMPTY
- macro can be considered completely reliable in testing for emptiness. Such a macro
- does already exist in the Boost Preprocessor library, and it is called BOOST_PP_VARIADIC_HAS_OPT.
- You can read the documentation for this macro in the Boost Preprocessor library
- documentation, but I will give a quick rundown of how this works here. The macro
- is a function-like macro taking no parameters and returns 1 if the compiler
- is in C++20 mode and __VA_OPT__ is supported, otherwise returns 0. The header
- file needed to invoke the macro as BOOST_PP_VARIADIC_HAS_OPT() is included as:
- #include <boost/preprocessor/variadic/has_opt.hpp>
-
- The programmer does not have to be compiling in C++20 mode to invoke the
- BOOST_PP_VARIADIC_HAS_OPT macro. When the programmer is not in C++20 mode invoking
- the macro always returns 0. When the programmer is in C++20 mode invoking
- the macro returns 1 when the __VA_OPT__ construct is supported and returns 0
- when the __VA_OPT__ construct is not supported. It does this latter step through
- clever preprocessor programming.
- [heading Macro Flaw conclusion]
- With all of the above mentioned, the cases where BOOST_VMD_IS_EMPTY will work
- incorrectly are very small, even with the erroneous VC++ preprocessor,
- and I consider the macro worthwhile to use since it works correctly with the vast
- majority of possible preprocessor input, and always works correctly in C++20
- mode with __VA_OPT__ preprocessor support.
- The case where it will not work, with both a C++ standard conforming preprocessor or
- with Visual C++, occurs when the name of a function-like macro is part of the input
- to BOOST_VMD_IS_EMPTY. Obviously the macro should be used by the preprocessor
- metaprogrammer when the possible input to it is constrained to eliminate the erroneous
- case.
- Furthermore, since emptiness can correctly be tested for in nearly every situation, the
- BOOST_VMD_IS_EMPTY macro can be used internally when the preprocessor metaprogrammer wants to return data
- from a macro and all or part of that data could be empty.
- Therefore I believe the BOOST_VMD_IS_EMPTY macro is quite useful, despite the corner case flaws
- which makes it imperfect. Consequently I believe that the preprocessor metaprogrammer
- can use the concept of empty preprocessor data in the design of his own macros.
- [heading Using the macro]
- The macro BOOST_VMD_IS_EMPTY is used internally throughout VMD and macro programmers
- may find this macro useful in their own programming efforts despite the slight flaw
- in the way that it works in pre C++20 mode.
- You can use the general header file:
- #include <boost/vmd/vmd.hpp>
-
- or you can use the individual header file:
- #include <boost/vmd/is_empty.hpp>
-
- for the BOOST_VMD_IS_EMPTY macro.
- [endsect]
|