Есть ли канонический способ решения проблем с пространством имен, возникающих при попытке поддерживать переносимый код между цепочкой инструментов TR1 и не-TR1?
У меня есть проект VC ++ 2010, который #include <type_traits>
, У меня также есть компилятор LLVM 3.0, который может справиться с этим хорошо. Это позволяет мне использовать шаблоны, такие как:
std::enable_if<typename>
std::is_enum<typename>
Однако мне также нужно собрать и поддерживать этот код на Clang-компиляторе Xcode 4.5:
$ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang --version
Apple clang version 4.1 (tags/Apple/clang-421.11.66) (based on LLVM 3.1svn)
Target: x86_64-apple-darwin11.4.2
Thread model: posix
Этот компилятор, кажется, не имеет включаемого файла, вместо этого он имеет. Однако это вызывает у меня проблемы, потому что пространство имен изменилось с std :: на __gnu_cxx ::, что означает, что я должен использовать:
__gnu_cxx::__enable_if<typename>
Каким-то образом мне удалось определить, что определение символа __GLIBCXX__
Достаточно определить, должен ли я использовать один или другой (даже не уверен, что это правильный способ сделать это, но пока он работает между компиляторами, которые я использую).
Поэтому я мог бы использовать макросы препроцессора:
#ifdef __GLIBCXX__
# include <tr1/type_traits>
# define ENABLE_IF __gnu_cxx::__enable_if
#else
# include <type_traits>
# define ENABLE_IF std::enable_if
#endif
Но кажется, что это скорее взлом, чем правильное решение. (На самом деле я пробовал это, и это не работает, потому что пытается использовать __gnu_cxx::__enable_if
вызывает эту ошибку:
error: too few template arguments for class template '__enable_if'
Я думал о том, чтобы сделать что-то вроде:
#ifdef __GLIBCXX__
# include <tr1/type_traits>
namespace __gnu_cxx = foo;
#else
# include <type_traits>
namespace std = foo;
#endif
... foo::enable_if< ... >
Однако это не работает, потому что шаблон называется enable_if
в одном пространстве имен, но __enable_if
в другом.
Я уверен, что я не первый, кто имеет дело с этой проблемой — может кто-нибудь указать мне на лучшие отраслевые практики для решения этой проблемы, пожалуйста? Или я должен просто использовать Boost вместо этого?
Есть похожий вопрос (я думаю), но только частичный ответ Вот. Есть ли лучшие варианты?
РЕДАКТИРОВАТЬ: я пробовал это, с <boost/type_traits.hpp>
:
#include <boost/type_traits.hpp>
template <typename ValueType>
class Extractor <ValueType, typename boost::enable_if<boost::is_enum<ValueType>::value>::type> {
public:
ValueType extract(double value) {
return static_cast<ValueType>(static_cast<int>(value)); // cast to int first, then enum, to satisfy VC++2010
}
};
enum MyEnum { Enum0, Enum1 };
Extractor<MyEnum> e;
MyEnum ev = e.extract(1.0);
Однако это дает мне следующую ошибку компилятора в Xcode 4.5:
error: expected a qualified name after 'typename'
class Extractor <ValueType, typename boost::enable_if<boost::is_enum<ValueType>::value>::type> {
^
error: unknown type name 'type'
Так что не похоже, что std :: enable_if и boost :: enable_if совместимы с раскрытием.
Я отвечу на свой вопрос, как я получил что-то рабочее, используя boost::enable_if_c
(обратите внимание, что замена для замены std::enable_if
является boost::enable_if_c
не boost::enable_if
).
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_enum.hpp>
// this would work except one of my environments doesn't contain <complex> so it's
// too inclusive. Better (for me) to use the more specific includes above.
// #include <boost/type_traits.hpp>
template <typename ValueType>
class Extractor <ValueType, typename boost::enable_if_c<boost::is_enum<ValueType>::value>::type> {
public:
ValueType extract(double value) {
return static_cast<ValueType>(static_cast<int>(value)); // cast to int first, then enum, to satisfy VC++2010
}
};
Однако мне все еще очень любопытно узнать, есть ли лучший способ справиться с этим, чем прибегать к Boost.
Других решений пока нет …