Эта библиотека позволяет обернуть типы в круглые скобки, чтобы они всегда могли передаваться в виде макро параметров.
Рассмотрим следующий макрос, который объявляет переменную var
n
с указанным типом
(см. также 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
при использовании в шаблонах. Например, запрограммируем макрос, который объявляет параметр функции arg
n
с указанным 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
, таким образом получая исходный тип из скобчатого типа (эффективно удаляя дополнительный скобки из-за указанного типа).