I think I've found myself in a situation where things are quickly spiraling out of control, and I'd like to get some feedback to select a way to proceed.
BACKGROUND
I'm working with a library based upon C++03. The library is cross-platform and supports multiple compilers. There are no external dependencies or prerequisites (i.e., no need for Autotools, Cmake, Boost, etc). We started testing with std=c++11
and found some things needed to be fixed. For example, auto_ptr
was deprecated, so we needed to switch to unique_ptr
if __cplusplus >= 201103L
.
The switch to unique_ptr
was relatively easy, except on OS X. OS X advertises __cplusplus
as 201103L
, but its a woefully deficient C++11 runtime library (I'm not even going to use the word "standard" in that sentence). Worse, if someone builds the latest LLVM/Clang from sources on OS X without libcxx, then the same deficiencies exist because the new Clang uses the old libraries.
To resolve the auto_ptr
issue, we jumped through some hoops that ended in the following code. While the code below looks clean, the preprocessor is working overtime to determine when it should to defined MYLIB_HAVE_UNIQUE_PTR
and MYLIB_HAVE_TEMPLATE_ALIAS
due to OS X.
// The result is MyLib::auto_ptr in both cases
#if defined(MYLIB_HAVE_UNIQUE_PTR) && defined(MYLIB_HAVE_TEMPLATE_ALIAS)
template<typename T>
using auto_ptr = std::unique_ptr<T>;
#else
using std::auto_ptr;
#endif
We are now trying to provide move semantics guarded on __cplusplus
because there are two to five classes that would benefit. However, OS X is giving us trouble again because it lacks std::move
. I suspect its going to have problems with Rvalues and Rvalue References, too.
We asked the Clang developers how to feature test for std::move
at How to test for std::move (intersection of Apple and Clang). One of the Clang developers suggested we roll our own, but I'm beginning to think its a losing battle based upon GNU's source code for move.h. For example a quick grep -IR is_lvalue_reference /usr/include/c++/4.2.1/
reveals no hits.
STRATEGIES
I think our choices are (1) drop support for OS X altogether; (2) treat OS X as C++03; (3) roll our own implementation for missing C++11 features on OS X; or (4) switch to Autotools or Cmake for feature detection.
(1) is categorically not going to happen. (2) seems reasonable. (3) seems like a lot of work. (4) is not going to happen because it introduces external dependencies.
For (2), I believe the test is as easy as:
// For Microsoft's cl.exe, pivot on _MSC_VER, not __cplusplus.
#if (_MSC_VER >= 1600) || (__cplusplus >= 201103L)
# define MYLIB_CXX11 1
#endif
// Ancient C++ runtime library; unset it because its too much trouble.
// Thanks to Jonathan Wakely for the `__has_include(<forward_list>)`
// test. See http://stackoverflow.com/q/31655462.
// TODO: test under Xcode 3, where g++ is really g++.
#if defined(__clang__)
# if !(__has_include(<forward_list>))
# undef MYLIB_CXX11
# endif
#endif
Then back to canonicalized feature detection:
#if MYLIB_CXX11
// Everyone appears to provide this list
#define MYLIB_CXX11_UNIQUE_PTR 1
...
// template aliases: MS at VS 2015 (v19.00); GCC at 4.7; Clang at 3.0; and Intel 12.1.
#if (_MSC_VER >= 1900) || (__INTEL_COMPILER >= 1210)
# define MYLIB_CXX11_TEMPLATE_ALIAS 1
#elif defined(__clang__)
# if (__has_feature(cxx_alias_templates))
# define MYLIB_CXX11_TEMPLATE_ALIAS 1
# endif
#elif (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))
# define MYLIB_CXX11_TEMPLATE_ALIAS 1
#endif // template aliases
#endif // MYLIB_CXX11
QUESTIONS
Are there any other strategies to deal with C++11 and OS X?
Has anyone tried strategy (3)? If so, how much work was it?
TESTING
If you have access to OS X, then try the following to test the built in compiler, the standard, and GNU's libstdc++
versus LLVM's libc++
. After that, then test with a modern compiler by exporting CXX=/opt/local/bin/clang++
.
$ cat test-clapple.cxx
// c++ -c test-clapple.cxx
// c++ -stdlib=libc++ -c test-clapple.cxx
// c++ -std=c++11 -c test-clapple.cxx
// c++ -std=c++11 -stdlib=libc++ -c test-clapple.cxx
# include <memory>
// Manage auto_ptr warnings and deprecation in C++11
#if (__cplusplus >= 201103L) || (_MSC_VER >= 1900)
template<typename T>
using auto_ptr = std::unique_ptr<T>;
#else
using std::auto_ptr;
#endif // C++11
int main(int argc, char* argv[])
{
return argc;
}