Patch attached.  Probably not in its final form, but I hope
it will be useful as a basis for discussion.

This is like the existing C89/99/11 checks, but for C++.
In this patch, I've implemented three checks:

AC_PROG_CXX_CXX98
AC_PROG_CXX_CXX98TR1
AC_PROG_CXX_CXX11

and AC_PROG_CXX will try them in the order
  AC_PROG_CXX_CXX11, AC_PROG_CXX_CXX98TR1, AC_PROG_CXX_CXX98
in order to put the compiler in the highest available mode
by default.  Each one performs a set of language and/or
library tests for each standard version.

Note that like the C equivalents, these are marked deprecated
and are not usable in their own right.  I'm still of the opinion
that it's useful to restrict the compiler to a minimum standard
so that you can
- prevent the use of features you don't want to use
- ensure that the features you do want are present
So ideally, I'd like to re-enable the C and C++ standard-
specific checks, if the consensus is that these remain useful,
and also add AC_PROG_CC_C11, which is currently missing.

Other points for discussion:
- The flags for non-GCC compilers were obtained from online
  documentation.  They are untested, and may be wrong; they
  are certainly not complete since I don't use non-GCC
  compilers myself.  Fairly trivial to test and update if
  anyone has access to such compilers.
- The feature tests are restricted to those which pass with
  GCC 4.7.  Depending upon which other compilers we want to
  put into "C++11 mode", this test set may need reducing to
  the common set which are usable across different compilers
  and/or compiler versions.

I've not updated the manual yet; I'll do this if people are
generally happy with things.


Kind regards,
Roger

-- 
  .''`.  Roger Leigh
 : :' :  Debian GNU/Linux    http://people.debian.org/~rleigh/
 `. `'   schroot and sbuild  http://alioth.debian.org/projects/buildd-tools
   `-    GPG Public Key      F33D 281D 470A B443 6756 147C 07B3 C8BC 4083 E800
>From f673f2ef9802857421133a96c801882b8dba9b9c Mon Sep 17 00:00:00 2001
From: Roger Leigh <rle...@debian.org>
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.
---
 lib/autoconf/c.m4 |  446 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 446 insertions(+)

diff --git a/lib/autoconf/c.m4 b/lib/autoconf/c.m4
index affd765..7575ead 100644
--- a/lib/autoconf/c.m4
+++ b/lib/autoconf/c.m4
@@ -749,6 +749,18 @@ else
   GXX=
 fi
 _AC_PROG_CXX_G
