Macro-based selection of C++ headers
I am currently writing a C++ library which will be required to compile with both GCC for linux and Sun CC for Solaris. In the interest of performance I am creating some classes which select different headers based on compiler; GCC with c++0x or TR1 or with niether and Sun CC RogueWave or STLPort. I'm sturggling to work out the best means of #ifdef'ing the typedefs, for example:
namespace project {
#if defined(__GNUG__)
#if defined(HAVE_CXXOX)
#include <unorderd_map>
typedef srd::unordered_map map;
#elif defined(HAVE_TR1)
#include <tr1/unordered_map>
typedef std::tr1::unordered_map map;
#else
#include <map>
typedef std::map ma开发者_JAVA技巧p;
#endif
#elif defined(__SUNPROC_CC)
#include <map>
typedef std::map map;
#endif
} //namespaces
This won't work for two reasons:
- Headers must be included outside of the scope of
namespace project { ... }
. (If the header contains nothing but templates and inline functions, it might work anyway, but I wouldn't count on it.) typedef
doesn't work on templates. There's a workaround where you define an empty derived class.
So perhaps something like this:
#if defined(__GNUG__)
#if defined(HAVE_CXXOX)
#include <unordered_map>
#define MAP std::unordered_map
#elif defined(HAVE_TR1)
#include <tr1/unordered_map>
#define MAP std::tr1::unordered_map
#else
#include <map>
#define MAP std::map
#endif
#elif defined(__SUNPROC_CC)
#include <map>
#define MAP std::map
#endif
namespace myproject {
template <class K, class V>
class map : public MAP<K, V> {};
}
#undef MAP
After some further reading it might be worthwhile investigatinng the new 'template alias' concept in C++0x. here is some untested code as an example of how it would work.
#if defined(__GNUG__)
#if defined(HAVE_CXXOX)
#include <unordered_map>
#define MAP std::unordered_map
#elif defined(HAVE_TR1)
#include <tr1/unordered_map>
#define MAP std::tr1::unordered_map
#else
#include <map>
#define MAP std::map
#endif
#elif defined(__SUNPROC_CC)
#include <map>
#define MAP std::map
#endif
namespace internal {
template <typename K, typename V>
struct unordered_map { typedef MAP<K, V>; >;
} //internal
template <typename K, typename V>
using unordered_map = typename internal::unordered_map<K, V>::type;
See here for more information http://www2.research.att.com/~bs/C++0xFAQ.html#template-alias
Between the thornyard of defines and includes and the necessity to test for pretty much every single compiler not to mention minor versions and stuff (things like eg.: tuple_element
having another name in some specific versions of GCC), I took the sane approach: delegate the solution to the people who actually have the choice and power to implement it. That means either using a wrapper like Boost.TR1 or "do it yourself".
I code most stuff under the assumption that header paths are like in C++11, as in #include <header>
. The user knows his own environment better than I do and thus he'll be able to eg.: add Boost.TR1 to the include paths (or another TR1 implementation of his choosing) to make things work much more seamlessly. Heck, even just point to a folder with "redirect" headers that just forward to the <tr1/*>
versions if desired. Etcetera. Essentially, why go through macro nightmares yourself if developers have already made that sacrifice for you?
This has taken a lot of pains off my back because it allows me to write "write-once" code, without having to test for different compilers even in minor versions each time, and also helps make my code forwards-compatible to C++11.
Of course, there is some trickery to it though - depending on what can you assume about your compiler(s), you might need to use tricks like the "using namespace tr1
" hack or (better) an alternand others of its kind
精彩评论