2

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.

Community
  • 1
  • 1
davidA
  • 12,528
  • 9
  • 64
  • 96
  • This question is a follow-on from http://stackoverflow.com/questions/14821846 – davidA Feb 12 '13 at 01:21
  • Got something working with boost/type_traits.hpp - but note that one needs to use boost::enable_if_c as a drop-in replacement for std::enable_if - or apparently one can remove the trailing ::value - see "More Info", section 2, http://www.gockelhut.com/c++/articles/has_member – davidA Feb 12 '13 at 01:36
  • Unfortunately it's swings-n-roundabouts here - boost/type_traits.hpp includes is_complex.hpp, which includes - which unfortunately doesn't exist in this particular (cut-down) compilation environment. Gah. Solved this with including and rather than - glad that the Boost creators split everything up so usefully - handy. – davidA Feb 12 '13 at 01:39
  • You do know there is [Boost.TR1](http://www.boost.org/doc/libs/1_53_0/doc/html/boost_tr1.html) I hope. – Benjamin Bannier Feb 12 '13 at 01:57
  • Yes, I did come across Boost.TR1, although I had trouble getting it to work due to the mismatch between std::enable_if and boost::enable_if_c. My understanding is that you can just #include boost/tr1.hpp, and it adds everything to std::, but that didn't seem to work. I'll try it again... – davidA Feb 12 '13 at 06:14
  • I just tried Boost.TR1 and the problem seems to be that it does not bring in std::tr1::enable_if (or std::enable_if for that matter). It *does* bring in std::tr1::is_enum, so that's useful, but it doesn't address my problem entirely. – davidA Feb 12 '13 at 20:40

1 Answers1

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.

davidA
  • 12,528
  • 9
  • 64
  • 96
  • What I did in my own library of C++11 backports - such as (precisely) `enable_if`, was to just implement (or `using`) everything right in namespace `std`. Then if I detect there is a tr1 namespace (or even if not, just in case), I open it and `using` it into namespace std, to have easy, forwards-compatible access to the tools. That only leaves the problem of stuff such as `hash<>` specializations, which would require the TR1 tools themselves to be implemented in namespace `std`. – Luis Machuca Sep 26 '13 at 17:48