From bd79b51000e2fe59368c93ff463adb59852ec6e7 Mon Sep 17 00:00:00 2001 From: Roger Leigh Date: Sun, 20 Jan 2013 18:50:49 +0000 Subject: [PATCH] AC_PROG_CXX: Add checks for C++11, C++98TR1 and C++98 These checks are the C++ equivalent of the existing C standards checks. * doc/autoconf.texi (C++ Compiler): Document new behavior. * lib/autoconf/c.m4 (AC_PROG_CXX): Try for C++11, falling back to C++98. (_AC_CXX_STD_TRY, _AC_CXX_CXX98_TEST_HEADER, _AC_CXX_CXX98_TEST_BODY) (_AC_CXX_CXX11_TEST_HEADER, _AC_CXX_CXX11_TEST_BODY) (_AC_PROG_CXX_CXX98, _AC_PROG_CXX_CXX11): New macros. --- doc/autoconf.texi | 30 ++++- lib/autoconf/c.m4 | 336 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 365 insertions(+), 1 deletion(-) diff --git a/doc/autoconf.texi b/doc/autoconf.texi index bb834437..2d6d8664 100644 --- a/doc/autoconf.texi +++ b/doc/autoconf.texi @@ -7642,6 +7642,35 @@ like this: AC_PROG_CXX([gcc cl KCC CC cxx cc++ xlC aCC c++ g++]) @end example +If necessary, add an option to output variable @code{CXX} to enable +support for ISO Standard C++ features with extensions. Prefer the +newest C++ standard that is supported. Currently the newest standard is +ISO C++11, with ISO C++98 being the previous standard. After calling +this macro you can check whether the C++ compiler has been set to accept +Standard C++; if not, the shell variable @code{ac_cv_prog_cxx_stdcxx} is +set to @samp{no}. If the C++ compiler will not accept C++11, the shell +variable @code{ac_cv_prog_cxx_cxx11} is set to @samp{no}, and if it will +not accept C++98, the shell variable @code{ac_cv_prog_cxx_cxx98} is set +to @samp{no}. + +When attempting to add compiler options, prefer extended functionality +to strict conformance: the goal is to enable whatever standard features +that are available, not to check for full conformance to the standard or +to prohibit incompatible extensions. Test for C++11 support by checking +for the language features @code{auto}, @code{constexpr}, +@code{decltype}, @code{default}ed and @code{delete}ed constructors, +delegate constructors, @code{final}, initializer lists, lambda +functions, @code{nullptr}, @code{override}, range-based for loops, +template brackets without spaces and unicode literals, and library +features @code{std::array}, @code{std::shared_ptr}, +@code{std::weak_ptr}, @code{std::regex} and @code{std::tuple}. Test for +C++98 support using basic features of the @code{std} namespace including +@code{std::string}, containers (@code{std::list}, @code{std::map}, +@code{std::set}, @code{std::vector}), streams (fstreams, iostreams, +stringstreams, iomanip), @code{std::pair}, exceptions (@code{try}, +@code{catch} and @code{std::runtime_error}) and algorithms. Tests for +more recent standards include all the tests for older standards. + If using the GNU C++ compiler, set shell variable @code{GXX} to @samp{yes}. If output variable @code{CXXFLAGS} was not already set, set it to @option{-g -O2} for the GNU C++ compiler (@option{-O2} on @@ -7649,7 +7678,6 @@ systems where G++ does not accept @option{-g}), or @option{-g} for other compilers. If your package does not like this default, then it is acceptable to insert the line @samp{: $@{CXXFLAGS=""@}} after @code{AC_INIT} and before @code{AC_PROG_CXX} to select an empty default instead. - @end defmac @defmac AC_PROG_CXXCPP diff --git a/lib/autoconf/c.m4 b/lib/autoconf/c.m4 index affd7658..6fe00fbd 100644 --- a/lib/autoconf/c.m4 +++ b/lib/autoconf/c.m4 @@ -749,6 +749,13 @@ else GXX= fi _AC_PROG_CXX_G +_AC_PROG_CXX_CXX11([ac_prog_cxx_stdcxx=cxx11 + ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx11 + ac_cv_prog_cxx_cxx98=$ac_cv_prog_cxx_cxx11], + [_AC_PROG_CXX_CXX98([ac_prog_cxx_stdcxx=cxx98 + ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx98], + [ac_prog_cxx_stdcxx=no + ac_cv_prog_cxx_stdcxx=no])]) AC_LANG_POP(C++)dnl ])# AC_PROG_CXX @@ -2158,3 +2165,332 @@ AC_DEFUN([AC_OPENMP], fi AC_SUBST([OPENMP_]_AC_LANG_PREFIX[FLAGS]) ]) + +# _AC_CXX_STD_TRY(STANDARD, TEST-PROLOGUE, TEST-BODY, OPTION-LIST, +# ACTION-IF-AVAILABLE, ACTION-IF-UNAVAILABLE) +# ---------------------------------------------------------------- +# Check whether the C++ compiler accepts features of STANDARD (e.g +# `cxx98', `cxx11') by trying to compile a program of TEST-PROLOGUE +# and TEST-BODY. If this fails, try again with each compiler option +# in the space-separated OPTION-LIST; if one helps, append it to CXX. +# If eventually successful, run ACTION-IF-AVAILABLE, else +# ACTION-IF-UNAVAILABLE. +AC_DEFUN([_AC_CXX_STD_TRY], +[AC_MSG_CHECKING([for $CXX option to enable ]m4_translit(m4_translit($1, [x], [+]), [a-z], [A-Z])[ features]) +AC_LANG_PUSH(C++)dnl +AC_CACHE_VAL(ac_cv_prog_cxx_$1, +[ac_cv_prog_cxx_$1=no +ac_save_CXX=$CXX +AC_LANG_CONFTEST([AC_LANG_PROGRAM([$2], [$3])]) +for ac_arg in '' $4 +do + CXX="$ac_save_CXX $ac_arg" + _AC_COMPILE_IFELSE([], [ac_cv_prog_cxx_$1=$ac_arg]) + test "x$ac_cv_prog_cxx_$1" != "xno" && break +done +rm -f conftest.$ac_ext +CXX=$ac_save_CXX +])# AC_CACHE_VAL +ac_prog_cxx_stdcxx_options= +case "x$ac_cv_prog_cxx_$1" in + x) + AC_MSG_RESULT([none needed]) ;; + xno) + AC_MSG_RESULT([unsupported]) ;; + *) + ac_prog_cxx_stdcxx_options=" $ac_cv_prog_cxx_$1" + CXX=$CXX$ac_prog_cxx_stdcxx_options + AC_MSG_RESULT([$ac_cv_prog_cxx_$1]) ;; +esac +AC_LANG_POP(C++)dnl +AS_IF([test "x$ac_cv_prog_cxx_$1" != xno], [$5], [$6]) +])# _AC_CXX_STD_TRY + +# _AC_CXX_CXX98_TEST_HEADER +# ------------------------- +# A C++ header suitable for testing for CXX98. +AC_DEFUN([_AC_CXX_CXX98_TEST_HEADER], +[[ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace test { + typedef std::vector string_vec; + typedef std::pair map_value; + typedef std::map map_type; + typedef std::set set_type; + + template + class printer { + public: + printer(std::ostringstream& os): os(os) {} + void operator() (T elem) { os << elem << std::endl; } + private: + std::ostringstream& os; + }; +} +]])# _AC_CXX_CXX98_TEST_HEADER + +# _AC_CXX_CXX98_TEST_BODY +# ----------------------- +# A C++ body suitable for testing for CXX98, assuming the corresponding header. +AC_DEFUN([_AC_CXX_CXX98_TEST_BODY], +[[ + +try { + // Basic string. + std::string teststr("ASCII text"); + teststr += " string"; + + // Simple vector. + test::string_vec testvec; + testvec.push_back(teststr); + testvec.push_back("foo"); + testvec.push_back("bar"); + if (testvec.size() != 3) { + throw std::runtime_error("Vector size is not 1"); + } + + // Dump vector into stringstream and obtain string. + std::ostringstream os; + for (test::string_vec::const_iterator i = testvec.begin(); + i != testvec.end(); ++i) { + if (i + 1 != testvec.end()) { + os << teststr << '\n'; + } + } + // Check algorithms work. + std::for_each(testvec.begin(), testvec.end(), test::printer(os)); + std::string os_out = os.str(); + + // Test pair and map. + test::map_type testmap; + testmap.insert(std::make_pair(std::string("key"), + std::make_pair(53,false))); + + // Test set. + int values[] = {9, 7, 13, 15, 4, 18, 12, 10, 5, 3, 14, 19, 17, 8, 6, 20, 16, 2, 11, 1}; + test::set_type testset(values, values + sizeof(values)/sizeof(values[0])); + std::list testlist(testset.begin(), testset.end()); + std::copy(testset.begin(), testset.end(), std::back_inserter(testlist)); +} catch (const std::exception& e) { + std::cerr << "Caught exception: " << e.what() << std::endl; + + // Test fstream + std::ofstream of("test.txt"); + of << "Test ASCII text\n" << std::flush; + of << "N= " << std::hex << std::setw(8) << std::left << 534 << std::endl; + of.close(); +} +std::exit(0); +]]) + +# _AC_CXX_CXX11_TEST_HEADER +# ------------------------- +# A C++ header suitable for testing for CXX11. +AC_DEFUN([_AC_CXX_CXX11_TEST_HEADER], +[[ +#include +#include +#include +#include +#include +#include +#include + +namespace cxx11test +{ + typedef std::shared_ptr sptr; + typedef std::weak_ptr wptr; + + typedef std::tuple tp; + typedef std::array int_array; + + constexpr int get_val() { return 20; } + + struct testinit + { + int i; + double d; + }; + + class delegate { + public: + delegate(int n) : n(n) {} + delegate(): delegate(2354) {} + + virtual int getval() { return this->n; }; + protected: + int n; + }; + + class overridden : public delegate { + public: + overridden(int n): delegate(n) {} + virtual int getval() override final { return this->n * 2; } + }; + + class nocopy { + public: + nocopy(int i): i(i) {} + nocopy() = default; + nocopy(const nocopy&) = delete; + nocopy & operator=(const nocopy&) = delete; + private: + int i; + }; +} +]])# _AC_CXX_CXX11_TEST_HEADER + +# _AC_CXX_CXX11_TEST_BODY +# ----------------------- +# A C++ body suitable for testing for CXX11, assuming the corresponding header. +AC_DEFUN([_AC_CXX_CXX11_TEST_BODY], +[[ +{ + // Test auto and decltype + std::deque d; + d.push_front(43); + d.push_front(484); + d.push_front(3); + d.push_front(844); + int total = 0; + for (auto i = d.begin(); i != d.end(); ++i) { total += *i; } + + auto a1 = 6538; + auto a2 = 48573953.4; + auto a3 = "String literal"; + + decltype(a2) a4 = 34895.034; +} +{ + // Test constexpr + short sa[cxx11test::get_val()] = { 0 }; +} +{ + // Test initialiser lists + cxx11test::testinit il = { 4323, 435234.23544 }; +} +{ + // Test range-based for and lambda + cxx11test::int_array array = {9, 7, 13, 15, 4, 18, 12, 10, 5, 3, 14, 19, 17, 8, 6, 20, 16, 2, 11, 1}; + for (int &x : array) { x += 23; } + std::for_each(array.begin(), array.end(), [](int v1){ std::cout << v1; }); +} +{ + using cxx11test::sptr; + using cxx11test::wptr; + + sptr sp(new std::string("ASCII string")); + wptr wp(sp); + sptr sp2(wp); +} +{ + cxx11test::tp tuple("test", 54, 45.53434); + double d = std::get<2>(tuple); + std::string s; + int i; + std::tie(s,i,d) = tuple; +} +{ + static std::regex filename_regex("^_?([a-z0-9_.]+-)+[a-z0-9]+$"); + std::string testmatch("Test if this string matches"); + bool match = std::regex_search(testmatch, filename_regex); +} +{ + cxx11test::int_array array = {9, 7, 13, 15, 4, 18, 12, 10, 5, 3, 14, 19, 17, 8, 6, 20, 16, 2, 11, 1}; + cxx11test::int_array::size_type size = array.size(); +} +{ + // Test constructor delegation + cxx11test::delegate d1; + cxx11test::delegate d2(); + cxx11test::delegate d3(45); +} +{ + // Test override and final + cxx11test::overridden o1(55464); +} +{ + // Test nullptr + char *c = nullptr; +} +{ + // Test template brackets + std::vector> v1; +} +{ + // Unicode literals + char *utf8 = u8"UTF-8 string \u2500"; + char16_t *utf16 = u"UTF-8 string \u2500"; + char32_t *utf32 = U"UTF-32 string \u2500"; +} +]]) + +# _AC_PROG_CXX_CXX98 ([ACTION-IF-AVAILABLE], [ACTION-IF-UNAVAILABLE]) +# ------------------------------------------------------------------- + +# If the C++ compiler is not in ISO C++98 mode by default, try to add +# an option to output variable CXX to make it so. This macro tries +# various options that select ISO C++98 on some system or another. It +# considers the compiler to be in ISO C++98 mode if it handles basic +# features of the std namespace including: string, containers (list, +# map, set, vector), streams (fstreams, iostreams, stringstreams, +# iomanip), pair, exceptions and algorithms. + + +AC_DEFUN([_AC_PROG_CXX_CXX98], +[_AC_CXX_STD_TRY([cxx98], +[_AC_CXX_CXX98_TEST_HEADER], +[_AC_CXX_CXX98_TEST_BODY], +dnl Try +dnl GCC -std=gnu++98 (unused restrictive mode: -std=c++98) +dnl IBM XL C -qlanglvl=extended +dnl HP aC++ -AA +dnl Intel ICC -std=gnu++98 +dnl Solaris N/A (default) +dnl Tru64 N/A (default, but -std gnu could be used) +dnl with extended modes being tried first. +[[-std=gnu++98 -std=c++98 -qlanglvl=extended -AA]], [$1], [$2])[]dnl +])# _AC_PROG_CXX_CXX98 + +# _AC_PROG_CXX_CXX11 ([ACTION-IF-AVAILABLE], [ACTION-IF-UNAVAILABLE]) +# ------------------------------------------------------------------- +# If the C++ compiler is not in ISO CXX11 mode by default, try to add +# an option to output variable CXX to make it so. This macro tries +# various options that select ISO C++11 on some system or another. It +# considers the compiler to be in ISO C++11 mode if it handles all the +# tests from the C++98 checks, plus the following: Language features +# (auto, constexpr, decltype, default/deleted constructors, delegate +# constructors, final, initialiser lists, lambda functions, nullptr, +# override, range-based for loops, template brackets without spaces, +# unicode literals) and library features (array, memory (shared_ptr, +# weak_ptr), regex and tuple types). +AC_DEFUN([_AC_PROG_CXX_CXX11], +[_AC_CXX_STD_TRY([cxx11], +[_AC_CXX_CXX11_TEST_HEADER +_AC_CXX_CXX98_TEST_HEADER], +[_AC_CXX_CXX11_TEST_BODY +_AC_CXX_CXX98_TEST_BODY], +dnl Try +dnl GCC -std=gnu++11 (unused restrictive mode: -std=c++11) [and 0x variants] +dnl IBM XL C -qlanglvl=extended0x +dnl (pre-V12.1; unused restrictive mode: -qlanglvl=stdcxx11) +dnl HP aC++ -AA +dnl Intel ICC -std=c++11 -std=c++0x +dnl Solaris N/A (no support) +dnl Tru64 N/A (no support) +dnl with extended modes being tried first. +[[-std=gnu++11 -std=c++11 -std=gnu++0x -std=c++0x -qlanglvl=extended0x -AA]], [$1], [$2])[]dnl +])# _AC_PROG_CXX_CXX11 -- 2.47.2