Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

This is the documentation for an old version of Boost. Click here to view this page for the latest version.
PrevUpHomeNext

Generating emptiness and identity

Using BOOST_PP_EMPTY and BOOST_PP_IDENTITY

Boost PP Has a macro called BOOST_PP_EMPTY() which expands to nothing.

Ordinarily this would not seem that useful, but the macro can be used in situations where one wants to return a specific value even though a further macro call syntax is required taking no parameters. This sort of usefulness occurs in Boost PP when there are two paths to take depending on the outcome of a BOOST_PP_IF or BOOST_PP_IIF logic. Here is an artificial example:

#include <boost/preprocessor/control/iif.hpp>
#include <boost/preprocessor/facilities/empty.hpp>

#define MACRO_CHOICE(parameter) \
    BOOST_PP_IIF(parameter) \
        ( \
        MACRO_CALL_IF_PARAMETER_1, \
        SOME_FIXED_VALUE BOOST_PP_EMPTY \
        ) \
    ()

#define MACRO_CALL_IF_PARAMETER_1() some_processing

In the general logic above is: if parameter is 1 another macro is invoked, whereas if the parameter is 0 some fixed value is returned. The reason that this is useful is that one may not want to code the MACRO_CHOICE macro in this way:

#include <boost/preprocessor/control/iif.hpp>

#define MACRO_CHOICE(parameter) \
    BOOST_PP_IIF(parameter) \
        ( \
        MACRO_CALL_IF_PARAMETER_1(), \
        SOME_FIXED_VALUE \
        )

#define MACRO_CALL_IF_PARAMETER_1() some_processing

because it is inefficient. The invocation of MACRO_CALL_IF_PARAMETER_1 will still be generated even when 'parameter' is 0.

This idiom of returning a fixed value through the use of BOOST_PP_EMPTY is so useful that Boost PP has an accompanying macro to BOOST_PP_EMPTY to work with it. This accompanying macro is BOOST_PP_IDENTITY(value)(). Essentially BOOST_PP_IDENTITY returns its value when it is invoked. Again, like BOOST_PP_EMPTY, the final invocation must be done with no value.

Our example from above, which originally used BOOST_PP_EMPTY to return a fixed value, is now:

#include <boost/preprocessor/control/iif.hpp>
#include <boost/preprocessor/facilities/identity.hpp>

#define MACRO_CHOICE(parameter) \
    BOOST_PP_IIF(parameter) \
        ( \
        MACRO_CALL_IF_PARAMETER_1, \
        BOOST_PP_IDENTITY(SOME_FIXED_VALUE) \
        ) \
    ()

#define MACRO_CALL_IF_PARAMETER_1() some_processing

The macro BOOST_PP_IDENTITY is actually just:

#define BOOST_PP_IDENTITY(value) value BOOST_PP_EMPTY

so you can see how it is essentially a shorthand for the common case originally shown at the top of returning a value through the use of BOOST_PP_EMPTY.

Using BOOST_VMD_EMPTY and BOOST_VMD_IDENTITY

The one problem when using BOOST_PP_EMPTY and BOOST_PP_IDENTITY is that the final invocation must be with no parameters. This is very limiting. If the final invocation must be with one or more parameters you cannot use BOOST_PP_EMPTY or BOOST_PP_IDENTITY. In other words, making a change to either of our two examples:

#include <boost/preprocessor/control/iif.hpp>
#include <boost/preprocessor/facilities/empty.hpp>

#define MACRO_CHOICE(parameter1,parameter2) \
    BOOST_PP_IIF(parameter1) \
        ( \
        MACRO_CALL_IF_PARAMETER_1, \
        SOME_FIXED_VALUE BOOST_PP_EMPTY \
        ) \
    (parameter2)

#define MACRO_CALL_IF_PARAMETER_1(parameter2) some_processing_using_a_parameter

or

#include <boost/preprocessor/control/iif.hpp>
#include <boost/preprocessor/facilities/identity.hpp>

#define MACRO_CHOICE(parameter1,parameter2) \
    BOOST_PP_IIF(parameter1) \
        ( \
        MACRO_CALL_IF_PARAMETER_1, \
        BOOST_PP_IDENTITY(SOME_FIXED_VALUE) \
        ) \
    (parameter2)

#define MACRO_CALL_IF_PARAMETER_1(parameter2) some_processing_using_a_parameter

will produce a preprocessing error since the final invocation to either BOOST_PP_EMPTY or BOOST_PP_IDENTITY can not be done with 1 or more parameters.

