add back C++ bindings
authorSven Verdoolaege <sven.verdoolaege@gmail.com>
Sat, 17 Feb 2018 13:32:04 +0000 (17 14:32 +0100)
committerSven Verdoolaege <sven.verdoolaege@gmail.com>
Sat, 17 Feb 2018 13:33:21 +0000 (17 14:33 +0100)
This reverts the previous commit.

Signed-off-by: Sven Verdoolaege <sven.verdoolaege@gmail.com>
12 files changed:
Makefile.am
configure.ac
interface/Makefile.am
interface/cpp.cc [new file with mode: 0644]
interface/cpp.h [new file with mode: 0644]
interface/extract_interface.cc
interface/isl-noexceptions.h.bot [new file with mode: 0644]
interface/isl-noexceptions.h.pre [new file with mode: 0644]
interface/isl-noexceptions.h.top [new file with mode: 0644]
interface/isl_test_cpp-noexceptions.cc [new file with mode: 0644]
m4/ax_cxx_compile_stdcxx.m4 [new file with mode: 0644]
m4/ax_cxx_compile_stdcxx_11.m4 [new file with mode: 0644]

index 4852c7e..2653115 100644 (file)
@@ -390,6 +390,9 @@ EXTRA_DIST = \
        imath/imrat.c \
        imath/imrat.h \
        interface/all.h \
+       interface/isl-noexceptions.h.top \
+       interface/isl-noexceptions.h.pre \
+       interface/isl-noexceptions.h.bot \
        interface/isl.py.top \
        interface/isl_test_python.py \
        test_inputs
index a5b83ea..2cb96fe 100644 (file)
@@ -25,6 +25,17 @@ AX_CC_MAXOPT
 AX_GCC_WARN_UNUSED_RESULT
 AX_C___ATTRIBUTE__
 
