问题
Is there a canonical way to deal with the namespace issues that arise when trying to maintain portable code between a TR1 and non-TR1 toolchain?
I have a VC++2010 project that #include <type_traits>
. I also have an LLVM 3.0 compiler that can handle this fine. This allows me to use templates such as:
std::enable_if<typename>
std::is_enum<typename>
However I also need to build and maintain this code on an Xcode 4.5 clang compiler:
$ /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
This compiler doesn't seem to have a include file, instead it has a . However this is causing me problems because the namespace has changed from std:: to __gnu_cxx::, meaning I have to use:
__gnu_cxx::__enable_if<typename>
Somehow I was able to determine that the definition of the symbol __GLIBCXX__
is sufficient to determine whether I should use one or the other (not even sure that's the right way to do it, but for now it works between the compilers I'm using).
So I could resort to using preprocessor macros:
#ifdef __GLIBCXX__
# include <tr1/type_traits>
# define ENABLE_IF __gnu_cxx::__enable_if
#else
# include <type_traits>
# define ENABLE_IF std::enable_if
#endif
But this seems like it might be more of a hack than a proper solution. (Actually I tried this and it doesn't work, because trying to use __gnu_cxx::__enable_if
causes this error:
error: too few template arguments for class template '__enable_if'
- further digging suggests that this version of enable_if actually takes two template arguments. I'm now very lost...)
I thought about doing something like:
#ifdef __GLIBCXX__
# include <tr1/type_traits>
namespace __gnu_cxx = foo;
#else
# include <type_traits>
namespace std = foo;
#endif
... foo::enable_if< ... >
However this doesn't work because the template is called enable_if
in one namespace, but __enable_if
in the other.
I'm sure I'm not the first person to deal with this problem - can someone point me at the industry best practice for resolving this please? Or should I just use Boost instead?
There is a similar question (I think) but only a partial answer here. Are there better options?
EDIT: I tried this, with <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);
However this gives me the following compiler error in 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'
So it doesn't seem that std::enable_if and boost::enable_if are drop-in compatible.
回答1:
I'll answer my own question as I did get something working using boost::enable_if_c
(note that the drop-in replacement for std::enable_if
is boost::enable_if_c
, not 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
}
};
However I'm still very curious to know whether there is a better way to deal with this than resorting to Boost.
来源:https://stackoverflow.com/questions/14823832/c-how-to-handle-tr1-and-non-tr1-namespaces-in-portable-code