Эта библиотека позволяет обернуть типы в круглые скобки, чтобы они всегда могли передаваться в виде макро параметров.
Рассмотрим следующий макрос, который объявляет переменную varn с указанным типом (см. также var_error.cpp):
#define VAR(type, n) type var ## n
VAR(int, 1);
VAR(std::map<int, char>, 2);
Первый макровызов работает корректно, объявляя переменную var1 типа int. Однако второй вызов макроса не приводит к возникновению ошибки препроцессора, аналогичной следующей:
error: macro "VAR" passed 3 arguments, but takes just 2
Это связано с тем, что тип std::map, принятый в качестве первого макропараметра, содержит запятую ,, не обернутую круглым скобком (). Препроцессор интерпретирует эту незавернутую запятую как разделение между макропараметрами, заключая, что в общей сложности три (а не два) параметра передаются макро в следующем порядке:
Обратите внимание, что, в отличие от компилятора, препроцессор распознает только круглый скобок (). Угловой <> и квадратный [] скобки не распознаются препроцессором при разборе макро параметров.
В некоторых случаях можно обойти эту проблему, избегая передачи выражения типа макросу. Например, в случае выше typedef можно было бы использовать для указания выражения типа с запятыми вне макроса (см. также var.cpp):
typedef std::map<int, char> map_type;
VAR(map_type, 3);
Когда это невозможно и не желательно (например, см. шаблон функции f в разделе ниже), этот заголовок библиотеки boost/utility/identity_type.hpp определяет макрос BOOST_IDENTITY_TYPE, который можно использовать для обхода вопроса при сохранении выражения типа в качестве одного из макро параметров (см. также var.cpp).
#include <boost/utility/identity_type.hpp>
VAR(BOOST_IDENTITY_TYPE((std::map<int, char>)), 4);
Макрос BOOST_IDENTITY_TYPE расширяется до выражения, которое оценивает (по времени компиляции) указанный тип. Указанный тип никогда не делится на несколько макропараметров, потому что он всегда обернут набором дополнительных круглых скобок (). Фактически, необходимо использовать в общей сложности два набора круглых скобок: Скобки для вызова макроса BOOST_IDENTITY_TYPE(...) плюс внутренняя скобка для обертывания типа перешла на макрос BOOST_IDENTITY_TYPE((...)).
Этот макрос работает на любом компиляторе C++03 (и не использует вариадные макросы). [] Авторы первоначально разработали и протестировали эту библиотеку с использованием GNU Compiler Collection (GCC) C++ 4.5.3 (с функциями C++11 и без них включено -std=c++0x) на Cygwin и Miscrosoft Visual C++ (MSVC) 8.0 на Windows 7. Для получения дополнительной информации о поддерживаемых компиляторах и платформах см. библиотеку результатов тестов регрессий .
Этот макрос должен быть префиксирован typename при использовании в шаблонах. Например, запрограммируем макрос, который объявляет параметр функции argn с указанным type (см. также template.cpp):
#define ARG(type, n) type arg ## n
template<typename T>
void f(
ARG(typename BOOST_IDENTITY_TYPE((std::map<int, T>)), 1)
) {
std::cout << arg1[0] << std::endl;
}
std::map<int, char> a;
a[0] = 'a';
f<char>(a);
Однако обратите внимание, что параметр шаблона char должен быть указан вручную при вызове функции, как в f<char>(a). На самом деле, когда BOOST_IDENTITY_TYPE макрос используется для обертывания параметра шаблона функции, параметр шаблона больше не может быть автоматически выведен компилятором в виде вызова функции f(a). [] (Это ограничение не распространяется на шаблоны классов, поскольку параметры шаблонов классов всегда должны быть четко указаны). Иными словами, без использования BOOST_IDENTITY_TYPE макрос, C++ обычно может автоматически вывести параметр шаблона функции, как показано ниже:
template<typename T>
void g(
std::map<int, T> arg1
) {
std::cout << arg1[0] << std::endl;
}
g<char>(a);
g(a);
На некоторых компиляторах (например, GCC) использование этого макроса на абстрактных типах (т.е. классах с одной или несколькими чистыми виртуальными функциями) порождает ошибку компилятора. Этого можно избежать, манипулируя добавлением и удалением ссылки на него.
Запрограммируем макрос, который выполняет статическое утверждение на метафункции Template Meta-Programming (TMP) (аналогично Boost). MPL BOOST_MPL_ASSERT. Макрос BOOST_IDENTITY_TYPE может использоваться для передачи метафункции с несколькими параметрами шаблона на макро утверждение (так, чтобы обрабатывать запятые, разделяющие параметры шаблона). В этом случае, если метафункция является абстрактным типом, необходимо манипулировать добавлением и удалением ссылки на нее (см. также abstract.cpp):
#define TMP_ASSERT(metafunction) \
BOOST_STATIC_ASSERT(metafunction::value)
template<typename T, bool b>
struct abstract {
static const bool value = b;
virtual void f(T const& x) = 0;
};
TMP_ASSERT(
boost::remove_reference<
BOOST_IDENTITY_TYPE((
boost::add_reference<
abstract<int, true>
>::type
))
>::type
);
Макрос BOOST_IDENTITY_TYPE может использоваться либо при вызове определяемого пользователем макроса (как показано примерами до сих пор), либо внутренне при реализации определяемого пользователем макроса (как показано ниже). При использовании BOOST_IDENTITY_TYPE в реализации определяемого пользователем макроса абонент пользовательского макроса должен будет указать дополнительную скобку (см. также paren.cpp):
#define TMP_ASSERT_PAREN(parenthesized_metafunction) \
\
BOOST_STATIC_ASSERT(BOOST_IDENTITY_TYPE(parenthesized_metafunction)::value)
#define TMP_ASSERT(metafunction) \
BOOST_STATIC_ASSERT(metafunction::value)
TMP_ASSERT_PAREN((boost::is_const<std::map<int, char> const>));
TMP_ASSERT(BOOST_IDENTITY_TYPE((boost::is_const<std::map<int, char> const>)));
Однако обратите внимание, что абоненту всегда необходимо указать дополнительную скобку, даже если параметры макроса не содержат запятой:
TMP_ASSERT_PAREN((boost::is_const<int const>));
TMP_ASSERT(boost::is_const<int const>);
В некоторых случаях использование BOOST_IDENTITY_TYPE в реализации определяемого пользователем макроса может обеспечить лучший синтаксис для абонента. Например, это относится к BOOST_MPL_ASSERT, поскольку большинство выражений метапрограммирования шаблонов содержат незавернутые запятые, поэтому пользователю менее сложно всегда указывать дополнительный скобки ((...)) вместо использования BOOST_IDENTITY_TYPE
BOOST_MPL_ASSERT((
boost::mpl::and_<
boost::is_const<T>
, boost::is_reference<T>
>
));
Однако в других ситуациях может быть предпочтительнее не требовать дополнительной скобки в обычных случаях и обрабатывать запятые в особых случаях с использованием BOOST_IDENTITY_TYPE. Например, это относится к BOOST_LOCAL_FUNCTION, для которых всегда требуется дополнительный скобки ((...)) вокруг типов приведет к неестественному синтаксису для локальной функциональной подписи:
int BOOST_LOCAL_FUNCTION( ((int&)) x, ((int&)) y ) {
return x + y;
} BOOST_LOCAL_FUNCTION_NAME(add)
Вместо этого пользователь должен указать BOOST_IDENTITY_TYPE только тогда, когда это необходимо. BOOST_LOCAL_FUNCTION,& BOOST_LOCAL_FUNCTION(BOOST_IDENTITY_TYPE::map,char,&.
Реализация этого библиотечного макроса эквивалентна следующему: []
#include <boost/type_traits/function_traits.hpp>
#define BOOST_IDENTITY_TYPE(parenthesized_type) \
boost::function_traits<void parenthesized_type>::arg1_type
По существу, тип обернут между круглыми скобками (std::map<int, char>), так что он может передаваться как один макропараметр, даже если он содержит запятые. Затем тип скобки преобразуется в тип функции, возвращающей void и с указанным типом как тип первого и единственного аргумента void (std::map<int,char>). Наконец, тип первого аргумента arg1_type извлекается во время компиляции с использованием мета-функции function_traits, таким образом получая исходный тип из скобчатого типа (эффективно удаляя дополнительный скобки из-за указанного типа).