Увеличить PP Имеет макрос под названием BOOST_PP_EMPTY(), который расширяется до нуля.
Обычно это не кажется полезным, но макрос может быть использован в ситуациях, когда требуется вернуть определенное значение, даже если требуется дополнительный синтаксис макровызова без параметров. Этот вид полезности происходит в Boost PP, когда есть два пути, которые нужно пройти в зависимости от результата логики BOOST_PP_IF или BOOST_PP_IIF. Вот искусственный пример:
#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
В общей логике выше: если параметр равен 1, то используется другой макрос, тогда как если параметр равен 0, возвращается некоторое фиксированное значение. Причина, по которой это полезно, заключается в том, что вы можете не захотеть кодировать макрос MACRO_CHOICE таким образом:
#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
Потому что это неэффективно. Вызов MACRO_CALL_IF_PARAMETER_1 будет по-прежнему генерироваться, даже если «параметр» равен 0.
Эта идиома возврата фиксированного значения через использование BOOST_PP_EMPTY настолько полезна, что Boost PP имеет сопровождающий макрос к BOOST_PP_EMPTY для работы с ним. Сопровождающим макросом является BOOST_PP_IDENTITY(value)(). По сути, BOOST_PP_IDENTITY возвращает свою ценность при вызове. Опять же, как и в случае с BOOST_PP_EMPTY, окончательное обращение должно быть сделано без какой-либо ценности.
Наш пример сверху, который первоначально использовал BOOST_PP_EMPTY для возврата фиксированного значения, теперь:
#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
На самом деле BOOST_PP_IDENTITY это:
#define BOOST_PP_IDENTITY(value) value BOOST_PP_EMPTY
Таким образом, вы можете увидеть, что это, по сути, сокращение для обычного случая, первоначально показанного в верхней части возврата значения с помощью BOOST_PP_EMPTY.
Единственная проблема при использовании BOOST_PP_EMPTY и BOOST_PP_IDENTITY заключается в том, что окончательный вызов должен быть без параметров. Это очень ограничивает. Если окончательный вызов должен быть с одним или несколькими параметрами, вы не можете использовать BOOST_PP_EMPTY или BOOST_PP_IDENTITY. Иными словами, внесение изменений в один из двух примеров:
#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
или
#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
Это приведет к ошибке предварительной обработки, поскольку окончательное вызов либо BOOST_PP_EMPTY, либо BOOST_PP_IDENTITY не может быть выполнено с 1 или более параметрами.
Было бы гораздо полезнее, если бы окончательный вызов можно было сделать с любым количеством параметров. Именно здесь использование вариадных макросов решает проблему. Макросы BOOST_VMD_EMPTY и BOOST_VMD_IDENTITY имеют ту же функциональность, что и их аналоги Boost PP, но окончательный вызов может быть сделан с любым количеством параметров, и эти параметры просто игнорируются, когда выбор сделан BOOST_VMD_EMPTY или BOOST_VMD_IDENTITY.
Теперь о двух примерах, которые мы можем привести:
#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
или
#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
и наши макросы будут компилироваться без ошибок предварительной обработки и работать так, как ожидалось. Как BOOST_VMD_EMPTY, так и BOOST_VMD_IDENTITY принимают любое количество параметров в своем обращении, что делает их полезными для окончательного вызова независимо от того, что передается.
Для использования макроса BOOST_VMD_EMPTY включите общий заголовок:
#include <boost/vmd/vmd.hpp>
или включить конкретный заголовок:
#include <boost/vmd/empty.hpp>
Для использования макроса BOOST_VMD_IDENTITY используется общий заголовок:
#include <boost/vmd/vmd.hpp>
или включить конкретный заголовок:
#include <boost/vmd/identity.hpp>
К сожалению, препроцессор Visual C++ имеет проблему, когда макрос расширяется до чего-то, за чем следует вариадный макрос. Это происходит при использовании BOOST_VMD_EMPTY после некоторого непустого расширения или эквивалентного использования BOOST_VMD_IDENTITY. Как бы странно это ни звучало, проблема препроцессора VC++ обычно решается путем объединения результата с помощью BOOST_PP_CAT с пустым значением. Но, опять же, многие нестандартные модели поведения VC++ трудно понять или даже отследить.
Чтобы сделать этот метод прозрачным при использовании со стандартом C++, соответствующим препроцессору или VC++. Нестандартный препроцессор BOOST_VMD_IDENTITY_RESULT макрос передает ему один параметр, который является результатом, возвращенным из макроса, который использует BOOST_VMD_IDENTITY (или его эквивалентное использование BOOST_VMD_EMPTY).
Приведем пример MACRO_CHOICE выше, если у вас есть другой макрос, вызывающий MACRO_CHOICE. Просто включите это обращение в BOOST_VMD_IDENTITY_RESULT. Как в самом простом:
#include <boost/vmd/identity.hpp>
#define CALLING_MACRO_CHOICE(parameter1,parameter2) \
BOOST_VMD_IDENTITY_RESULT(MACRO_CHOICE(parameter1,parameter2))
В качестве альтернативы вы можете изменить MACRO_CHOICE, чтобы его реализация и использование были:
#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)
Использование BOOST_VMD_EMPTY и BOOST_VMD_IDENTITY таким образом гарантирует, что они могут использоваться без проблем предварительной обработки с VC++ или любым стандартным препроцессором C++.
BOOST_VMD_IDENTITY_RESULT находится в том же файле заголовка, что и BOOST_VMD_IDENTITY, поэтому используйте BOOST_VMD_IDENTITY_RESULT Макро либо включает общий заголовок:
#include <boost/vmd/vmd.hpp>
или включить конкретный заголовок:
#include <boost/vmd/identity.hpp>