+# CXX11FLAGS contains the flags (if any) added by AX_CXX_COMPILE_STDCXX_11
+# Original state of CXX and CXXCPP is preserved because CXX11FLAGS
+# is only needed for compiling interface/isl_test_cpp
+AC_SUBST(CXX11FLAGS)
+ac_save_CXX="$CXX"
+ac_save_CXXCPP="$CXXCPP"
+AX_CXX_COMPILE_STDCXX_11([noext], [optional])
+CXX11FLAGS=${CXX#$ac_save_CXX}
+CXX="$ac_save_CXX"
+CXXCPP="$ac_save_CXXCPP"
+
 AC_PROG_GREP
 AC_PROG_LIBTOOL
 AC_PROG_SED
@@ -70,6 +81,7 @@ fi
 AM_CONDITIONAL(IMATH_FOR_MP, test x$with_int = ximath -o x$with_int = ximath-32)
 AM_CONDITIONAL(GMP_FOR_MP, test x$with_int = xgmp)
 
+AM_CONDITIONAL(HAVE_CXX11, test "x$HAVE_CXX11" = "x1")
 AM_CONDITIONAL(SMALL_INT_OPT, test "x$with_int" == "ximath-32")
 AS_IF([test "x$with_int" == "ximath-32"], [
        AC_DEFINE([USE_SMALL_INT_OPT], [], [Use small integer optimization])
index b61709a..9e67bfa 100644 (file)
@@ -2,6 +2,10 @@ AUTOMAKE_OPTIONS = nostdinc
 
 noinst_PROGRAMS = extract_interface
 TESTS =
+if HAVE_CXX11
+  noinst_PROGRAMS += isl_test_cpp-noexceptions
+  TESTS += isl_test_cpp-noexceptions
+endif
 if HAVE_PYTHON
   TESTS += isl_test_python.py
   isl_test_python.py: isl.py
@@ -22,6 +26,8 @@ extract_interface_SOURCES = \
        generator.cc \
        python.h \
        python.cc \
+       cpp.h \
+       cpp.cc \
        extract_interface.h \
        extract_interface.cc
 extract_interface_LDFLAGS = $(CLANG_LDFLAGS)
@@ -31,7 +37,16 @@ extract_interface_LDADD = \
        -lclangAnalysis -lclangAST -lclangLex -lclangBasic -lclangDriver \
        $(CLANG_LIBS) $(CLANG_LDFLAGS)
 
-CLEANFILES = isl.py
+isl_test_cpp_noexceptions_CPPFLAGS = $(includes) -I.
+isl_test_cpp_noexceptions_CXXFLAGS = @CXX11FLAGS@
+isl_test_cpp_noexceptions_SOURCES = \
+       isl_test_cpp-noexceptions.cc \
+       isl-noexceptions.h
+isl_test_cpp_noexceptions_LDFLAGS = @MP_LDFLAGS@
+isl_test_cpp_noexceptions_LDADD = ../libisl.la @MP_LIBS@
+
+BUILT_SOURCES = isl-noexceptions.h
+CLEANFILES = isl.py isl-noexceptions.h
 
 # dummy library that captures the dependencies on all headers
 # that are relevant for the bindings
@@ -45,5 +60,14 @@ isl.py: extract_interface libdep.a isl.py.top
                        $(srcdir)/all.h) \
                        > isl.py
 
-dist-hook: isl.py
-       cp isl.py $(distdir)/
+isl-noexceptions.h: extract_interface libdep.a isl-noexceptions.h.top \
+               isl-noexceptions.h.pre isl-noexceptions.h.bot
+       (cat $(srcdir)/isl-noexceptions.h.top $(srcdir)/all.h \
+           $(srcdir)/isl-noexceptions.h.pre; \
+               ./extract_interface$(EXEEXT) --language=cpp $(includes) \
+                       $(srcdir)/all.h; \
+               cat $(srcdir)/isl-noexceptions.h.bot) \
+                       > isl-noexceptions.h
+
+dist-hook: isl.py isl-noexceptions.h
+       cp isl.py isl-noexceptions.h $(distdir)/
diff --git a/interface/cpp.cc b/interface/cpp.cc
new file mode 100644 (file)
index 0000000..b8a62b8
--- /dev/null
@@ -0,0 +1,1124 @@
+/*
+ * Copyright 2016, 2017 Tobias Grosser. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *    2. Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOBIAS GROSSER ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SVEN VERDOOLAEGE OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation
+ * are those of the authors and should not be interpreted as
+ * representing official policies, either expressed or implied, of
+ * Tobias Grosser.
+ */
+
+#include <cstdarg>
+#include <cstdio>
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "cpp.h"
+#include "isl_config.h"
+
+/* Print string formatted according to "fmt" to ostream "os".
+ *
+ * This osprintf method allows us to use printf style formatting constructs when
+ * writing to an ostream.
+ */
+static void osprintf(ostream &os, const char *format, ...)
+{
+       va_list arguments;
+       char *string_pointer;
+       size_t size;
+
+       va_start(arguments, format);
+       size = vsnprintf(NULL, 0, format, arguments);
+       string_pointer = new char[size + 1];
+       va_end(arguments);
+       va_start(arguments, format);
+       vsnprintf(string_pointer, size + 1, format, arguments);
+       va_end(arguments);
+       os << string_pointer;
+       delete[] string_pointer;
+}
+
+/* Convert "l" to a string.
+ */
+static std::string to_string(long l)
+{
+       std::ostringstream strm;
+       strm << l;
+       return strm.str();
+}
+
+/* Generate a cpp interface based on the extracted types and functions.
+ *
+ * Print first a set of forward declarations for all isl wrapper
+ * classes, then the declarations of the classes, and at the end all
+ * implementations.
+ */
+void cpp_generator::generate()
+{
+       ostream &os = cout;
+
+       osprintf(os, "\n");
+       osprintf(os, "namespace isl {\n\n");
+       osprintf(os, "inline namespace noexceptions {\n\n");
+
+       print_forward_declarations(os);
+       osprintf(os, "\n");
+       print_declarations(os);
+       osprintf(os, "\n");
+       print_implementations(os);
+
+       osprintf(os, "} // namespace noexceptions\n");
+       osprintf(os, "} // namespace isl\n");
+}
+
+/* Print forward declarations for all classes to "os".
+*/
+void cpp_generator::print_forward_declarations(ostream &os)
+{
+       map<string, isl_class>::iterator ci;
+
+       osprintf(os, "// forward declarations\n");
+
+       for (ci = classes.begin(); ci != classes.end(); ++ci)
+               print_class_forward_decl(os, ci->second);
+}
+
+/* Print all declarations to "os".
+ */
+void cpp_generator::print_declarations(ostream &os)
+{
+       map<string, isl_class>::iterator ci;
+       bool first = true;
+
+       for (ci = classes.begin(); ci != classes.end(); ++ci) {
+               if (first)
+                       first = false;
+               else
+                       osprintf(os, "\n");
+
+               print_class(os, ci->second);
+       }
+}
+
+/* Print all implementations to "os".
+ */
+void cpp_generator::print_implementations(ostream &os)
+{
+       map<string, isl_class>::iterator ci;
+       bool first = true;
+
+       for (ci = classes.begin(); ci != classes.end(); ++ci) {
+               if (first)
+                       first = false;
+               else
+                       osprintf(os, "\n");
+
+               print_class_impl(os, ci->second);
+       }
+}
+
+/* Print declarations for class "clazz" to "os".
+ */
+void cpp_generator::print_class(ostream &os, const isl_class &clazz)
+{
+       const char *name = clazz.name.c_str();
+       std::string cppstring = type2cpp(clazz);
+       const char *cppname = cppstring.c_str();
+
+       osprintf(os, "// declarations for isl::%s\n", cppname);
+
+       print_class_factory_decl(os, clazz);
+       osprintf(os, "\n");
+       osprintf(os, "class %s {\n", cppname);
+       print_class_factory_decl(os, clazz, "  friend ");
+       osprintf(os, "\n");
+       osprintf(os, "  %s *ptr = nullptr;\n", name);
+       osprintf(os, "\n");
+       print_private_constructors_decl(os, clazz);
+       osprintf(os, "\n");
+       osprintf(os, "public:\n");
+       print_public_constructors_decl(os, clazz);
+       print_constructors_decl(os, clazz);
+       print_copy_assignment_decl(os, clazz);
+       print_destructor_decl(os, clazz);
+       print_ptr_decl(os, clazz);
+       print_get_ctx_decl(os);
+       osprintf(os, "\n");
+       print_methods_decl(os, clazz);
+
+       osprintf(os, "};\n");
+}
+
+/* Print forward declaration of class "clazz" to "os".
+ */
+void cpp_generator::print_class_forward_decl(ostream &os,
+       const isl_class &clazz)
+{
+       std::string cppstring = type2cpp(clazz);
+       const char *cppname = cppstring.c_str();
+
+       osprintf(os, "class %s;\n", cppname);
+}
+
+/* Print global factory functions to "os".
+ *
+ * Each class has two global factory functions:
+ *
+ *     isl::set manage(__isl_take isl_set *ptr);
+ *     isl::set manage_copy(__isl_keep isl_set *ptr);
+ *
+ * A user can construct isl C++ objects from a raw pointer and indicate whether
+ * they intend to take the ownership of the object or not through these global
+ * factory functions. This ensures isl object creation is very explicit and
+ * pointers are not converted by accident. Thanks to overloading, manage() and
+ * manage_copy() can be called on any isl raw pointer and the corresponding
+ * object is automatically created, without the user having to choose the right
+ * isl object type.
+ */
+void cpp_generator::print_class_factory_decl(ostream &os,
+       const isl_class &clazz, const std::string &prefix)
+{
+       const char *name = clazz.name.c_str();
+       std::string cppstring = type2cpp(clazz);
+       const char *cppname = cppstring.c_str();
+
+       os << prefix;
+       osprintf(os, "inline isl::%s manage(__isl_take %s *ptr);\n", cppname,
+                name);
+       os << prefix;
+       osprintf(os, "inline isl::%s manage_copy(__isl_keep %s *ptr);\n",
+               cppname, name);
+}
+
+/* Print declarations of private constructors for class "clazz" to "os".
+ *
+ * Each class has currently one private constructor:
+ *
+ *     1) Constructor from a plain isl_* C pointer
+ *
+ * Example:
+ *
+ *     set(__isl_take isl_set *ptr);
+ *
+ * The raw pointer constructor is kept private. Object creation is only
+ * possible through isl::manage() or isl::manage_copy().
+ */
+void cpp_generator::print_private_constructors_decl(ostream &os,
+       const isl_class &clazz)
+{
+       const char *name = clazz.name.c_str();
+       std::string cppstring = type2cpp(clazz);
+       const char *cppname = cppstring.c_str();
+
+       osprintf(os, "  inline explicit %s(__isl_take %s *ptr);\n", cppname,
+                name);
+}
+
+/* Print declarations of public constructors for class "clazz" to "os".
+ *
+ * Each class currently has two public constructors:
+ *
+ *     1) A default constructor
+ *     2) A copy constructor
+ *
+ * Example:
+ *
+ *     set();
+ *     set(const isl::set &set);
+ */
+void cpp_generator::print_public_constructors_decl(ostream &os,
+       const isl_class &clazz)
+{
+       std::string cppstring = type2cpp(clazz);
+       const char *cppname = cppstring.c_str();
+       osprintf(os, "  inline /* implicit */ %s();\n", cppname);
+
+       osprintf(os, "  inline /* implicit */ %s(const isl::%s &obj);\n",
+                cppname, cppname);
+}
+
+/* Print declarations for constructors for class "class" to "os".
+ *
+ * For each isl function that is marked as __isl_constructor,
+ * add a corresponding C++ constructor.
+ *
+ * Example:
+ *
+ *     inline /\* implicit *\/ union_set(isl::basic_set bset);
+ *     inline /\* implicit *\/ union_set(isl::set set);
+ *     inline explicit val(isl::ctx ctx, long i);
+ *     inline explicit val(isl::ctx ctx, const std::string &str);
+ */
+void cpp_generator::print_constructors_decl(ostream &os,
+       const isl_class &clazz)
+{
+       set<FunctionDecl *>::const_iterator in;
+       const set<FunctionDecl *> &constructors = clazz.constructors;
+
+       for (in = constructors.begin(); in != constructors.end(); ++in) {
+               FunctionDecl *cons = *in;
+               string fullname = cons->getName();
+               function_kind kind = function_kind_constructor;
+
+               print_method_decl(os, clazz, fullname, cons, kind);
+       }
+}
+
+/* Print declarations of copy assignment operator for class "clazz"
+ * to "os".
+ *
+ * Each class has one assignment operator.
+ *
+ *     isl:set &set::operator=(isl::set obj)
+ *
+ */
+void cpp_generator::print_copy_assignment_decl(ostream &os,
+       const isl_class &clazz)
+{
+       std::string cppstring = type2cpp(clazz);
+       const char *cppname = cppstring.c_str();
+
+       osprintf(os, "  inline isl::%s &operator=(isl::%s obj);\n", cppname,
+                cppname);
+}
+
+/* Print declaration of destructor for class "clazz" to "os".
+ */
+void cpp_generator::print_destructor_decl(ostream &os, const isl_class &clazz)
+{
+       std::string cppstring = type2cpp(clazz);
+       const char *cppname = cppstring.c_str();
+
+       osprintf(os, "  inline ~%s();\n", cppname);
+}
+
+/* Print declaration of pointer functions for class "clazz" to "os".
+ *
+ * To obtain a raw pointer three functions are provided:
+ *
+ *     1) __isl_give isl_set *copy()
+ *
+ *       Returns a pointer to a _copy_ of the internal object
+ *
+ *     2) __isl_keep isl_set *get()
+ *
+ *       Returns a pointer to the internal object
+ *
+ *     3) __isl_give isl_set *release()
+ *
+ *       Returns a pointer to the internal object and resets the
+ *       internal pointer to nullptr.
+ *
+ * We also provide functionality to explicitly check if a pointer is
+ * currently managed by this object.
+ *
+ *     4) bool is_null()
+ *
+ *       Check if the current object is a null pointer.
+ *
+ * The functions get() and release() model the value_ptr proposed in
+ * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3339.pdf.
+ * The copy() function is an extension to allow the user to explicitly
+ * copy the underlying object.
+ *
+ * Also generate a declaration to delete copy() for r-values, for
+ * r-values release() should be used to avoid unnecessary copies.
+ */
+void cpp_generator::print_ptr_decl(ostream &os, const isl_class &clazz)
+{
+       const char *name = clazz.name.c_str();
+
+       osprintf(os, "  inline __isl_give %s *copy() const &;\n", name);
+       osprintf(os, "  inline __isl_give %s *copy() && = delete;\n", name);
+       osprintf(os, "  inline __isl_keep %s *get() const;\n", name);
+       osprintf(os, "  inline __isl_give %s *release();\n", name);
+       osprintf(os, "  inline bool is_null() const;\n");
+}
+
+/* Print the declaration of the get_ctx method.
+ */
+void cpp_generator::print_get_ctx_decl(ostream &os)
+{
+       osprintf(os, "  inline isl::ctx get_ctx() const;\n");
+}
+
+/* Print declarations for methods in class "clazz" to "os".
+ */
+void cpp_generator::print_methods_decl(ostream &os, const isl_class &clazz)
+{
+       map<string, set<FunctionDecl *> >::const_iterator it;
+
+       for (it = clazz.methods.begin(); it != clazz.methods.end(); ++it)
+               print_method_group_decl(os, clazz, it->first, it->second);
+}
+
+/* Print declarations for methods "methods" of name "fullname" in class "clazz"
+ * to "os".
+ *
+ * "fullname" is the name of the generated C++ method.  It commonly corresponds
+ * to the isl name, with the object type prefix dropped.
+ * In case of overloaded methods, the result type suffix has also been removed.
+ */
+void cpp_generator::print_method_group_decl(ostream &os, const isl_class &clazz,
+       const string &fullname, const set<FunctionDecl *> &methods)
+{
+       set<FunctionDecl *>::const_iterator it;
+
+       for (it = methods.begin(); it != methods.end(); ++it) {
+               function_kind kind = get_method_kind(clazz, *it);
+               print_method_decl(os, clazz, fullname, *it, kind);
+       }
+}
+
+/* Print declarations for "method" in class "clazz" to "os".
+ *
+ * "fullname" is the name of the generated C++ method.  It commonly corresponds
+ * to the isl name, with the object type prefix dropped.
+ * In case of overloaded methods, the result type suffix has also been removed.
+ *
+ * "kind" specifies the kind of method that should be generated.
+ */
+void cpp_generator::print_method_decl(ostream &os, const isl_class &clazz,
+       const string &fullname, FunctionDecl *method, function_kind kind)
+{
+       print_method_header(os, clazz, method, fullname, true, kind);
+}
+
+/* Print implementations for class "clazz" to "os".
+ */
+void cpp_generator::print_class_impl(ostream &os, const isl_class &clazz)
+{
+       std::string cppstring = type2cpp(clazz);
+       const char *cppname = cppstring.c_str();
+
+       osprintf(os, "// implementations for isl::%s\n", cppname);
+
+       print_class_factory_impl(os, clazz);
+       osprintf(os, "\n");
+       print_public_constructors_impl(os, clazz);
+       osprintf(os, "\n");
+       print_private_constructors_impl(os, clazz);
+       osprintf(os, "\n");
+       print_constructors_impl(os, clazz);
+       osprintf(os, "\n");
+       print_copy_assignment_impl(os, clazz);
+       osprintf(os, "\n");
+       print_destructor_impl(os, clazz);
+       osprintf(os, "\n");
+       print_ptr_impl(os, clazz);
+       osprintf(os, "\n");
+       print_get_ctx_impl(os, clazz);
+       osprintf(os, "\n");
+       print_methods_impl(os, clazz);
+}
+
+/* Print implementation of global factory functions to "os".
+ */
+void cpp_generator::print_class_factory_impl(ostream &os,
+       const isl_class &clazz)
+{
+       const char *name = clazz.name.c_str();
+       std::string cppstring = type2cpp(clazz);
+       const char *cppname = cppstring.c_str();
+
+       osprintf(os, "isl::%s manage(__isl_take %s *ptr) {\n", cppname, name);
+       osprintf(os, "  return %s(ptr);\n", cppname);
+       osprintf(os, "}\n");
+
+       osprintf(os, "isl::%s manage_copy(__isl_keep %s *ptr) {\n", cppname,
+               name);
+       osprintf(os, "  return %s(%s_copy(ptr));\n", cppname, name);
+       osprintf(os, "}\n");
+}
+
+/* Print implementations of private constructors for class "clazz" to "os".
+ */
+void cpp_generator::print_private_constructors_impl(ostream &os,
+       const isl_class &clazz)
+{
+       const char *name = clazz.name.c_str();
+       std::string cppstring = type2cpp(clazz);
+       const char *cppname = cppstring.c_str();
+
+       osprintf(os, "%s::%s(__isl_take %s *ptr)\n    : ptr(ptr) {}\n",
+                cppname, cppname, name);
+}
+
+/* Print implementations of public constructors for class "clazz" to "os".
+ */
+void cpp_generator::print_public_constructors_impl(ostream &os,
+       const isl_class &clazz)
+{
+       std::string cppstring = type2cpp(clazz);
+       const char *cppname = cppstring.c_str();
+
+       osprintf(os, "%s::%s()\n    : ptr(nullptr) {}\n\n", cppname, cppname);
+       osprintf(os, "%s::%s(const isl::%s &obj)\n    : ptr(obj.copy()) {}\n",
+                cppname, cppname, cppname);
+}
+
+/* Print implementations of constructors for class "clazz" to "os".
+ */
+void cpp_generator::print_constructors_impl(ostream &os,
+       const isl_class &clazz)
+{
+       set<FunctionDecl *>::const_iterator in;
+       const set<FunctionDecl *> constructors = clazz.constructors;
+
+       for (in = constructors.begin(); in != constructors.end(); ++in) {
+               FunctionDecl *cons = *in;
+               string fullname = cons->getName();
+               function_kind kind = function_kind_constructor;
+
+               print_method_impl(os, clazz, fullname, cons, kind);
+       }
+}
+
+/* Print implementation of copy assignment operator for class "clazz" to "os".
+ */
+void cpp_generator::print_copy_assignment_impl(ostream &os,
+       const isl_class &clazz)
+{
+       const char *name = clazz.name.c_str();
+       std::string cppstring = type2cpp(clazz);
+       const char *cppname = cppstring.c_str();
+
+       osprintf(os, "%s &%s::operator=(isl::%s obj) {\n", cppname,
+                cppname, cppname);
+       osprintf(os, "  std::swap(this->ptr, obj.ptr);\n", name);
+       osprintf(os, "  return *this;\n");
+       osprintf(os, "}\n");
+}
+
+/* Print implementation of destructor for class "clazz" to "os".
+ */
+void cpp_generator::print_destructor_impl(ostream &os,
+       const isl_class &clazz)
+{
+       const char *name = clazz.name.c_str();
+       std::string cppstring = type2cpp(clazz);
+       const char *cppname = cppstring.c_str();
+
+       osprintf(os, "%s::~%s() {\n", cppname, cppname);
+       osprintf(os, "  if (ptr)\n");
+       osprintf(os, "    %s_free(ptr);\n", name);
+       osprintf(os, "}\n");
+}
+
+/* Print implementation of ptr() functions for class "clazz" to "os".
+ */
+void cpp_generator::print_ptr_impl(ostream &os, const isl_class &clazz)
+{
+       const char *name = clazz.name.c_str();
+       std::string cppstring = type2cpp(clazz);
+       const char *cppname = cppstring.c_str();
+
+       osprintf(os, "__isl_give %s *%s::copy() const & {\n", name, cppname);
+       osprintf(os, "  return %s_copy(ptr);\n", name);
+       osprintf(os, "}\n\n");
+       osprintf(os, "__isl_keep %s *%s::get() const {\n", name, cppname);
+       osprintf(os, "  return ptr;\n");
+       osprintf(os, "}\n\n");
+       osprintf(os, "__isl_give %s *%s::release() {\n", name, cppname);
+       osprintf(os, "  %s *tmp = ptr;\n", name);
+       osprintf(os, "  ptr = nullptr;\n");
+       osprintf(os, "  return tmp;\n");
+       osprintf(os, "}\n\n");
+       osprintf(os, "bool %s::is_null() const {\n", cppname);
+       osprintf(os, "  return ptr == nullptr;\n");
+       osprintf(os, "}\n");
+}
+
+/* Print the implementation of the get_ctx method.
+ */
+void cpp_generator::print_get_ctx_impl(ostream &os, const isl_class &clazz)
+{
+       const char *name = clazz.name.c_str();
+       std::string cppstring = type2cpp(clazz);
+       const char *cppname = cppstring.c_str();
+
+       osprintf(os, "isl::ctx %s::get_ctx() const {\n", cppname);
+       osprintf(os, "  return isl::ctx(%s_get_ctx(ptr));\n", name);
+       osprintf(os, "}\n");
+}
+
+/* Print definitions for methods of class "clazz" to "os".
+ */
+void cpp_generator::print_methods_impl(ostream &os, const isl_class &clazz)
+{
+       map<string, set<FunctionDecl *> >::const_iterator it;
+       bool first = true;
+
+       for (it = clazz.methods.begin(); it != clazz.methods.end(); ++it) {
+               if (first)
+                       first = false;
+               else
+                       osprintf(os, "\n");
+               print_method_group_impl(os, clazz, it->first, it->second);
+       }
+}
+
+/* Print definitions for methods "methods" of name "fullname" in class "clazz"
+ * to "os".
+ *
+ * "fullname" is the name of the generated C++ method.  It commonly corresponds
+ * to the isl name, with the object type prefix dropped.
+ * In case of overloaded methods, the result type suffix has also been removed.
+ *
+ * "kind" specifies the kind of method that should be generated.
+ */
+void cpp_generator::print_method_group_impl(ostream &os, const isl_class &clazz,
+       const string &fullname, const set<FunctionDecl *> &methods)
+{
+       set<FunctionDecl *>::const_iterator it;
+       bool first = true;
+
+       for (it = methods.begin(); it != methods.end(); ++it) {
+               function_kind kind;
+               if (first)
+                       first = false;
+               else
+                       osprintf(os, "\n");
+               kind = get_method_kind(clazz, *it);
+               print_method_impl(os, clazz, fullname, *it, kind);
+       }
+}
+
+/* Print the use of "param" to "os".
+ *
+ * "load_from_this_ptr" specifies whether the parameter should be loaded from
+ * the this-ptr.  In case a value is loaded from a this pointer, the original
+ * value must be preserved and must consequently be copied.  Values that are
+ * loaded from parameters do not need to be preserved, as such values will
+ * already be copies of the actual parameters.  It is consequently possible
+ * to directly take the pointer from these values, which saves
+ * an unnecessary copy.
+ *
+ * In case the parameter is a callback function, two parameters get printed,
+ * a wrapper for the callback function and a pointer to the actual
+ * callback function.  The wrapper is expected to be available
+ * in a previously declared variable <name>_lambda, while
+ * the actual callback function is expected in <name>_p.
+ * The caller of this function must ensure that these variables exist.
+ */
+void cpp_generator::print_method_param_use(ostream &os, ParmVarDecl *param,
+       bool load_from_this_ptr)
+{
+       string name = param->getName().str();
+       const char *name_str = name.c_str();
+       QualType type = param->getOriginalType();
+
+       if (type->isIntegerType()) {
+               osprintf(os, "%s", name_str);
+               return;
+       }
+
+       if (is_string(type)) {
+               osprintf(os, "%s.c_str()", name_str);
+               return;
+       }
+
+       if (is_callback(type)) {
+               osprintf(os, "%s_lambda, ", name_str);
+               osprintf(os, "&%s_p", name_str);
+               return;
+       }
+
+       if (!load_from_this_ptr && !is_callback(type))
+               osprintf(os, "%s.", name_str);
+
+       if (keeps(param)) {
+               osprintf(os, "get()");
+       } else {
+               if (load_from_this_ptr)
+                       osprintf(os, "copy()");
+               else
+                       osprintf(os, "release()");
+       }
+}
+
+/* Print definition for "method" in class "clazz" to "os".
+ *
+ * "fullname" is the name of the generated C++ method.  It commonly corresponds
+ * to the isl name, with the object type prefix dropped.
+ * In case of overloaded methods, the result type suffix has also been removed.
+ *
+ * "kind" specifies the kind of method that should be generated.
+ *
+ * This method distinguishes three kinds of methods: member methods, static
+ * methods, and constructors.
+ *
+ * Member methods call "method" by passing to the underlying isl function the
+ * isl object belonging to "this" as first argument and the remaining arguments
+ * as subsequent arguments. The result of the isl function is returned as a new
+ * object if the underlying isl function returns an isl_* ptr or an isl_bool
+ * value, as std::string if the isl function returns 'const char *', and as
+ * unmodified return value otherwise.
+ *
+ * Static methods call "method" by passing all arguments to the underlying isl
+ * function, as no this-pointer is available. The result is a newly managed
+ * isl C++ object.
+ *
+ * Constructors create a new object from a given set of input parameters. They
+ * do not return a value, but instead update the pointer stored inside the
+ * newly created object.
+ *
+ * If the method has a callback argument, we reduce the number of parameters
+ * that are exposed by one to hide the user pointer from the interface. On
+ * the C++ side no user pointer is needed, as arguments can be forwarded
+ * as part of the std::function argument which specifies the callback function.
+ */
+void cpp_generator::print_method_impl(ostream &os, const isl_class &clazz,
+       const string &fullname, FunctionDecl *method, function_kind kind)
+{
+       string methodname = method->getName();
+       int num_params = method->getNumParams();
+       QualType return_type = method->getReturnType();
+       string rettype_str = type2cpp(return_type);
+       bool has_callback = false;
+
+       print_method_header(os, clazz, method, fullname, false, kind);
+
+       for (int i = 0; i < num_params; ++i) {
+               ParmVarDecl *param = method->getParamDecl(i);
+               if (is_callback(param->getType())) {
+                       has_callback = true;
+                       num_params -= 1;
+                       print_callback_local(os, param);
+               }
+       }
+
+       osprintf(os, "  auto res = %s(", methodname.c_str());
+
+       for (int i = 0; i < num_params; ++i) {
+               ParmVarDecl *param = method->getParamDecl(i);
+               bool load_from_this_ptr = false;
+
+               if (i == 0 && kind == function_kind_member_method)
+                       load_from_this_ptr = true;
+
+               print_method_param_use(os, param, load_from_this_ptr);
+
+               if (i != num_params - 1)
+                       osprintf(os, ", ");
+       }
+       osprintf(os, ");\n");
+
+       if (kind == function_kind_constructor) {
+               osprintf(os, "  ptr = res;\n");
+       } else if (is_isl_type(return_type) || is_isl_bool(return_type)) {
+               osprintf(os, "  return manage(res);\n");
+       } else if (has_callback) {
+               osprintf(os, "  return %s(res);\n", rettype_str.c_str());
+       } else if (is_string(return_type)) {
+               osprintf(os, "  std::string tmp(res);\n");
+               if (gives(method))
+                       osprintf(os, "  free(res);\n");
+               osprintf(os, "  return tmp;\n");
+       } else {
+               osprintf(os, "  return res;\n");
+       }
+
+       osprintf(os, "}\n");
+}
+
+/* Print the header for "method" in class "clazz" to "os".
+ *
+ * Print the header of a declaration if "is_declaration" is set, otherwise print
+ * the header of a method definition.
+ *
+ * "fullname" is the name of the generated C++ method.  It commonly corresponds
+ * to the isl name, with the object type prefix dropped.
+ * In case of overloaded methods, the result type suffix has also been removed.
+ *
+ * "kind" specifies the kind of method that should be generated.
+ *
+ * This function prints headers for member methods, static methods, and
+ * constructors, either for their declaration or definition.
+ *
+ * Member functions are declared as "const", as they do not change the current
+ * object, but instead create a new object. They always retrieve the first
+ * parameter of the original isl function from the this-pointer of the object,
+ * such that only starting at the second parameter the parameters of the
+ * original function become part of the method's interface.
+ *
+ * A function
+ *
+ *     __isl_give isl_set *isl_set_intersect(__isl_take isl_set *s1,
+ *             __isl_take isl_set *s2);
+ *
+ * is translated into:
+ *
+ *     inline isl::set intersect(isl::set set2) const;
+ *
+ * For static functions and constructors all parameters of the original isl
+ * function are exposed.
+ *
+ * Parameters that are defined as __isl_keep or are of type string, are passed
+ * as const reference, which allows the compiler to optimize the parameter
+ * transfer.
+ *
+ * Constructors are marked as explicit using the C++ keyword 'explicit' or as
+ * implicit using a comment in place of the explicit keyword. By annotating
+ * implicit constructors with a comment, users of the interface are made
+ * aware of the potential danger that implicit construction is possible
+ * for these constructors, whereas without a comment not every user would
+ * know that implicit construction is allowed in absence of an explicit keyword.
+ */
+void cpp_generator::print_method_header(ostream &os, const isl_class &clazz,
+       FunctionDecl *method, const string &fullname, bool is_declaration,
+       function_kind kind)
+{
+       string cname = fullname.substr(clazz.name.length() + 1);
+       string rettype_str = type2cpp(method->getReturnType());
+       string classname = type2cpp(clazz);
+       int num_params = method->getNumParams();
+       int first_param = 0;
+
+       cname = rename_method(cname);
+       if (kind == function_kind_member_method)
+               first_param = 1;
+
+       if (is_declaration) {
+               osprintf(os, "  ");
+
+               if (kind == function_kind_static_method)
+                       osprintf(os, "static ");
+
+               osprintf(os, "inline ");
+
+               if (kind == function_kind_constructor) {
+                       if (is_implicit_conversion(clazz, method))
+                               osprintf(os, "/* implicit */ ");
+                       else
+                               osprintf(os, "explicit ");
+               }
+       }
+
+       if (kind != function_kind_constructor)
+               osprintf(os, "%s ", rettype_str.c_str());
+
+       if (!is_declaration)
+               osprintf(os, "%s::", classname.c_str());
+
+       if (kind != function_kind_constructor)
+               osprintf(os, "%s", cname.c_str());
+       else
+               osprintf(os, "%s", classname.c_str());
+
+       osprintf(os, "(");
+
+       for (int i = first_param; i < num_params; ++i) {
+               ParmVarDecl *param = method->getParamDecl(i);
+               QualType type = param->getOriginalType();
+               string cpptype = type2cpp(type);
+
+               if (is_callback(type))
+                       num_params--;
+
+               if (keeps(param) || is_string(type) || is_callback(type))
+                       osprintf(os, "const %s &%s", cpptype.c_str(),
+                                param->getName().str().c_str());
+               else
+                       osprintf(os, "%s %s", cpptype.c_str(),
+                                param->getName().str().c_str());
+
+               if (i != num_params - 1)
+                       osprintf(os, ", ");
+       }
+
+       osprintf(os, ")");
+
+       if (kind == function_kind_member_method)
+               osprintf(os, " const");
+
+       if (is_declaration)
+               osprintf(os, ";\n");
+       else
+               osprintf(os, " {\n");
+}
+
+/* Generate the list of argument types for a callback function of
+ * type "type".  If "cpp" is set, then generate the C++ type list, otherwise
+ * the C type list.
+ *
+ * For a callback of type
+ *
+ *      isl_stat (*)(__isl_take isl_map *map, void *user)
+ *
+ * the following C++ argument list is generated:
+ *
+ *      isl::map
+ */
+string cpp_generator::generate_callback_args(QualType type, bool cpp)
+{
+       std::string type_str;
+       const FunctionProtoType *callback;
+       int num_params;
+
+       callback = type->getPointeeType()->getAs<FunctionProtoType>();
+       num_params = callback->getNumArgs();
+       if (cpp)
+               num_params--;
+
+       for (long i = 0; i < num_params; i++) {
+               QualType type = callback->getArgType(i);
+
+               if (cpp)
+                       type_str += type2cpp(type);
+               else
+                       type_str += type.getAsString();
+
+               if (!cpp)
+                       type_str += "arg_" + ::to_string(i);
+
+               if (i != num_params - 1)
+                       type_str += ", ";
+       }
+
+       return type_str;
+}
+
+/* Generate the full cpp type of a callback function of type "type".
+ *
+ * For a callback of type
+ *
+ *      isl_stat (*)(__isl_take isl_map *map, void *user)
+ *
+ * the following type is generated:
+ *
+ *      std::function<isl::stat(isl::map)>
+ */
+string cpp_generator::generate_callback_type(QualType type)
+{
+       std::string type_str;
+       const FunctionProtoType *callback = type->getPointeeType()->getAs<FunctionProtoType>();
+       QualType return_type = callback->getReturnType();
+       string rettype_str = type2cpp(return_type);
+
+       type_str = "std::function<";
+       type_str += rettype_str;
+       type_str += "(";
+       type_str += generate_callback_args(type, true);
+       type_str += ")>";
+
+       return type_str;
+}
+
+/* Print the local variables that are needed for a callback argument,
+ * in particular, print a lambda function that wraps the callback and
+ * a pointer to the actual C++ callback function.
+ *
+ * For a callback of the form
+ *
+ *      isl_stat (*fn)(__isl_take isl_map *map, void *user)
+ *
+ * the following lambda function is generated:
+ *
+ *      auto fn_lambda = [](isl_map *arg_0, void *arg_1) -> isl_stat {
+ *        auto *func = *static_cast<const std::function<stat(map)> **>(arg_1);
+ *        stat ret = (*func)(isl::manage(arg_0));
+ *        return isl_stat(ret);
+ *      };
+ *
+ * The pointer to the std::function C++ callback function is stored in fn_p.
+ * This std::function object represents the actual user
+ * callback function together with the locally captured state at the caller.
+ *
+ * The lambda function is expected to be used as a C callback function
+ * where the lambda itself is provided as the function pointer and
+ * where the user void pointer is a pointer to fn_p.
+ * The std::function object is extracted from the pointer to fn_p
+ * inside the lambda function.
+ * The double indirection is used to avoid having to worry about
+ * const casting.
+ */
+void cpp_generator::print_callback_local(ostream &os, ParmVarDecl *param)
+{
+       string pname;
+       QualType ptype;
+       string call_args, c_args, cpp_args, rettype, last_idx;
+       const FunctionProtoType *callback;
+       int num_params;
+
+       pname = param->getName().str();
+       ptype = param->getType();
+
+       c_args = generate_callback_args(ptype, false);
+       cpp_args = generate_callback_type(ptype);
+
+       callback = ptype->getPointeeType()->getAs<FunctionProtoType>();
+       rettype = callback->getReturnType().getAsString();
+       num_params = callback->getNumArgs();
+
+       last_idx = ::to_string(num_params - 1);
+
+       for (long i = 0; i < num_params - 1; i++) {
+               call_args += "isl::manage(arg_" + ::to_string(i) + ")";
+               if (i != num_params - 2)
+                       call_args += ", ";
+       }
+
+       osprintf(os, "  auto %s_p = &%s;\n", pname.c_str(), pname.c_str());
+       osprintf(os,
+                "  auto %s_lambda = [](%s) -> %s {\n"
+                "    auto *func = *static_cast<const %s **>(arg_%s);\n"
+                "    stat ret = (*func)(%s);\n"
+                "    return isl_stat(ret);\n"
+                "  };\n",
+                pname.c_str(), c_args.c_str(), rettype.c_str(),
+                cpp_args.c_str(), last_idx.c_str(), call_args.c_str());
+}
+
+/* An array listing functions that must be renamed and the function name they
+ * should be renamed to. We currently rename functions in case their name would
+ * match a reserved C++ keyword, which is not allowed in C++.
+ */
+static const char *rename_map[][2] = {
+       { "union", "unite" },
+};
+
+/* Rename method "name" in case the method name in the C++ bindings should not
+ * match the name in the C bindings. We do this for example to avoid
+ * C++ keywords.
+ */
+std::string cpp_generator::rename_method(std::string name)
+{
+       for (size_t i = 0; i < sizeof(rename_map) / sizeof(rename_map[0]); i++)
+               if (name.compare(rename_map[i][0]) == 0)
+                       return rename_map[i][1];
+
+       return name;
+}
+
+/* Translate isl class "clazz" to its corresponding C++ type.
+ */
+string cpp_generator::type2cpp(const isl_class &clazz)
+{
+       return type2cpp(clazz.name);
+}
+
+/* Translate type string "type_str" to its C++ name counterpart.
+*/
+string cpp_generator::type2cpp(string type_str)
+{
+       return type_str.substr(4);
+}
+
+/* Translate QualType "type" to its C++ name counterpart.
+ */
+string cpp_generator::type2cpp(QualType type)
+{
+       if (is_isl_type(type))
+               return "isl::" + type2cpp(type->getPointeeType().getAsString());
+
+       if (is_isl_bool(type))
+               return "isl::boolean";
+
+       if (is_isl_stat(type))
+               return "isl::stat";
+
+       if (type->isIntegerType())
+               return type.getAsString();
+
+       if (is_string(type))
+               return "std::string";
+
+       if (is_callback(type))
+               return generate_callback_type(type);
+
+       die("Cannot convert type to C++ type");
+}
+
+/* Check if "subclass_type" is a subclass of "class_type".
+ */
+bool cpp_generator::is_subclass(QualType subclass_type,
+       const isl_class &class_type)
+{
+       std::string type_str = subclass_type->getPointeeType().getAsString();
+       std::vector<std::string> superclasses;
+       std::vector<const isl_class *> parents;
+       std::vector<std::string>::iterator ci;
+
+       superclasses = generator::find_superclasses(classes[type_str].type);
+
+       for (ci = superclasses.begin(); ci < superclasses.end(); ci++)
+               parents.push_back(&classes[*ci]);
+
+       while (!parents.empty()) {
+               const isl_class *candidate = parents.back();
+
+               parents.pop_back();
+
+               if (&class_type == candidate)
+                       return true;
+
+               superclasses = generator::find_superclasses(candidate->type);
+
+               for (ci = superclasses.begin(); ci < superclasses.end(); ci++)
+                       parents.push_back(&classes[*ci]);
+       }
+
+       return false;
+}
+
+/* Check if "cons" is an implicit conversion constructor of class "clazz".
+ *
+ * An implicit conversion constructor is generated in case "cons" has a single
+ * parameter, where the parameter type is a subclass of the class that is
+ * currently being generated.
+ */
+bool cpp_generator::is_implicit_conversion(const isl_class &clazz,
+       FunctionDecl *cons)
+{
+       ParmVarDecl *param = cons->getParamDecl(0);
+       QualType type = param->getOriginalType();
+
+       int num_params = cons->getNumParams();
+       if (num_params != 1)
+               return false;
+
+       if (is_isl_type(type) && !is_isl_ctx(type) && is_subclass(type, clazz))
+               return true;
+
+       return false;
+}
+
+/* Get kind of "method" in "clazz".
+ *
+ * Given the declaration of a static or member method, returns its kind.
+ */
+cpp_generator::function_kind cpp_generator::get_method_kind(
+       const isl_class &clazz, FunctionDecl *method)
+{
+       if (is_static(clazz, method))
+               return function_kind_static_method;
+       else
+               return function_kind_member_method;
+}
diff --git a/interface/cpp.h b/interface/cpp.h
new file mode 100644 (file)
index 0000000..3d8d660
--- /dev/null
@@ -0,0 +1,77 @@
+#include "generator.h"
+
+using namespace std;
+using namespace clang;
+
+class cpp_generator : public generator {
+public:
+       cpp_generator(set<RecordDecl *> &exported_types,
+               set<FunctionDecl *> exported_functions,
+               set<FunctionDecl *> functions) :
+               generator(exported_types, exported_functions, functions) {}
+
+       enum function_kind {
+               function_kind_static_method,
+               function_kind_member_method,
+               function_kind_constructor,
+       };
+
+       virtual void generate();
+private:
+       void print_file(ostream &os, std::string filename);
+       void print_forward_declarations(ostream &os);
+       void print_declarations(ostream &os);
+       void print_class(ostream &os, const isl_class &clazz);
+       void print_class_forward_decl(ostream &os, const isl_class &clazz);
+       void print_class_factory_decl(ostream &os, const isl_class &clazz,
+               const std::string &prefix = std::string());
+       void print_private_constructors_decl(ostream &os,
+               const isl_class &clazz);
+       void print_copy_assignment_decl(ostream &os, const isl_class &clazz);
+       void print_public_constructors_decl(ostream &os,
+               const isl_class &clazz);
+       void print_constructors_decl(ostream &os, const isl_class &clazz);
+       void print_destructor_decl(ostream &os, const isl_class &clazz);
+       void print_ptr_decl(ostream &os, const isl_class &clazz);
+       void print_get_ctx_decl(ostream &os);
+       void print_methods_decl(ostream &os, const isl_class &clazz);
+       void print_method_group_decl(ostream &os, const isl_class &clazz,
+               const string &fullname, const set<FunctionDecl *> &methods);
+       void print_method_decl(ostream &os, const isl_class &clazz,
+               const string &fullname, FunctionDecl *method,
+               function_kind kind);
+       void print_implementations(ostream &os);
+       void print_class_impl(ostream &os, const isl_class &clazz);
+       void print_class_factory_impl(ostream &os, const isl_class &clazz);
+       void print_private_constructors_impl(ostream &os,
+               const isl_class &clazz);
+       void print_public_constructors_impl(ostream &os,
+               const isl_class &clazz);
+       void print_constructors_impl(ostream &os, const isl_class &clazz);
+       void print_copy_assignment_impl(ostream &os, const isl_class &clazz);
+       void print_destructor_impl(ostream &os, const isl_class &clazz);
+       void print_ptr_impl(ostream &os, const isl_class &clazz);
+       void print_get_ctx_impl(ostream &os, const isl_class &clazz);
+       void print_methods_impl(ostream &os, const isl_class &clazz);
+       void print_method_group_impl(ostream &os, const isl_class &clazz,
+               const string &fullname, const set<FunctionDecl *> &methods);
+       void print_method_impl(ostream &os, const isl_class &clazz,
+               const string &fullname, FunctionDecl *method,
+               function_kind kind);
+       void print_method_param_use(ostream &os, ParmVarDecl *param,
+               bool load_from_this_ptr);
+       void print_method_header(ostream &os, const isl_class &clazz,
+               FunctionDecl *method, const string &fullname,
+               bool is_declaration, function_kind kind);
+       string generate_callback_args(QualType type, bool cpp);
+       string generate_callback_type(QualType type);
+       void print_callback_local(ostream &os, ParmVarDecl *param);
+       std::string rename_method(std::string name);
+       string type2cpp(const isl_class &clazz);
+       string type2cpp(string type_string);
+       string type2cpp(QualType type);
+       bool is_implicit_conversion(const isl_class &clazz, FunctionDecl *cons);
+       bool is_subclass(QualType subclass_type, const isl_class &class_type);
+       function_kind get_method_kind(const isl_class &clazz,
+               FunctionDecl *method);
+};
index 1118688..1275b34 100644 (file)
@@ -76,6 +76,7 @@
 #include "extract_interface.h"
 #include "generator.h"
 #include "python.h"
+#include "cpp.h"
 
 using namespace std;
 using namespace clang;
@@ -449,6 +450,9 @@ int main(int argc, char *argv[])
        if (Language.compare("python") == 0)
                gen = new python_generator(consumer.exported_types,
                        consumer.exported_functions, consumer.functions);
+       else if (Language.compare("cpp") == 0)
+               gen = new cpp_generator(consumer.exported_types,
+                       consumer.exported_functions, consumer.functions);
        else
                cerr << "Language '" << Language << "' not recognized." << endl
                     << "Not generating bindings." << endl;
diff --git a/interface/isl-noexceptions.h.bot b/interface/isl-noexceptions.h.bot
new file mode 100644 (file)
index 0000000..98425d1
--- /dev/null
@@ -0,0 +1,2 @@
+
+#endif /* ISL_CPP_NOEXCEPTIONS */
diff --git a/interface/isl-noexceptions.h.pre b/interface/isl-noexceptions.h.pre
new file mode 100644 (file)
index 0000000..b45eb24
--- /dev/null
@@ -0,0 +1,74 @@
+
+#include <functional>
+#include <string>
+
+namespace isl {
+inline namespace noexceptions {
+
+#define ISLPP_STRINGIZE_(X) #X
+#define ISLPP_STRINGIZE(X) ISLPP_STRINGIZE_(X)
+
+#define ISLPP_ASSERT(test, message)                          \
+  do {                                                       \
+    if (test)                                                \
+      break;                                                 \
+    fputs("Assertion \"" #test "\" failed at " __FILE__      \
+      ":" ISLPP_STRINGIZE(__LINE__) "\n  " message "\n",     \
+      stderr);                                               \
+  } while (0)
+
+class boolean {
+private:
+  isl_bool val;
+
+  friend isl::boolean manage(isl_bool val);
+  boolean(isl_bool val): val(val) {}
+public:
+  boolean()
+      : val(isl_bool_error) {}
+
+  /* implicit */ boolean(bool val)
+      : val(val ? isl_bool_true : isl_bool_false) {}
+
+  bool is_error() const { return val == isl_bool_error; }
+  bool is_false() const { return val == isl_bool_false; }
+  bool is_true() const { return val == isl_bool_true; }
+
+  explicit operator bool() const {
+    ISLPP_ASSERT(!is_error(), "IMPLEMENTATION ERROR: Unhandled error state");
+    return is_true();
+  }
+
+  boolean operator!() const {
+    if (is_error())
+      return *this;
+    return !is_true();
+  }
+};
+
+inline isl::boolean manage(isl_bool val) {
+  return isl::boolean(val);
+}
+
+class ctx {
+  isl_ctx *ptr;
+public:
+  /* implicit */ ctx(isl_ctx *ctx)
+      : ptr(ctx) {}
+  isl_ctx *release() {
+    auto tmp = ptr;
+    ptr = nullptr;
+    return tmp;
+  }
+  isl_ctx *get() {
+    return ptr;
+  }
+};
+
+enum class stat {
+  ok = isl_stat_ok,
+  error = isl_stat_error
+};
+
+}
+} // namespace isl
diff --git a/interface/isl-noexceptions.h.top b/interface/isl-noexceptions.h.top
new file mode 100644 (file)
index 0000000..9e18d12
--- /dev/null
@@ -0,0 +1,10 @@
+/// These are automatically generated C++ bindings without exceptions for isl.
+///
+/// isl is a library for computing with integer sets and maps described by
+/// Presburger formulas. On top of this, isl provides various tools for
+/// polyhedral compilation, ranging from dependence analysis over scheduling
+/// to AST generation.
+
+#ifndef ISL_CPP_NOEXCEPTIONS
+#define ISL_CPP_NOEXCEPTIONS
+
diff --git a/interface/isl_test_cpp-noexceptions.cc b/interface/isl_test_cpp-noexceptions.cc
new file mode 100644 (file)
index 0000000..d468b17
--- /dev/null
@@ -0,0 +1,346 @@
+/* Copyright 2016-2017 Tobias Grosser
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Tobias Grosser, Weststrasse 47, CH-8003, Zurich
+ */
+
+#include <vector>
+#include <string>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <isl/options.h>
+#include <isl-noexceptions.h>
+
+static void assert_impl(bool condition, const char *file, int line,
+       const char *message)
+{
+       if (condition)
+               return;
+
+       fprintf(stderr, "Assertion failed in %s:%d %s\n", file, line, message);
+       exit(EXIT_FAILURE);
+}
+
+static void assert_impl(isl::boolean condition, const char *file, int line,
+       const char *message)
+{
+       assert_impl(bool(condition), file, line, message);
+}
+
+#define assert(exp) assert_impl(exp, __FILE__, __LINE__, #exp)
+
+/* Test the pointer interface for interaction between isl C and C++ types.
+ *
+ * This tests:
+ * - construction from an isl C object
+ * - check that constructed objects are non-null
+ * - get a non-owned C pointer from an isl C++ object usable in __isl_keep
+ *   methods
+ * - use copy to get an owned C pointer from an isl C++ object which is usable
+ *   in __isl_take methods. Verify that the original C++ object retains a valid
+ *   pointer.
+ * - use release to get an owned C pointer from an isl C++ object which is
+ *   usable in __isl_take methods. Verify that the original C++ object gave up
+ *   its pointer and now is null.
+ */
+void test_pointer(isl::ctx ctx)
+{
+       isl_set *c_empty = isl_set_read_from_str(ctx.get(), "{ : false }");
+       isl::set empty = isl::manage(c_empty);
+       assert(empty.is_empty());
+       assert(isl_set_is_empty(empty.get()));
+
+       assert(!empty.is_null());
+       isl_set_free(empty.copy());
+       assert(!empty.is_null());
+       isl_set_free(empty.release());
+       assert(empty.is_null());
+}
+
+/* Test that isl objects can be constructed.
+ *
+ * This tests:
+ *  - construction of a null object
+ *  - construction from a string
+ *  - construction from an integer
+ *  - static constructor without a parameter
+ *  - conversion construction (implicit)
+ *  - conversion construction (explicit)
+ *
+ *  The tests to construct from integers and strings cover functionality that
+ *  is also tested in the parameter type tests, but here we verify that
+ *  multiple overloaded constructors are available and that overload resolution
+ *  works as expected.
+ *
+ *  Construction from an isl C pointer is tested in test_pointer.
+ */
+void test_constructors(isl::ctx ctx)
+{
+       isl::val null;
+       assert(null.is_null());
+
+       isl::val zero_from_str = isl::val(ctx, "0");
+       assert(zero_from_str.is_zero());
+
+       isl::val zero_int_con = isl::val(ctx, 0);
+       assert(zero_int_con.is_zero());
+
+       isl::val zero_static_con = isl::val::zero(ctx);
+       assert(zero_static_con.is_zero());
+
+       isl::basic_set bs(ctx, "{ [1] }");
+       isl::set result(ctx, "{ [1] }");
+       isl::set s = bs;
+       assert(s.is_equal(result));
+       isl::set s2(bs);
+       assert(s.unite(s2).is_equal(result));
+}
+
+/* Test integer function parameters.
+ *
+ * Verify that extreme values and zero work.
+ */
+void test_parameters_int(isl::ctx ctx)
+{
+       isl::val long_max_str(ctx, std::to_string(LONG_MAX));
+       isl::val long_max_int(ctx, LONG_MAX);
+       assert(long_max_str.eq(long_max_int));
+
+       isl::val long_min_str(ctx, std::to_string(LONG_MIN));
+       isl::val long_min_int(ctx, LONG_MIN);
+       assert(long_min_str.eq(long_min_int));
+
+       isl::val long_zero_str = isl::val(ctx, std::to_string(0));
+       isl::val long_zero_int = isl::val(ctx, 0);
+       assert(long_zero_str.eq(long_zero_int));
+}
+
+/* Test isl objects parameters.
+ *
+ * Verify that isl objects can be passed as lvalue and rvalue parameters.
+ * Also verify that isl object parameters are automatically type converted if
+ * there is an inheritance relation. Finally, test function calls without
+ * any additional parameters, apart from the isl object on which
+ * the method is called.
+ */
+void test_parameters_obj(isl::ctx ctx)
+{
+       isl::set a(ctx, "{ [0] }");
+       isl::set b(ctx, "{ [1] }");
+       isl::set c(ctx, "{ [2] }");
+       isl::set expected(ctx, "{ [i] : 0 <= i <= 2 }");
+
+       isl::set tmp = a.unite(b);
+       isl::set res_lvalue_param = tmp.unite(c);
+       assert(res_lvalue_param.is_equal(expected));
+
+       isl::set res_rvalue_param = a.unite(b).unite(c);
+       assert(res_rvalue_param.is_equal(expected));
+
+       isl::basic_set a2(ctx, "{ [0] }");
+       assert(a.is_equal(a2));
+
+       isl::val two(ctx, 2);
+       isl::val half(ctx, "1/2");
+       isl::val res_only_this_param = two.inv();
+       assert(res_only_this_param.eq(half));
+}
+
+/* Test different kinds of parameters to be passed to functions.
+ *
+ * This includes integer and isl C++ object parameters.
+ */
+void test_parameters(isl::ctx ctx)
+{
+       test_parameters_int(ctx);
+       test_parameters_obj(ctx);
+}
+
+/* Test that isl objects are returned correctly.
+ *
+ * This only tests that after combining two objects, the result is successfully
+ * returned.
+ */
+void test_return_obj(isl::ctx ctx)
+{
+       isl::val one(ctx, "1");
+       isl::val two(ctx, "2");
+       isl::val three(ctx, "3");
+
+       isl::val res = one.add(two);
+
+       assert(res.eq(three));
+}
+
+/* Test that integer values are returned correctly.
+ */
+void test_return_int(isl::ctx ctx)
+{
+       isl::val one(ctx, "1");
+       isl::val neg_one(ctx, "-1");
+       isl::val zero(ctx, "0");
+
+       assert(one.sgn() > 0);
+       assert(neg_one.sgn() < 0);
+       assert(zero.sgn() == 0);
+}
+
+/* Test that isl_bool values are returned correctly.
+ *
+ * We check in detail the following parts of the isl::boolean class:
+ *  - The is_true, is_false, and is_error functions return true in case they
+ *    are called on a true, false, or error instance of isl::boolean,
+ *    respectively
+ *  - Explicit conversion to 'bool'
+ *  - Implicit conversion to 'bool'
+ *  - The complement operator
+ *  - Explicit construction from 'true' and 'false'
+ *  - Explicit construction form isl_bool
+ */
+void test_return_bool(isl::ctx ctx)
+{
+       isl::set empty(ctx, "{ : false }");
+       isl::set univ(ctx, "{ : }");
+       isl::set null;
+
+       isl::boolean b_true = empty.is_empty();
+       isl::boolean b_false = univ.is_empty();
+       isl::boolean b_error = null.is_empty();
+
+       assert(b_true.is_true());
+       assert(!b_true.is_false());
+       assert(!b_true.is_error());
+
+       assert(!b_false.is_true());
+       assert(b_false.is_false());
+       assert(!b_false.is_error());
+
+       assert(!b_error.is_true());
+       assert(!b_error.is_false());
+       assert(b_error.is_error());
+
+       assert(bool(b_true) == true);
+       assert(bool(b_false) == false);
+
+       assert(b_true);
+
+       assert((!b_false).is_true());
+       assert((!b_true).is_false());
+       assert((!b_error).is_error());
+
+       assert(isl::boolean(true).is_true());
+       assert(!isl::boolean(true).is_false());
+       assert(!isl::boolean(true).is_error());
+
+       assert(isl::boolean(false).is_false());
+       assert(!isl::boolean(false).is_true());
+       assert(!isl::boolean(false).is_error());
+
+       assert(isl::manage(isl_bool_true).is_true());
+       assert(!isl::manage(isl_bool_true).is_false());
+       assert(!isl::manage(isl_bool_true).is_error());
+
+       assert(isl::manage(isl_bool_false).is_false());
+       assert(!isl::manage(isl_bool_false).is_true());
+       assert(!isl::manage(isl_bool_false).is_error());
+
+       assert(isl::manage(isl_bool_error).is_error());
+       assert(!isl::manage(isl_bool_error).is_true());
+       assert(!isl::manage(isl_bool_error).is_false());
+}
+
+/* Test that strings are returned correctly.
+ * Do so by calling overloaded isl::ast_build::from_expr methods.
+ */
+void test_return_string(isl::ctx ctx)
+{
+       isl::set context(ctx, "[n] -> { : }");
+       isl::ast_build build = isl::ast_build::from_context(context);
+       isl::pw_aff pw_aff(ctx, "[n] -> { [n] }");
+       isl::set set(ctx, "[n] -> { : n >= 0 }");
+
+       isl::ast_expr expr = build.expr_from(pw_aff);
+       const char *expected_string = "n";
+       assert(expected_string == expr.to_C_str());
+
+       expr = build.expr_from(set);
+       expected_string = "n >= 0";
+       assert(expected_string == expr.to_C_str());
+}
+
+/* Test that return values are handled correctly.
+ *
+ * Test that isl C++ objects, integers, boolean values, and strings are
+ * returned correctly.
+ */
+void test_return(isl::ctx ctx)
+{
+       test_return_obj(ctx);
+       test_return_int(ctx);
+       test_return_bool(ctx);
+       test_return_string(ctx);
+}
+
+/* Test that foreach functions are modeled correctly.
+ *
+ * Verify that lambdas are correctly called as callback of a 'foreach'
+ * function and that variables captured by the lambda work correctly. Also
+ * check that the foreach function takes account of the return value of the
+ * lambda and aborts in case isl::stat::error is returned and then returns
+ * isl::stat::error itself.
+ */
+void test_foreach(isl::ctx ctx)
+{
+       isl::set s(ctx, "{ [0]; [1]; [2] }");
+
+       std::vector<isl::basic_set> basic_sets;
+
+       auto add_to_vector = [&] (isl::basic_set bs) {
+               basic_sets.push_back(bs);
+               return isl::stat::ok;
+       };
+
+       isl::stat ret1 = s.foreach_basic_set(add_to_vector);
+
+       assert(ret1 == isl::stat::ok);
+       assert(basic_sets.size() == 3);
+       assert(isl::set(basic_sets[0]).is_subset(s));
+       assert(isl::set(basic_sets[1]).is_subset(s));
+       assert(isl::set(basic_sets[2]).is_subset(s));
+       assert(!basic_sets[0].is_equal(basic_sets[1]));
+
+       auto fail = [&] (isl::basic_set bs) {
+               return isl::stat::error;
+       };
+
+       isl::stat ret2 = s.foreach_basic_set(fail);
+
+       assert(ret2 == isl::stat::error);
+}
+
+/* Test the isl C++ interface
+ *
+ * This includes:
+ *  - The isl C <-> C++ pointer interface
+ *  - Object construction
+ *  - Different parameter types
+ *  - Different return types
+ *  - Foreach functions
+ */
+int main()
+{
+       isl_ctx *ctx = isl_ctx_alloc();
+
+       isl_options_set_on_error(ctx, ISL_ON_ERROR_ABORT);
+
+       test_pointer(ctx);
+       test_constructors(ctx);
+       test_parameters(ctx);
+       test_return(ctx);
+       test_foreach(ctx);
+
+       isl_ctx_free(ctx);
+}
diff --git a/m4/ax_cxx_compile_stdcxx.m4 b/m4/ax_cxx_compile_stdcxx.m4
new file mode 100644 (file)
index 0000000..5032bba
--- /dev/null
@@ -0,0 +1,982 @@
+# ===========================================================================
+#  https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
+#
+# DESCRIPTION
+#
+#   Check for baseline language coverage in the compiler for the specified
+#   version of the C++ standard.  If necessary, add switches to CXX and
+#   CXXCPP to enable support.  VERSION may be '11' (for the C++11 standard)
+#   or '14' (for the C++14 standard).
+#
+#   The second argument, if specified, indicates whether you insist on an
+#   extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
+#   -std=c++11).  If neither is specified, you get whatever works, with
+#   preference for an extended mode.
+#
+#   The third argument, if specified 'mandatory' or if left unspecified,
+#   indicates that baseline support for the specified C++ standard is
+#   required and that the macro should error out if no mode with that
+#   support is found.  If specified 'optional', then configuration proceeds
+#   regardless, after defining HAVE_CXX${VERSION} if and only if a
+#   supporting mode is found.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
+#   Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
+#   Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
+#   Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
+#   Copyright (c) 2015 Paul Norman <penorman@mac.com>
+#   Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
+#   Copyright (c) 2016 Krzesimir Nowak <qdlacz@gmail.com>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved.  This file is offered as-is, without any
+#   warranty.
+
+#serial 7
+
+dnl  This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
+dnl  (serial version number 13).
+
+AX_REQUIRE_DEFINED([AC_MSG_WARN])
+AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
+  m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
+        [$1], [14], [ax_cxx_compile_alternatives="14 1y"],
+        [$1], [17], [ax_cxx_compile_alternatives="17 1z"],
+        [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
+  m4_if([$2], [], [],
+        [$2], [ext], [],
+        [$2], [noext], [],
+        [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
+  m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
+        [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
+        [$3], [optional], [ax_cxx_compile_cxx$1_required=false],
+        [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
+  AC_LANG_PUSH([C++])dnl
+  ac_success=no
+  AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
+  ax_cv_cxx_compile_cxx$1,
+  [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+    [ax_cv_cxx_compile_cxx$1=yes],
+    [ax_cv_cxx_compile_cxx$1=no])])
+  if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
+    ac_success=yes
+  fi
+
+  m4_if([$2], [noext], [], [dnl
+  if test x$ac_success = xno; then
+    for alternative in ${ax_cxx_compile_alternatives}; do
+      switch="-std=gnu++${alternative}"
+      cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
+      AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
+                     $cachevar,
+        [ac_save_CXX="$CXX"
+         CXX="$CXX $switch"
+         AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+          [eval $cachevar=yes],
+          [eval $cachevar=no])
+         CXX="$ac_save_CXX"])
+      if eval test x\$$cachevar = xyes; then
+        CXX="$CXX $switch"
+        if test -n "$CXXCPP" ; then
+          CXXCPP="$CXXCPP $switch"
+        fi
+        ac_success=yes
+        break
+      fi
+    done
+  fi])
+
+  m4_if([$2], [ext], [], [dnl
+  if test x$ac_success = xno; then
+    dnl HP's aCC needs +std=c++11 according to:
+    dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
+    dnl Cray's crayCC needs "-h std=c++11"
+    for alternative in ${ax_cxx_compile_alternatives}; do
+      for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
+        cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
+        AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
+                       $cachevar,
+          [ac_save_CXX="$CXX"
+           CXX="$CXX $switch"
+           AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+            [eval $cachevar=yes],
+            [eval $cachevar=no])
+           CXX="$ac_save_CXX"])
+        if eval test x\$$cachevar = xyes; then
+          CXX="$CXX $switch"
+          if test -n "$CXXCPP" ; then
+            CXXCPP="$CXXCPP $switch"
+          fi
+          ac_success=yes
+          break
+        fi
+      done
+      if test x$ac_success = xyes; then
+        break
+      fi
+    done
+  fi])
+  AC_LANG_POP([C++])
+  if test x$ax_cxx_compile_cxx$1_required = xtrue; then
+    if test x$ac_success = xno; then
+      AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
+    fi
+  fi
+  if test x$ac_success = xno; then
+    HAVE_CXX$1=0
+    AC_MSG_NOTICE([No compiler with C++$1 support was found])
+  else
+    HAVE_CXX$1=1
+    AC_DEFINE(HAVE_CXX$1,1,
+              [define if the compiler supports basic C++$1 syntax])
+  fi
+  AC_SUBST(HAVE_CXX$1)
+  m4_if([$1], [17], [AC_MSG_WARN([C++17 is not yet standardized, so the checks may change in incompatible ways anytime])])
+])
+
+
+dnl  Test body for checking C++11 support
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+)
+
+
+dnl  Test body for checking C++14 support
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
+)
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_17
+)
+
+dnl  Tests for new features in C++11
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
+
+// If the compiler admits that it is not ready for C++11, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201103L
+
+#error "This is not a C++11 compiler"
+
+#else
+
+namespace cxx11
+{
+
+  namespace test_static_assert
+  {
+
+    template <typename T>
+    struct check
+    {
+      static_assert(sizeof(int) <= sizeof(T), "not big enough");
+    };
+
+  }
+
+  namespace test_final_override
+  {
+
+    struct Base
+    {
+      virtual void f() {}
+    };
+
+    struct Derived : public Base
+    {
+      virtual void f() override {}
+    };
+
+  }
+
+  namespace test_double_right_angle_brackets
+  {
+
+    template < typename T >
+    struct check {};
+
+    typedef check<void> single_type;
+    typedef check<check<void>> double_type;
+    typedef check<check<check<void>>> triple_type;
+    typedef check<check<check<check<void>>>> quadruple_type;
+
+  }
+
+  namespace test_decltype
+  {
+
+    int
+    f()
+    {
+      int a = 1;
+      decltype(a) b = 2;
+      return a + b;
+    }
+
+  }
+
+  namespace test_type_deduction
+  {
+
+    template < typename T1, typename T2 >
+    struct is_same
+    {
+      static const bool value = false;
+    };
+
+    template < typename T >
+    struct is_same<T, T>
+    {
+      static const bool value = true;
+    };
+
+    template < typename T1, typename T2 >
+    auto
+    add(T1 a1, T2 a2) -> decltype(a1 + a2)
+    {
+      return a1 + a2;
+    }
+
+    int
+    test(const int c, volatile int v)
+    {
+      static_assert(is_same<int, decltype(0)>::value == true, "");
+      static_assert(is_same<int, decltype(c)>::value == false, "");
+      static_assert(is_same<int, decltype(v)>::value == false, "");
+      auto ac = c;
+      auto av = v;
+      auto sumi = ac + av + 'x';
+      auto sumf = ac + av + 1.0;
+      static_assert(is_same<int, decltype(ac)>::value == true, "");
+      static_assert(is_same<int, decltype(av)>::value == true, "");
+      static_assert(is_same<int, decltype(sumi)>::value == true, "");
+      static_assert(is_same<int, decltype(sumf)>::value == false, "");
+      static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
+      return (sumf > 0.0) ? sumi : add(c, v);
+    }
+
+  }
+
+  namespace test_noexcept
+  {
+
+    int f() { return 0; }
+    int g() noexcept { return 0; }
+
+    static_assert(noexcept(f()) == false, "");
+    static_assert(noexcept(g()) == true, "");
+
+  }
+
+  namespace test_constexpr
+  {
+
+    template < typename CharT >
+    unsigned long constexpr
+    strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
+    {
+      return *s ? strlen_c_r(s + 1, acc + 1) : acc;
+    }
+
+    template < typename CharT >
+    unsigned long constexpr
+    strlen_c(const CharT *const s) noexcept
+    {
+      return strlen_c_r(s, 0UL);
+    }
+
+    static_assert(strlen_c("") == 0UL, "");
+    static_assert(strlen_c("1") == 1UL, "");
+    static_assert(strlen_c("example") == 7UL, "");
+    static_assert(strlen_c("another\0example") == 7UL, "");
+
+  }
+
+  namespace test_rvalue_references
+  {
+
+    template < int N >
+    struct answer
+    {
+      static constexpr int value = N;
+    };
+
+    answer<1> f(int&)       { return answer<1>(); }
+    answer<2> f(const int&) { return answer<2>(); }
+    answer<3> f(int&&)      { return answer<3>(); }
+
+    void
+    test()
+    {
+      int i = 0;
+      const int c = 0;
+      static_assert(decltype(f(i))::value == 1, "");
+      static_assert(decltype(f(c))::value == 2, "");
+      static_assert(decltype(f(0))::value == 3, "");
+    }
+
+  }
+
+  namespace test_uniform_initialization
+  {
+
+    struct test
+    {
+      static const int zero {};
+      static const int one {1};
+    };
+
+    static_assert(test::zero == 0, "");
+    static_assert(test::one == 1, "");
+
+  }
+
+  namespace test_lambdas
+  {
+
+    void
+    test1()
+    {
+      auto lambda1 = [](){};
+      auto lambda2 = lambda1;
+      lambda1();
+      lambda2();
+    }
+
+    int
+    test2()
+    {
+      auto a = [](int i, int j){ return i + j; }(1, 2);
+      auto b = []() -> int { return '0'; }();
+      auto c = [=](){ return a + b; }();
+      auto d = [&](){ return c; }();
+      auto e = [a, &b](int x) mutable {
+        const auto identity = [](int y){ return y; };
+        for (auto i = 0; i < a; ++i)
+          a += b--;
+        return x + identity(a + b);
+      }(0);
+      return a + b + c + d + e;
+    }
+
+    int
+    test3()
+    {
+      const auto nullary = [](){ return 0; };
+      const auto unary = [](int x){ return x; };
+      using nullary_t = decltype(nullary);
+      using unary_t = decltype(unary);
+      const auto higher1st = [](nullary_t f){ return f(); };
+      const auto higher2nd = [unary](nullary_t f1){
+        return [unary, f1](unary_t f2){ return f2(unary(f1())); };
+      };
+      return higher1st(nullary) + higher2nd(nullary)(unary);
+    }
+
+  }
+
+  namespace test_variadic_templates
+  {
+
+    template <int...>
+    struct sum;
+
+    template <int N0, int... N1toN>
+    struct sum<N0, N1toN...>
+    {
+      static constexpr auto value = N0 + sum<N1toN...>::value;
+    };
+
+    template <>
+    struct sum<>
+    {
+      static constexpr auto value = 0;
+    };
+
+    static_assert(sum<>::value == 0, "");
+    static_assert(sum<1>::value == 1, "");
+    static_assert(sum<23>::value == 23, "");
+    static_assert(sum<1, 2>::value == 3, "");
+    static_assert(sum<5, 5, 11>::value == 21, "");
+    static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
+
+  }
+
+  // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
+  // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
+  // because of this.
+  namespace test_template_alias_sfinae
+  {
+
+    struct foo {};
+
+    template<typename T>
+    using member = typename T::member_type;
+
+    template<typename T>
+    void func(...) {}
+
+    template<typename T>
+    void func(member<T>*) {}
+
+    void test();
+
+    void test() { func<foo>(0); }
+
+  }
+
+}  // namespace cxx11
+
+#endif  // __cplusplus >= 201103L
+
+]])
+
+
+dnl  Tests for new features in C++14
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
+
+// If the compiler admits that it is not ready for C++14, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201402L
+
+#error "This is not a C++14 compiler"
+
+#else
+
+namespace cxx14
+{
+
+  namespace test_polymorphic_lambdas
+  {
+
+    int
+    test()
+    {
+      const auto lambda = [](auto&&... args){
+        const auto istiny = [](auto x){
+          return (sizeof(x) == 1UL) ? 1 : 0;
+        };
+        const int aretiny[] = { istiny(args)... };
+        return aretiny[0];
+      };
+      return lambda(1, 1L, 1.0f, '1');
+    }
+
+  }
+
+  namespace test_binary_literals
+  {
+
+    constexpr auto ivii = 0b0000000000101010;
+    static_assert(ivii == 42, "wrong value");
+
+  }
+
+  namespace test_generalized_constexpr
+  {
+
+    template < typename CharT >
+    constexpr unsigned long
+    strlen_c(const CharT *const s) noexcept
+    {
+      auto length = 0UL;
+      for (auto p = s; *p; ++p)
+        ++length;
+      return length;
+    }
+
+    static_assert(strlen_c("") == 0UL, "");
+    static_assert(strlen_c("x") == 1UL, "");
+    static_assert(strlen_c("test") == 4UL, "");
+    static_assert(strlen_c("another\0test") == 7UL, "");
+
+  }
+
+  namespace test_lambda_init_capture
+  {
+
+    int
+    test()
+    {
+      auto x = 0;
+      const auto lambda1 = [a = x](int b){ return a + b; };
+      const auto lambda2 = [a = lambda1(x)](){ return a; };
+      return lambda2();
+    }
+
+  }
+
+  namespace test_digit_separators
+  {
+
+    constexpr auto ten_million = 100'000'000;
+    static_assert(ten_million == 100000000, "");
+
+  }
+
+  namespace test_return_type_deduction
+  {
+
+    auto f(int& x) { return x; }
+    decltype(auto) g(int& x) { return x; }
+
+    template < typename T1, typename T2 >
+    struct is_same
+    {
+      static constexpr auto value = false;
+    };
+
+    template < typename T >
+    struct is_same<T, T>
+    {
+      static constexpr auto value = true;
+    };
+
+    int
+    test()
+    {
+      auto x = 0;
+      static_assert(is_same<int, decltype(f(x))>::value, "");
+      static_assert(is_same<int&, decltype(g(x))>::value, "");
+      return x;
+    }
+
+  }
+
+}  // namespace cxx14
+
+#endif  // __cplusplus >= 201402L
+
+]])
+
+
+dnl  Tests for new features in C++17
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
+
+// If the compiler admits that it is not ready for C++17, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus <= 201402L
+
+#error "This is not a C++17 compiler"
+
+#else
+
+#if defined(__clang__)
+  #define REALLY_CLANG
+#else
+  #if defined(__GNUC__)
+    #define REALLY_GCC
+  #endif
+#endif
+
+#include <initializer_list>
+#include <utility>
+#include <type_traits>
+
+namespace cxx17
+{
+
+#if !defined(REALLY_CLANG)
+  namespace test_constexpr_lambdas
+  {
+
+    // TODO: test it with clang++ from git
+
+    constexpr int foo = [](){return 42;}();
+
+  }
+#endif // !defined(REALLY_CLANG)
+
+  namespace test::nested_namespace::definitions
+  {
+
+  }
+
+  namespace test_fold_expression
+  {
+
+    template<typename... Args>
+    int multiply(Args... args)
+    {
+      return (args * ... * 1);
+    }
+
+    template<typename... Args>
+    bool all(Args... args)
+    {
+      return (args && ...);
+    }
+
+  }
+
+  namespace test_extended_static_assert
+  {
+
+    static_assert (true);
+
+  }
+
+  namespace test_auto_brace_init_list
+  {
+
+    auto foo = {5};
+    auto bar {5};
+
+    static_assert(std::is_same<std::initializer_list<int>, decltype(foo)>::value);
+    static_assert(std::is_same<int, decltype(bar)>::value);
+  }
+
+  namespace test_typename_in_template_template_parameter
+  {
+
+    template<template<typename> typename X> struct D;
+
+  }
+
+  namespace test_fallthrough_nodiscard_maybe_unused_attributes
+  {
+
+    int f1()
+    {
+      return 42;
+    }
+
+    [[nodiscard]] int f2()
+    {
+      [[maybe_unused]] auto unused = f1();
+
+      switch (f1())
+      {
+      case 17:
+        f1();
+        [[fallthrough]];
+      case 42:
+        f1();
+      }
+      return f1();
+    }
+
+  }
+
+  namespace test_extended_aggregate_initialization
+  {
+
+    struct base1
+    {
+      int b1, b2 = 42;
+    };
+
+    struct base2
+    {
+      base2() {
+        b3 = 42;
+      }
+      int b3;
+    };
+
+    struct derived : base1, base2
+    {
+        int d;
+    };
+
+    derived d1 {{1, 2}, {}, 4};  // full initialization
+    derived d2 {{}, {}, 4};      // value-initialized bases
+
+  }
+
+  namespace test_general_range_based_for_loop
+  {
+
+    struct iter
+    {
+      int i;
+
+      int& operator* ()
+      {
+        return i;
+      }
+
+      const int& operator* () const
+      {
+        return i;
+      }
+
+      iter& operator++()
+      {
+        ++i;
+        return *this;
+      }
+    };
+
+    struct sentinel
+    {
+      int i;
+    };
+
+    bool operator== (const iter& i, const sentinel& s)
+    {
+      return i.i == s.i;
+    }
+
+    bool operator!= (const iter& i, const sentinel& s)
+    {
+      return !(i == s);
+    }
+
+    struct range
+    {
+      iter begin() const
+      {
+        return {0};
+      }
+
+      sentinel end() const
+      {
+        return {5};
+      }
+    };
+
+    void f()
+    {
+      range r {};
+
+      for (auto i : r)
+      {
+        [[maybe_unused]] auto v = i;
+      }
+    }
+
+  }
+
+  namespace test_lambda_capture_asterisk_this_by_value
+  {
+
+    struct t
+    {
+      int i;
+      int foo()
+      {
+        return [*this]()
+        {
+          return i;
+        }();
+      }
+    };
+
+  }
+
+  namespace test_enum_class_construction
+  {
+
+    enum class byte : unsigned char
+    {};
+
+    byte foo {42};
+
+  }
+
+  namespace test_constexpr_if
+  {
+
+    template <bool cond>
+    int f ()
+    {
+      if constexpr(cond)
+      {
+        return 13;
+      }
+      else
+      {
+        return 42;
+      }
+    }
+
+  }
+
+  namespace test_selection_statement_with_initializer
+  {
+
+    int f()
+    {
+      return 13;
+    }
+
+    int f2()
+    {
+      if (auto i = f(); i > 0)
+      {
+        return 3;
+      }
+
+      switch (auto i = f(); i + 4)
+      {
+      case 17:
+        return 2;
+
+      default:
+        return 1;
+      }
+    }
+
+  }
+
+#if !defined(REALLY_CLANG)
+  namespace test_template_argument_deduction_for_class_templates
+  {
+
+    // TODO: test it with clang++ from git
+
+    template <typename T1, typename T2>
+    struct pair
+    {
+      pair (T1 p1, T2 p2)
+        : m1 {p1},
+          m2 {p2}
+      {}
+
+      T1 m1;
+      T2 m2;
+    };
+
+    void f()
+    {
+      [[maybe_unused]] auto p = pair{13, 42u};
+    }
+
+  }
+#endif // !defined(REALLY_CLANG)
+
+  namespace test_non_type_auto_template_parameters
+  {
+
+    template <auto n>
+    struct B
+    {};
+
+    B<5> b1;
+    B<'a'> b2;
+
+  }
+
+#if !defined(REALLY_CLANG)
+  namespace test_structured_bindings
+  {
+
+    // TODO: test it with clang++ from git
+
+    int arr[2] = { 1, 2 };
+    std::pair<int, int> pr = { 1, 2 };
+
+    auto f1() -> int(&)[2]
+    {
+      return arr;
+    }
+
+    auto f2() -> std::pair<int, int>&
+    {
+      return pr;
+    }
+
+    struct S
+    {
+      int x1 : 2;
+      volatile double y1;
+    };
+
+    S f3()
+    {
+      return {};
+    }
+
+    auto [ x1, y1 ] = f1();
+    auto& [ xr1, yr1 ] = f1();
+    auto [ x2, y2 ] = f2();
+    auto& [ xr2, yr2 ] = f2();
+    const auto [ x3, y3 ] = f3();
+
+  }
+#endif // !defined(REALLY_CLANG)
+
+#if !defined(REALLY_CLANG)
+  namespace test_exception_spec_type_system
+  {
+
+    // TODO: test it with clang++ from git
+
+    struct Good {};
+    struct Bad {};
+
+    void g1() noexcept;
+    void g2();
+
+    template<typename T>
+    Bad
+    f(T*, T*);
+
+    template<typename T1, typename T2>
+    Good
+    f(T1*, T2*);
+
+    static_assert (std::is_same_v<Good, decltype(f(g1, g2))>);
+
+  }
+#endif // !defined(REALLY_CLANG)
+
+  namespace test_inline_variables
+  {
+
+    template<class T> void f(T)
+    {}
+
+    template<class T> inline T g(T)
+    {
+      return T{};
+    }
+
+    template<> inline void f<>(int)
+    {}
+
+    template<> int g<>(int)
+    {
+      return 5;
+    }
+
+  }
+
+}  // namespace cxx17
+
+#endif  // __cplusplus <= 201402L
+
+]])
diff --git a/m4/ax_cxx_compile_stdcxx_11.m4 b/m4/ax_cxx_compile_stdcxx_11.m4
new file mode 100644 (file)
index 0000000..0aadeaf
--- /dev/null
@@ -0,0 +1,39 @@
+# ============================================================================
+#  http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html
+# ============================================================================
+#
+# SYNOPSIS
+#
+#   AX_CXX_COMPILE_STDCXX_11([ext|noext], [mandatory|optional])
+#
+# DESCRIPTION
+#
+#   Check for baseline language coverage in the compiler for the C++11
+#   standard; if necessary, add switches to CXX and CXXCPP to enable
+#   support.
+#
+#   This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX
+#   macro with the version set to C++11.  The two optional arguments are
+#   forwarded literally as the second and third argument respectively.
+#   Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for
+#   more information.  If you want to use this macro, you also need to
+#   download the ax_cxx_compile_stdcxx.m4 file.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
+#   Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
+#   Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
+#   Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
+#   Copyright (c) 2015 Paul Norman <penorman@mac.com>
+#   Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 17
+
+AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX])
+AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [AX_CXX_COMPILE_STDCXX([11], [$1], [$2])])