C++, как обрабатывать пространства имен tr1 и не-tr1 в переносимом коде?

Существует ли канонический способ решения проблем с пространством имен, возникающих при попытке поддерживать переносимый код между цепочкой инструментов 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'
  • дальнейшее копание предполагает, что эта версия 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 совместимы с раскрывающимся списком.

1 ответ

Я отвечу на свой вопрос, как я получил что-то рабочее с помощью 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.

Другие вопросы по тегам