+dnl
+dnl Set ac_prog_cc_stdc to the supported C version.
+dnl Also set the documented variable ac_cv_prog_cc_stdc;
+dnl its name was chosen when it was cached, but it is no longer cached.
+_AC_PROG_CXX_CXX11([ac_prog_cxx_stdcxx=c11
+		    ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx11],
+  [_AC_PROG_CXX_CXX98TR1([ac_prog_cxx_stdcxx=c99
+			  ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx98tr1],
+     [_AC_PROG_CXX_CXX98([ac_prog_cxx_stdcxx=c89
+			  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 +2170,437 @@ 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
+# `c++98', `c++11') 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_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
+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 <algorithm>
+#include <cstdlib>
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <list>
+#include <map>
+#include <set>
+#include <sstream>
+#include <stdexcept>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace test {
+  typedef std::vector<std::string> string_vec;
+  typedef std::pair<int,bool> map_value;
+  typedef std::map<std::string,map_value> map_type;
+  typedef std::set<int> set_type;
+
+  template<typename T>
+  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<std::string>(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<int> 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_CXX98TR1_TEST_HEADER
+# ----------------------------
+# A C++ header suitable for testing for CXX98TR1.
+AC_DEFUN([_AC_CXX_CXX98TR1_TEST_HEADER],
+[[
+#include <tr1/functional>
+#include <tr1/memory>
+#include <tr1/tuple>
+#include <tr1/array>
+#include <tr1/regex>
+
+namespace tr1test{
+  typedef std::tr1::shared_ptr<std::string> sptr;
+  typedef std::tr1::weak_ptr<std::string> wptr;
+
+  typedef std::tr1::tuple<std::string,int,double> tp;
+  typedef std::tr1::array<int, 20> int_array;
+}
+]])# _AC_CXX_CXX98TR1_TEST_HEADER
+
+# _AC_CXX_CXX98TR1_TEST_BODY
+# --------------------------
+# A C++ body suitable for testing for CXX98TR1, assuming the corresponding header.
+AC_DEFUN([_AC_CXX_CXX98TR1_TEST_BODY],
+[[
+{
+  using tr1test::sptr;
+  using tr1test::wptr;
+
+  sptr sp(new std::string("ASCII string"));
+  wptr wp(sp);
+  sptr sp2(wp);
+}
+{
+  tr1test::tp tuple("test", 54, 45.53434);
+  double d = std::tr1::get<2>(tuple);
+  std::string s;
+  int i;
+  std::tr1::tie(s,i,d) = tuple;
+}
+{
+  static std::tr1::regex filename_regex("^_?([a-z0-9_.]+-)+[a-z0-9]+$");
+  std::string testmatch("Test if this string matches");
+  bool match = std::tr1::regex_search(testmatch, filename_regex);
+}
+{
+  tr1test::int_array array = {9, 7, 13, 15, 4, 18, 12, 10, 5, 3, 14, 19, 17, 8, 6, 20, 16, 2, 11, 1};
+  tr1test::int_array::size_type size = array.size();
+}
+]])
+
+# _AC_CXX_CXX11_TEST_HEADER
+# -------------------------
+# A C++ header suitable for testing for CXX11.
+AC_DEFUN([_AC_CXX_CXX11_TEST_HEADER],
+[[
+#include <deque>
+#include <functional>
+#include <memory>
+#include <tuple>
+#include <array>
+#include <regex>
+#include <iostream>
+
+namespace cxx11test
+{
+  typedef std::shared_ptr<std::string> sptr;
+  typedef std::weak_ptr<std::string> wptr;
+
+  typedef std::tuple<std::string,int,double> tp;
+  typedef std::array<int, 20> 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<int> 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<std::pair<int,char*>> 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_CXX98TR1 ([ACTION-IF-AVAILABLE], [ACTION-IF-UNAVAILABLE])
+# ---------------------------------------------------------------------
+# If the C++ compiler is not in ISO C++98TR1 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++98TR1 on some system or
+# another.  It considers the compiler to be in ISO C++98TR1 mode if it
+# handles the all the C++98 tests, plus the following features of the
+# std::tr1 namespace: array, memory (shared_ptr, weak_ptr), regex and
+# tuple types.
+AC_DEFUN([_AC_PROG_CXX_CXX98TR1],
+[_AC_CXX_STD_TRY([cxx98tr1],
+[_AC_CXX_CXX98TR1_TEST_HEADER
+_AC_CXX_CXX98_TEST_HEADER],
+[_AC_CXX_CXX98TR1_TEST_BODY
+_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_CXX98TR1
+
+# _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 and C++09TR1 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_CXX98TR1_TEST_HEADER
+_AC_CXX_CXX98_TEST_HEADER],
+[_AC_CXX_CXX11_TEST_BODY
+_AC_CXX_CXX98TR1_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
+
+# AC_PROG_CXX_CXX98
+# -----------------
+AU_DEFUN([AC_PROG_CXX_CXX98],
+  [AC_REQUIRE([AC_PROG_CXX])],
+  [$0 is obsolete; use AC_PROG_CXX]
+)
+
+# AC_PROG_CXX_CXX98TR1
+# --------------------
+AU_DEFUN([AC_PROG_CXX_CXX98TR1],
+  [AC_REQUIRE([AC_PROG_CXX])],
+  [$0 is obsolete; use AC_PROG_CXX]
+)
+
+# AC_PROG_CXX_CXX11
+# -----------------
+AU_DEFUN([AC_PROG_CXX_CXX11],
+  [AC_REQUIRE([AC_PROG_CXX])],
+  [$0 is obsolete; use AC_PROG_CXX]
+)
+
+# AC_PROG_CXX_STDCXX
+# ------------------
+AU_DEFUN([AC_PROG_CXX_STDCXX],
+  [AC_REQUIRE([AC_PROG_CXX])],
+  [$0 is obsolete; use AC_PROG_CXX]
+)
-- 
1.7.10.4

_______________________________________________
Autoconf mailing list
Autoconf@gnu.org
https://lists.gnu.org/mailman/listinfo/autoconf

Reply via email to