It would be much more useful if the final invocation could be done with any number of parameters. This is where using variadic macros solves the problem. The BOOST_VMD_EMPTY and BOOST_VMD_IDENTITY macros have the exact same functionality as their Boost PP counterparts but the final invocation can be made with any number of parameters, and those parameters are just ignored when BOOST_VMD_EMPTY or BOOST_VMD_IDENTITY is the choice.

Now for our two examples we can have:

#include <boost/preprocessor/control/iif.hpp>
#include <boost/vmd/empty.hpp>

#define MACRO_CHOICE(parameter1,parameter2) \
    BOOST_PP_IIF(parameter1) \
        ( \
        MACRO_CALL_IF_PARAMETER_1, \
        SOME_FIXED_VALUE BOOST_VMD_EMPTY \
        ) \
    (parameter2)

#define MACRO_CALL_IF_PARAMETER_1(parameter2) some_processing_using_parameters

or

#include <boost/preprocessor/control/iif.hpp>
#include <boost/vmd/identity.hpp>

#define MACRO_CHOICE(parameter1,parameter2) \
    BOOST_PP_IIF(parameter1) \
        ( \
        MACRO_CALL_IF_PARAMETER_1, \
        BOOST_VMD_IDENTITY(SOME_FIXED_VALUE) \
        ) \
    (parameter2)

#define MACRO_CALL_IF_PARAMETER_1(parameter2) some_processing_using_parameters

and our macros will compile without preprocessing errors and work as expected. Both BOOST_VMD_EMPTY and BOOST_VMD_IDENTITY will take any number of parameters in their invocation, which makes them useful for a final invocation no matter what is being passed.

Usage for BOOST_VMD_EMPTY and BOOST_VMD_IDENTITY

To use the BOOST_VMD_EMPTY macro either include the general header:

#include <boost/vmd/vmd.hpp>

or include the specific header:

#include <boost/vmd/empty.hpp>

To use the BOOST_VMD_IDENTITY macro either include the general header:

#include <boost/vmd/vmd.hpp>

or include the specific header:

#include <boost/vmd/identity.hpp>
Using BOOST_VMD_EMPTY and BOOST_VMD_IDENTITY with VC++

Unfortunately the Visual C++ preprocessor has a problem when a macro expands to something followed by a variadic macro which expands to nothing. This is the case when using BOOST_VMD_EMPTY following some non-empty expansion, or the equivalent use of BOOST_VMD_IDENTITY. As strange as it sounds this VC++ preprocessor problem is normally solved by concatenating the result using BOOST_PP_CAT with an empty value. But then again the many non-standard behaviors of VC++ are difficult to understand or even track.

In order to make this technique transparent when used with a C++ standard conforming preprocessor or VC++ non-standard preprocessor you can use the BOOST_VMD_IDENTITY_RESULT macro passing to it a single parameter which is a result returned from a macro which uses BOOST_VMD_IDENTITY ( or its equivalent 'value BOOST_VMD_EMPTY' usage ).

Given our MACRO_CHOICE example above, if you have another macro invoking MACRO_CHOICE simply enclose that invocation within BOOST_VMD_IDENTITY_RESULT. As in the very simple:

#include <boost/vmd/identity.hpp>

#define CALLING_MACRO_CHOICE(parameter1,parameter2) \
    BOOST_VMD_IDENTITY_RESULT(MACRO_CHOICE(parameter1,parameter2))

Alternatively you can change MACRO_CHOICE so that its implementation and usage is:

#include <boost/preprocessor/control/iif.hpp>
#include <boost/vmd/identity.hpp>

#define MACRO_CHOICE(parameter1,parameter2) \
    BOOST_VMD_IDENTITY_RESULT \
        ( \
        BOOST_PP_IIF(parameter1) \
            ( \
            MACRO_CALL_IF_PARAMETER_1, \
            BOOST_VMD_IDENTITY(SOME_FIXED_VALUE) \
            ) \
        (parameter2) \
        )

#define CALLING_MACRO_CHOICE(parameter1,parameter2) \
    MACRO_CHOICE(parameter1,parameter2)

Using BOOST_VMD_EMPTY and BOOST_VMD_IDENTITY in this way will ensure they can be used without preprocessing problems with either VC++ or any C++ standard conforming preprocessor.

Usage for BOOST_VMD_IDENTITY_RESULT

The macro BOOST_VMD_IDENTITY_RESULT is in the same header file as BOOST_VMD_IDENTITY, so to use the BOOST_VMD_IDENTITY_RESULT macro either include the general header:

#include <boost/vmd/vmd.hpp>

or include the specific header:

#include <boost/vmd/identity.hpp>

PrevUpHomeNext