Эта библиотека позволяет обернуть типы в круглые скобки, чтобы они всегда могли передаваться в виде макро параметров.
Рассмотрим следующий макрос, который объявляет переменную<var
><n
>с указанным<type
>(см. также<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
>, принятый в качестве первого макропараметра, содержит запятую<,
>, не обернутую круглым скобком<()
>. Препроцессор интерпретирует эту незавернутую запятую как разделение между макропараметрами, заключая, что в общей сложности три (а не два) параметра передаются макро в следующем порядке:
- <
std::map<int
> - [ORIG_END] -->
- <
2
>
Обратите внимание, что в отличие от компилятора, препроцессор распознает только круглые скобки<()
>. Угловой<<>
>и квадратный<[]
>скобки не распознаются препроцессором при разборе макропараметров.
В некоторых случаях можно обойти эту проблему, избегая передачи выражения типа макросу. Например, в случае выше<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) использование этого макроса на абстрактных типах (т.е. классах с одной или несколькими чистыми виртуальными функциями) порождает ошибку компилятора. Этого можно избежать, манипулируя добавлением и удалением ссылки на него.
Давайте пропрограммируем макрос, который выполняет статическое утверждение на метафункциишаблонного метапрограммирования(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(int&
x,int&y)
>в общих случаях, когда типы параметров не содержат запятой (в то же время позволяя указывать типы параметров с запятыми в качестве специальных случаев с использованием<BOOST_LOCAL_FUNCTION(BOOST_IDENTITY_TYPE((std::map<int,char>))&
x,int&y)
>).
Реализация этого библиотечного макроса эквивалентна следующему:
#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
>, следовательно, получая исходный тип из скобчатого типа (эффективно удаляя дополнительный скобки из-за указанного типа).