From a2e5b58be20fe45aafc94c973b1d256434e36888 Mon Sep 17 00:00:00 2001
From: Andreas Kloeckner <inform@tiker.net>
Date: Thu, 25 Jun 2015 00:53:26 -0500
Subject: [PATCH] Some headway towards cffi wrappers: pieces of the demo
 execute, with issues

---
 .gitignore                     |    4 +
 .gitmodules                    |    3 -
 bpl-subset                     |    1 -
 gen_wrap.py                    | 1251 +++++++++++++++++---------------
 islpy/__init__.py              |  115 ++-
 py_codegen.py                  |  101 +++
 setup.py                       |   34 +-
 src/wrapper/wrap_helpers.hpp   |   77 --
 src/wrapper/wrap_isl.cpp       |  152 ----
 src/wrapper/wrap_isl.hpp       |  298 --------
 src/wrapper/wrap_isl_part1.cpp |  122 ----
 src/wrapper/wrap_isl_part2.cpp |   32 -
 src/wrapper/wrap_isl_part3.cpp |   42 --
 13 files changed, 914 insertions(+), 1318 deletions(-)
 delete mode 160000 bpl-subset
 create mode 100644 py_codegen.py
 delete mode 100644 src/wrapper/wrap_helpers.hpp
 delete mode 100644 src/wrapper/wrap_isl.cpp
 delete mode 100644 src/wrapper/wrap_isl.hpp
 delete mode 100644 src/wrapper/wrap_isl_part1.cpp
 delete mode 100644 src/wrapper/wrap_isl_part2.cpp
 delete mode 100644 src/wrapper/wrap_isl_part3.cpp

diff --git a/.gitignore b/.gitignore
index 1202eab..121f09a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,3 +48,7 @@ setuptools-*.tar.gz
 core
 src/wrapper/gen-*
 .dirty-git-ok
+wrapped-functions.h
+_isl_cffi.py
+_isl.py
+class_list.py
diff --git a/.gitmodules b/.gitmodules
index 670e245..dbf0c71 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,3 @@
 [submodule "isl"]
 	path = isl
 	url = http://repo.or.cz/isl.git
-[submodule "bpl-subset"]
-	path = bpl-subset
-	url = git://github.com/inducer/bpl-subset
diff --git a/bpl-subset b/bpl-subset
deleted file mode 160000
index a44c81f..0000000
--- a/bpl-subset
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit a44c81ff6251387d660a25b3c1c39a5639484079
diff --git a/gen_wrap.py b/gen_wrap.py
index a9b43c8..da45150 100644
--- a/gen_wrap.py
+++ b/gen_wrap.py
@@ -1,14 +1,18 @@
+from __future__ import print_function
 import re
 import sys
+from py_codegen import PythonCodeGenerator, Indentation
 
 SEM_TAKE = "take"
 SEM_GIVE = "give"
 SEM_KEEP = "keep"
+SEM_NULL = "null"
 
 ISL_SEM_TO_SEM = {
     "__isl_take": SEM_TAKE,
     "__isl_give": SEM_GIVE,
     "__isl_keep": SEM_KEEP,
+    "__isl_null": SEM_NULL,
     }
 
 NON_COPYABLE = ["ctx", "printer", "access_info"]
@@ -34,14 +38,28 @@ class Argument:
         self.base_type = base_type
         self.ptr = ptr
 
+    def c_declarator(self):
+        return "{type} {ptr}{name}".format(
+                type=self.base_type,
+                ptr=self.ptr,
+                name=self.name)
+
 
 class CallbackArgument:
-    def __init__(self, name, return_base_type, return_ptr, args):
+    def __init__(self, name, return_semantics, return_base_type, return_ptr, args):
         self.name = name
+        self.return_semantics = return_semantics
         self.return_base_type = return_base_type
         self.return_ptr = return_ptr
         self.args = args
 
+    def c_declarator(self):
+        return "{type} {ptr}(*{name})({args})".format(
+                type=self.return_base_type,
+                ptr=self.return_ptr,
+                name=self.name,
+                args=", ".join(arg.c_declarator() for arg in self.args))
+
 
 class Method:
     def __init__(self, cls, name, c_name,
@@ -81,59 +99,254 @@ class Method:
 # }}}
 
 
-PART_TO_CLASSES = {
-        # If you change this, change:
-        # - src/wrapper/wrap_isl.hpp to add WRAP_CLASS(...)
-        # - src/wrapper/wrap_isl_partN.hpp to add MAKE_WRAP(...)
-        # - doc/reference.rst
-
-        "part1": [
-            # lists
-            "id_list",
-            "basic_set_list", "basic_map_list", "set_list",
-            "aff_list", "pw_aff_list", "band_list",
-            "ast_expr_list", "ast_node_list",
-
-            # maps
-            "id_to_ast_expr",
-
-            # others
-            "printer",  "val", "multi_val", "vec", "mat",
-            "aff", "pw_aff", "union_pw_aff",
-            "multi_aff", "multi_pw_aff", "pw_multi_aff", "union_pw_multi_aff",
-            "multi_union_pw_aff",
-
-            "id",
-            "constraint", "space", "local_space",
-        ],
-
-        "part2": [
-            "basic_set", "basic_map",
-            "set", "map",
-            "union_map", "union_set",
-            "point", "vertex", "cell", "vertices",
-        ],
-
-        "part3": [
-            "qpolynomial_fold", "pw_qpolynomial_fold",
-            "union_pw_qpolynomial_fold",
-            "union_pw_qpolynomial",
-            "qpolynomial", "pw_qpolynomial",
-            "term",
-
-            "band", "schedule", "schedule_constraints",
-            "schedule_node",
-
-            "access_info", "flow", "restriction",
-            "union_access_info", "union_flow",
-
-            "ast_expr", "ast_node", "ast_print_options",
-            "ast_build",
+CLASSES = [
+        "options",
+        "ctx",
+
+        # lists
+        "id_list", "val_list",
+        "basic_set_list", "basic_map_list", "set_list", "map_list",
+        "union_set_list",
+        "constraint_list",
+        "aff_list", "pw_aff_list", "band_list",
+        "ast_expr_list", "ast_node_list",
+
+        # maps
+        "id_to_ast_expr",
+
+        # others
+        "printer",  "val", "multi_val", "vec", "mat",
+        "aff", "pw_aff", "union_pw_aff",
+        "multi_aff", "multi_pw_aff", "pw_multi_aff", "union_pw_multi_aff",
+        "multi_union_pw_aff",
+
+        "id",
+        "constraint", "space", "local_space",
+
+        "basic_set", "basic_map",
+        "set", "map",
+        "union_map", "union_set",
+        "point", "vertex", "cell", "vertices",
+
+        "qpolynomial_fold", "pw_qpolynomial_fold",
+        "union_pw_qpolynomial_fold",
+        "union_pw_qpolynomial",
+        "qpolynomial", "pw_qpolynomial",
+        "term",
+
+        "band", "schedule", "schedule_constraints",
+        "schedule_node",
+
+        "access_info", "flow", "restriction",
+        "union_access_info", "union_flow",
+
+        "ast_expr", "ast_node", "ast_print_options",
+        "ast_build",
         ]
-        }
-CLASSES = []
-for cls_list in PART_TO_CLASSES.values():
-    CLASSES.extend(cls_list)
+
+
+IMPLICIT_CONVERSIONS = {
+    "isl_set": [("isl_basic_set", "from_basic_set")],
+    "isl_map": [("isl_basic_map", "from_basic_map")],
+    "isl_union_set": [("isl_set", "from_set")],
+    "isl_union_map": [("isl_map", "from_map")],
+    "isl_local_space": [("isl_space", "from_space")],
+    "isl_pw_aff": [("isl_aff", "from_aff")],
+    }
+
+
+HEADER_PREAMBLE = """
+// ctx.h
+typedef enum {
+        isl_error_none = 0,
+        isl_error_abort,
+        isl_error_alloc,
+        isl_error_unknown,
+        isl_error_internal,
+        isl_error_invalid,
+        isl_error_quota,
+        isl_error_unsupported
+} isl_error;
+
+typedef enum {
+        isl_stat_error = -1,
+        isl_stat_ok = 0,
+} isl_stat;
+
+typedef enum {
+        isl_bool_error = -1,
+        isl_bool_false = 0,
+        isl_bool_true = 1
+} isl_bool;
+
+// space.h
+typedef enum {
+        isl_dim_cst,
+        isl_dim_param,
+        isl_dim_in,
+        isl_dim_out,
+        isl_dim_set = isl_dim_out,
+        isl_dim_div,
+        isl_dim_all
+} isl_dim_type;
+
+// ast_type.h
+typedef enum {
+        isl_ast_op_error = -1,
+        isl_ast_op_and,
+        isl_ast_op_and_then,
+        isl_ast_op_or,
+        isl_ast_op_or_else,
+        isl_ast_op_max,
+        isl_ast_op_min,
+        isl_ast_op_minus,
+        isl_ast_op_add,
+        isl_ast_op_sub,
+        isl_ast_op_mul,
+        isl_ast_op_div,
+        isl_ast_op_fdiv_q,      /* Round towards -infty */
+        isl_ast_op_pdiv_q,      /* Dividend is non-negative */
+        isl_ast_op_pdiv_r,      /* Dividend is non-negative */
+        isl_ast_op_zdiv_r,      /* Result only compared against zero */
+        isl_ast_op_cond,
+        isl_ast_op_select,
+        isl_ast_op_eq,
+        isl_ast_op_le,
+        isl_ast_op_lt,
+        isl_ast_op_ge,
+        isl_ast_op_gt,
+        isl_ast_op_call,
+        isl_ast_op_access,
+        isl_ast_op_member,
+        isl_ast_op_address_of
+} isl_ast_op_type;
+
+typedef enum {
+        isl_ast_expr_error = -1,
+        isl_ast_expr_op,
+        isl_ast_expr_id,
+        isl_ast_expr_int
+} isl_ast_expr_type ;
+
+typedef enum {
+        isl_ast_node_error = -1,
+        isl_ast_node_for = 1,
+        isl_ast_node_if,
+        isl_ast_node_block,
+        isl_ast_node_mark,
+        isl_ast_node_user
+} isl_ast_node_type;
+
+typedef enum {
+        isl_ast_loop_error = -1,
+        isl_ast_loop_default = 0,
+        isl_ast_loop_atomic,
+        isl_ast_loop_unroll,
+        isl_ast_loop_separate
+} isl_ast_loop_type;
+
+// flow.h
+typedef int (*isl_access_level_before)(void *first, void *second);
+typedef isl_restriction *(*isl_access_restrict)(
+        isl_map *source_map, isl_set *sink,
+        void *source_user, void *user);
+
+// polynomial_type.h
+typedef enum {
+        isl_fold_min,
+        isl_fold_max,
+        isl_fold_list
+} isl_fold;
+"""
+
+PY_PREAMBLE = """
+from __future__ import print_function
+
+import six
+
+
+from islpy._isl_cffi import ffi
+lib = ffi.dlopen("libisl.so.13")
+
+from cffi import FFI
+libc_ffi = FFI()
+libc_ffi.cdef('''
+    char *strdup(const char *s);
+    void free(void *ptr);
+    ''')
+
+libc = libc_ffi.dlopen(None)
+
+
+class Error(RuntimeError):
+    pass
+
+
+
+_context_use_map = {}
+
+def _ref_ctx(ctx_data):
+    iptr = int(ffi.cast("intptr_t", ctx_data))
+    _context_use_map[iptr] = _context_use_map.get(iptr, 0) + 1
+
+def _deref_ctx(ctx_data):
+    iptr = int(ffi.cast("intptr_t", ctx_data))
+    _context_use_map[iptr] -= 1
+    if not _context_use_map[iptr]:
+        del _context_use_map[iptr]
+        lib.isl_ctx_free(ctx_data)
+
+
+class _ISLObjectBase(object):
+    def _setup(self, data):
+        assert not hasattr(self, "data")
+        assert isinstance(data, ffi.CData)
+        self.data =  data
+
+        _ref_ctx(self._get_ctx_data())
+
+    def _reset(self, data):
+        assert self.data is not None
+        assert isinstance(data, ffi.CData)
+
+        _deref_ctx(self._get_ctx_data())
+        self.data = data
+        _ref_ctx(self._get_ctx_data())
+
+    def _release(self):
+        if self.data is None:
+            raise Error("cannot release already-released object")
+
+        data = self.data
+        _deref_ctx(self._get_ctx_data())
+        self.data = None
+        return data
+
+
+def _instantiate(cls, data):
+    result = _ISLObjectBase.__new__(_ISLObjectBase)
+    result.__class__ = cls
+    result._setup(data)
+    return result
+
+
+class _ManagedCString(object):
+    def __init__(self, cdata):
+        self.data = libc.strdup(cdata)
+        if self.data == libc_ffi.NULL:
+            raise Error("strdup() failed")
+
+    def release(self):
+        if self.data is None:
+            raise Error("cannot release already-released object")
+
+        data = self.data
+        self.data = None
+        return data
+
+    def __del__(self):
+        libc.free(self.data)
+"""
 
 CLASS_MAP = {
         "equality": "constraint",
@@ -143,12 +356,30 @@ CLASS_MAP = {
 
 ENUMS = ["isl_dim_type", "isl_fold",
         "isl_ast_op_type", "isl_ast_expr_type",
-        "isl_ast_node_type", "isl_stat"]
+        "isl_ast_node_type", "isl_stat", "isl_error"]
 
 SAFE_TYPES = ENUMS + ["int", "unsigned", "uint32_t", "size_t", "double",
         "long", "unsigned long"]
 SAFE_IN_TYPES = SAFE_TYPES + ["const char *", "char *"]
 
+
+SPECIAL_CLASS_NAME_MAP = {
+        "ctx": "Context"
+        }
+
+
+def isl_class_to_py_class(cls_name):
+    if cls_name.startswith("isl_"):
+        cls_name = cls_name[4:]
+
+    try:
+        return SPECIAL_CLASS_NAME_MAP[cls_name]
+    except KeyError:
+        result = cls_name.title().replace("_", "")
+        result = result.replace("Qpoly", "QPoly")
+        return result
+
+
 # {{{ parser
 
 DECL_RE = re.compile(r"""
@@ -208,36 +439,11 @@ def split_at_unparenthesized_commas(s):
     yield s[last_start:i]
 
 
-def to_py_class(cls):
-    if cls.startswith("isl_"):
-        cls = cls[4:]
-
-    if cls == "ctx":
-        return "Context"
-
-    upper_next = True
-    result = ""
-
-    for c in cls:
-        if c == "_":
-            upper_next = True
-        else:
-            if upper_next:
-                result += c.upper()
-                upper_next = False
-            else:
-                result += c
-
-    result = result.replace("Qpoly", "QPoly")
-
-    return result
-
-
-class Retry(RuntimeError):
+class BadArg(ValueError):
     pass
 
 
-class BadArg(ValueError):
+class Retry(ValueError):
     pass
 
 
@@ -254,14 +460,21 @@ def parse_arg(arg):
         arg_match = FUNC_PTR_RE.match(arg)
         assert arg_match is not None, "fptr: %s" % arg
 
-        return_base_type = arg_match.group(1)
+        return_semantics, ret_words = filter_semantics(
+                arg_match.group(1).split())
+        ret_words = [w for w in ret_words if w not in ["struct", "enum"]]
+        return_base_type, = ret_words
+
         return_ptr = arg_match.group(2)
         name = arg_match.group(3)
         args = [parse_arg(i.strip())
                 for i in split_at_unparenthesized_commas(arg_match.group(4))]
 
         return CallbackArgument(name.strip(),
-                return_base_type.strip(), return_ptr.strip(), args)
+                return_semantics,
+                return_base_type,
+                return_ptr.strip(),
+                args)
 
     words = arg.split()
     semantics, words = filter_semantics(words)
@@ -271,11 +484,16 @@ def parse_arg(arg):
     rebuilt_arg = " ".join(words)
     arg_match = ARG_RE.match(rebuilt_arg)
 
+    base_type = arg_match.group(1).strip()
+
+    if base_type == "isl_args":
+        raise BadArg("isl_args not supported")
+
     assert arg_match is not None, rebuilt_arg
     return Argument(
             name=arg_match.group(3),
             semantics=semantics,
-            base_type=arg_match.group(1).strip(),
+            base_type=base_type,
             ptr=arg_match.group(2).strip())
 
 
@@ -305,11 +523,20 @@ class FunctionData:
         finally:
             inf.close()
 
-        # split at semicolons
+        # heed continuations, split at semicolons
         new_lines = []
-        for l in lines:
-            l = INLINE_SEMICOLON_RE.sub(";\n", l)
-            new_lines.extend(l.split("\n"))
+        i = 0
+        while i < len(lines):
+            my_line = lines[i].strip()
+            i += 1
+
+            while my_line.endswith("\\"):
+                my_line = my_line[:-1] + lines[i].strip()
+                i += 1
+
+            if not my_line.strip().startswith("#"):
+                my_line = INLINE_SEMICOLON_RE.sub(";\n", my_line)
+                new_lines.extend(my_line.split("\n"))
 
         lines = new_lines
 
@@ -319,7 +546,6 @@ class FunctionData:
             l = lines[i].strip()
 
             if (not l
-                    or l.startswith("#")
                     or l.startswith("extern")
                     or STRUCT_DECL_RE.search(l)
                     or l.startswith("typedef")
@@ -374,11 +600,16 @@ class FunctionData:
             return
 
         return_base_type = decl_match.group(1)
+        return_base_type = return_base_type.replace("ISL_DEPRECATED", "").strip()
+
         return_ptr = decl_match.group(2)
         c_name = decl_match.group(3)
         args = [i.strip()
                 for i in split_at_unparenthesized_commas(decl_match.group(4))]
 
+        if args == ["void"]:
+            args = []
+
         if c_name in [
                 "ISL_ARG_DECL",
                 "ISL_DECLARE_LIST",
@@ -388,6 +619,10 @@ class FunctionData:
                 "ISL_DECLARE_MULTI_NEG",
                 "ISL_DECLARE_MULTI_DIMS",
                 "ISL_DECLARE_MULTI_WITH_DOMAIN",
+                "isl_malloc_or_die",
+                "isl_calloc_or_die",
+                "isl_realloc_or_die",
+                "isl_handle_error",
                 ]:
             return
 
@@ -403,6 +638,9 @@ class FunctionData:
         if found_class:
             name = name[len(cls)+1:]
 
+        if name.startswith("2"):
+            name = "two_"+name[1:]
+
         # Don't be tempted to chop off "_val"--the "_val" versions of
         # some methods are incompatible with the isl_int ones.
         #
@@ -421,9 +659,6 @@ class FunctionData:
 
         assert found_class, name
 
-        if name in ["free", "cow", "dump"]:
-            return
-
         try:
             args = [parse_arg(arg) for arg in args]
         except BadArg:
@@ -466,117 +701,122 @@ class FunctionData:
 # }}}
 
 
-def get_callback(cb_name, cb):
-    body = []
-    passed_args = []
+# {{{ header writer
 
-    assert cb.args[-1].name == "user"
+def write_classes_to_header(header_f):
+    for cls_name in CLASSES:
+        header_f.write("struct isl_{name}_struct;\n".format(name=cls_name))
+        header_f.write(
+                "typedef struct isl_{name}_struct isl_{name};\n"
+                .format(name=cls_name))
 
-    for arg in cb.args[:-1]:
-        if arg.base_type.startswith("isl_"):
-            if arg.ptr != "*":
-                raise SignatureNotSupported("unsupported callback arg: %s %s" % (
-                    arg.base_type, arg.ptr))
-            arg_cls = arg.base_type[4:]
 
-            if arg.semantics is not SEM_TAKE:
-                raise SignatureNotSupported("non-take callback arg")
+def write_method_header(header_f, method):
+    header_f.write(
+            "{ret_type} {ret_ptr}{name}({args});\n"
+            .format(
+                ret_type=method.return_base_type,
+                ret_ptr=method.return_ptr,
+                name=method.c_name,
+                args=", ".join(arg.c_declarator() for arg in method.args)))
 
-            passed_args.append("arg_%s" % arg.name)
+# }}}
 
-            body.append("""
-                std::auto_ptr<%(arg_cls)s> wrapped_arg_%(name)s(
-                    new %(arg_cls)s(c_arg_%(name)s));
-                py::object arg_%(name)s(
-                    handle_from_new_ptr(wrapped_arg_%(name)s.get()));
-                wrapped_arg_%(name)s.release();
-                """ % dict(
-                    arg_cls=arg_cls,
-                    name=arg.name,
-                    ))
-        else:
-            raise SignatureNotSupported("unsupported callback arg: %s %s" % (
-                arg.base_type, arg.ptr))
-
-    return """
-        static %(ret_type)s %(cb_name)s(%(input_args)s)
-        {
-            py::object &py_cb = *reinterpret_cast<py::object *>(c_arg_user);
-            try
-            {
-              %(body)s
-              py::object retval = py_cb(%(passed_args)s);
-              if (retval.ptr() == Py_None)
-              {
-                #if !defined(ISLPY_ISL_VERSION) || (ISLPY_ISL_VERSION >= 15)
-                  return isl_stat_ok;
-                #else
-                  return 0;
-                #endif
-              }
-              else
-                return py::extract<%(ret_type)s>(retval);
-            }
-            catch (py::error_already_set)
-            {
-              std::cout << "[islpy warning] A Python exception occurred in "
-                "a call back function, ignoring:" << std::endl;
-              PyErr_Print();
-              #if !defined(ISLPY_ISL_VERSION) || (ISLPY_ISL_VERSION >= 15)
-                return isl_stat_error;
-              #else
-                return -1;
-              #endif
-            }
-            catch (std::exception &e)
-            {
-              std::cerr << "[islpy] An exception occurred in "
-                "a Python callback query:" << std::endl
-                << e.what() << std::endl;
-              std::cout << "[islpy] Aborting now." << std::endl;
-              #if !defined(ISLPY_ISL_VERSION) || (ISLPY_ISL_VERSION >= 15)
-                return isl_stat_error;
-              #else
-                return -1;
-              #endif
-            }
-        }
-        """ % dict(
-                ret_type="%s %s" % (cb.return_base_type, cb.return_ptr),
-                cb_name=cb_name,
-                input_args=(
-                    ", ".join("%s %sc_arg_%s" % (arg.base_type, arg.ptr, arg.name)
-                        for arg in cb.args)),
-                body="\n".join(body),
-                passed_args=", ".join(passed_args))
 
+# {{{ python wrapper writer
 
-# {{{ wrapper generator
+def write_classes_to_wrapper(wrapper_f):
+    gen = PythonCodeGenerator()
 
-def write_wrapper(outf, meth):
-    body = []
-    checks = []
+    gen("# {{{ declare classes")
+    gen("")
+    for cls_name in CLASSES:
+        py_cls = isl_class_to_py_class(cls_name)
+        gen("class {cls}(_ISLObjectBase):".format(cls=py_cls))
+        with Indentation(gen):
+            gen("_base_name = "+repr(cls_name))
+            gen("")
+
+            if cls_name == "ctx":
+                gen("""
+                    def _get_ctx_data(self):
+                        return self.data
+
+                    def __del__(self):
+                        if self.data is not None:
+                            self._release()
+                    """)
+
+            else:
+                gen("""
+                    def _get_ctx_data(self):
+                        return lib.isl_{cls}_get_ctx(self.data)
+
+                    def __del__(self):
+                        if self.data is not None:
+                            lib.isl_{cls}_free(self._release())
+                    """
+                    .format(cls=cls_name))
+
+            if cls_name not in NON_COPYABLE_WITH_ISL_PREFIX:
+                gen("""
+                    def _copy(self):
+                        assert self.data is not None
+
+                        data = lib.isl_{cls}_copy(self.data)
+                        if data == ffi.NULL:
+                            raise Error("failed to copy instance of {py_cls}")
+
+                        return _instantiate({py_cls}, data)
+                    """
+                    .format(cls=cls_name, py_cls=py_cls))
+
+                gen("")
+
+    gen("")
+    gen("# }}}")
+    gen("")
+    gen("")
+
+    wrapper_f.write(gen.get())
+
+
+def gen_conversions(gen, tgt_cls, name):
+    conversions = IMPLICIT_CONVERSIONS.get(tgt_cls, [])
+    for src_cls, conversion_method in conversions:
+        gen_conversions(gen, src_cls, name)
+
+        gen("""
+            if isinstance({name}, {py_src_cls}):
+                {name} = {py_cls}.{conversion_method}({name})
+            """
+            .format(
+                name=name,
+                py_src_cls=isl_class_to_py_class(src_cls),
+                py_cls=isl_class_to_py_class(tgt_cls),
+                conversion_method=conversion_method))
+
+
+def write_method_wrapper(gen, cls_name, meth):
+    pre_call = PythonCodeGenerator()
+    post_call = PythonCodeGenerator()
     docs = []
 
     passed_args = []
     input_args = []
-    post_call = []
-    extra_ret_vals = []
-    extra_ret_descrs = []
-    preamble = []
-
-    arg_names = []
+    doc_args = []
+    ret_vals = []
+    ret_descrs = []
 
     arg_idx = 0
     while arg_idx < len(meth.args):
         arg = meth.args[arg_idx]
-        arg_names.append(arg.name)
 
         if isinstance(arg, CallbackArgument):
+            raise SignatureNotSupported("callback")
             if arg.return_base_type not in SAFE_IN_TYPES or arg.return_ptr:
                 raise SignatureNotSupported("non-int callback")
 
-            arg_names.pop()
             arg_idx += 1
             if meth.args[arg_idx].name != "user":
                 raise SignatureNotSupported("unexpected callback signature")
@@ -587,8 +827,6 @@ def write_wrapper(outf, meth):
             passed_args.append(cb_name)
             passed_args.append("&py_%s" % arg.name)
 
-            preamble.append(get_callback(cb_name, arg))
-
             docs.append(":param %s: callback(%s)"
                     % (arg.name, ", ".join(
                         sub_arg.name
@@ -596,8 +834,11 @@ def write_wrapper(outf, meth):
                         if sub_arg.name != "user")))
 
         elif arg.base_type in SAFE_IN_TYPES and not arg.ptr:
-            passed_args.append("arg_"+arg.name)
-            input_args.append("%s arg_%s" % (arg.base_type, arg.name))
+            passed_args.append(arg.name)
+            input_args.append(arg.name)
+            doc_args.append(arg.name)
+
+            pre_call("# no argument processing for {}".format(arg.name))
 
             doc_cls = arg.base_type
             if doc_cls.startswith("isl_"):
@@ -606,160 +847,111 @@ def write_wrapper(outf, meth):
             docs.append(":param %s: :class:`%s`" % (arg.name, doc_cls))
 
         elif arg.base_type in ["char", "const char"] and arg.ptr == "*":
-            if arg.semantics is SEM_KEEP:
-                passed_args.append("strdup(%s)" % arg.name)
+            c_name = "_cstr_"+arg.name
+
+            pre_call('{c_name} = ffi.new("char[]", {arg_name}.encode())'
+                    .format(c_name=c_name, arg_name=arg.name))
+
+            if arg.semantics is SEM_TAKE:
+                pre_call(
+                        "{c_name} = _ManagedCString({c_name})"
+                        .format(c_name=c_name))
+                passed_args.append(c_name + "._release()")
             else:
-                passed_args.append(arg.name)
-            input_args.append("%s *%s" % (arg.base_type, arg.name))
+                passed_args.append(c_name)
+            input_args.append(arg.name)
 
             docs.append(":param %s: string" % arg.name)
 
         elif arg.base_type == "int" and arg.ptr == "*":
             if arg.name in ["exact", "tight"]:
-                body.append("int arg_%s;" % arg.name)
-                passed_args.append("&arg_%s" % arg.name)
-                extra_ret_vals.append("arg_%s" % arg.name)
-                extra_ret_descrs.append("%s (integer)" % arg.name)
-                arg_names.pop()
+                c_name = "cint_"+arg.name
+                pre_call('{c_name} = ffi.new("int[1]")'.format(c_name=c_name))
+
+                passed_args.append(c_name)
+                ret_vals.append("{c_name}[0]".format(c_name=c_name))
+                ret_descrs.append("%s (integer)" % arg.name)
             else:
                 raise SignatureNotSupported("int *")
 
         elif arg.base_type == "isl_val" and arg.ptr == "*" and arg_idx > 0:
             # {{{ val input argument
 
-            arg_descr = ":param %s: :class:`Val`" % arg.name
-            input_args.append("py::object py_%s" % arg.name)
-            checks.append("""
-                std::auto_ptr<val> auto_arg_%(name)s;
-                py::extract<val *> ex_%(name)s(py_%(name)s);
-                isl_ctx *ctx_for_%(name)s =
-                    %(first_arg_base_type)s_get_ctx(arg_%(first_arg)s.m_data);
-
-                if (ex_%(name)s.check())
-                {
-                  val *arg_%(name)s = ex_%(name)s();
-                  if (!auto_arg_%(name)s->is_valid())
-                    throw isl::error(
-                      "passed invalid val for %(name)s");
-
-                  isl_val *tmp_ptr = isl_val_copy(arg_%(name)s->m_data);
-                  if (!tmp_ptr)
-                      throw isl::error("failed to copy arg %(name)s");
-                  auto_arg_%(name)s = std::auto_ptr<val>(new val(tmp_ptr));
-                }
-                else if (PyLong_Check(py_%(name)s.ptr()))
-                {
-                  long value = PyLong_AsLong(py_%(name)s.ptr());
-                  if (PyErr_Occurred())
-                    throw py::error_already_set();
-
-                  isl_val *tmp_ptr = isl_val_int_from_si(ctx_for_%(name)s, value);
-                  if (!tmp_ptr)
-                      throw isl::error("failed to create arg %(name)s from integer");
-                  auto_arg_%(name)s = std::auto_ptr<val>(new val(tmp_ptr));
-                }
-                """ % dict(
-                    name=arg.name,
-                    first_arg_base_type=meth.args[0].base_type,
-                    first_arg=meth.args[0].name,
-                    ))
-
-            if sys.version_info < (3,):
-                checks.append("""
-                    else if (PyInt_Check(py_%(name)s.ptr()))
-                    {
-                      isl_val *tmp_ptr = isl_val_int_from_si(ctx_for_%(name)s,
-                          PyInt_AsLong(py_%(name)s.ptr()));
-                      if (!tmp_ptr)
-                          throw isl::error("failed to create arg "
-                              "%(name)s from integer");
-                      auto_arg_%(name)s = std::auto_ptr<val>(new val(tmp_ptr));
-                    }
-                    """ % dict(
-                        name=arg.name,
-                        ))
-
-            checks.append("""
-                else
-                {
-                  throw isl::error("unrecognized argument for %(name)s");
-                }
-                """ % dict(
-                    name=arg.name,
-                    ))
+            val_name = "_val_" + arg.name
 
-            if arg.semantics is None and arg.base_type != "isl_ctx":
-                raise Undocumented(meth)
+            pre_call("""
+                if isinstance({name}, Val):
+                    {val_name} = {name}._copy()
+
+                elif isinstance({name}, six.integer_types):
+                    _cdata_{name} = lib.isl_val_int_from_si(
+                        {arg0_name}._get_ctx_data(), {name})
+
+                    if _cdata_{name} == ffi.NULL:
+                        raise Error("isl_val_int_from_si failed")
+
+                    {val_name} = _instantiate(Val, _cdata_{name})
+                """
+                .format(
+                    arg0_name=meth.args[0].name,
+                    name=arg.name,
+                    val_name=val_name))
 
             if arg.semantics is SEM_TAKE:
-                post_call.append("auto_arg_%s.release();" % arg.name)
+                passed_args.append(val_name + "._release()")
+            else:
+                passed_args.append(val_name + ".data")
+            input_args.append(arg.name)
 
-            passed_args.append("auto_arg_%s->m_data" % arg.name)
-            docs.append(arg_descr)
+            docs.append(":param %s: :class:`Val`" % arg.name)
 
             # }}}
 
         elif arg.base_type.startswith("isl_") and arg.ptr == "*":
             # {{{ isl types input arguments
 
-            need_nonconst = False
+            gen_conversions(pre_call, arg.base_type, arg.name)
+
+            arg_py_cls = isl_class_to_py_class(arg.base_type)
+            pre_call("if not isinstance({name}, {py_cls}):"
+                    .format(
+                        name=arg.name, py_cls=arg_py_cls))
+            with Indentation(pre_call):
+                pre_call('raise Error("{name} is not a {py_cls}")'
+                    .format(
+                        name=arg.name, py_cls=arg_py_cls))
 
             arg_cls = arg.base_type[4:]
-            arg_descr = ":param %s: :class:`%s`" % (arg.name, to_py_class(arg_cls))
+            arg_descr = ":param %s: :class:`%s`" % (
+                    arg.name, isl_class_to_py_class(arg_cls))
 
             if arg.semantics is None and arg.base_type != "isl_ctx":
                 raise Undocumented(meth)
 
-            checks.append("""
-                if (!arg_%(name)s.is_valid())
-                  throw isl::error(
-                    "passed invalid arg to isl_%(meth)s for %(name)s");
-                """ % dict(name=arg.name, meth="%s_%s" % (meth.cls, meth.name)))
-
             copyable = arg_cls not in NON_COPYABLE
             if arg.semantics is SEM_TAKE:
                 if copyable:
-                    checks.append("""
-                        if (!arg_%(name)s.is_valid())
-                          throw isl::error(
-                            "passed invalid arg to isl_%(meth)s for %(name)s");
-                        std::auto_ptr<%(cls)s> auto_arg_%(name)s;
-                        {
-                            isl_%(cls)s *tmp_ptr =
-                                isl_%(cls)s_copy(arg_%(name)s.m_data);
-                            if (!tmp_ptr)
-                                throw isl::error("failed to copy arg "
-                                    "%(name)s on entry to %(meth)s");
-                            auto_arg_%(name)s = std::auto_ptr<%(cls)s>(
-                                new %(cls)s(tmp_ptr));
-                        }
-                        """ % dict(
-                            name=arg.name,
-                            meth="%s_%s" % (meth.cls, meth.name),
-                            cls=arg_cls))
-
-                    post_call.append("auto_arg_%s.release();" % arg.name)
-                    passed_args.append("auto_arg_%s->m_data" % arg.name)
+                    copy_name = "_copy_"+arg.name
+                    pre_call('{copy_name} = {name}._copy()'
+                            .format(copy_name=copy_name, name=arg.name))
 
-                else:
-                    need_nonconst = True
+                    passed_args.append(copy_name+"._release()")
 
+                else:
                     if not (arg_idx == 0 and meth.is_mutator):
-                        post_call.append("arg_%s.invalidate();" % arg.name)
-
-                    passed_args.append("arg_%s.m_data" % arg.name)
-
-                    if arg_idx == 0 and meth.is_mutator:
+                        passed_args.append(arg.name+"._release()")
                         arg_descr += " (mutated in-place)"
                     else:
+                        passed_args.append(arg.name+".data")
                         arg_descr += " (:ref:`becomes invalid <auto-invalidation>`)"
-            else:
-                passed_args.append("arg_%s.m_data" % arg.name)
 
-            if need_nonconst:
-                input_args.append("%s &%s" % (arg_cls, "arg_"+arg.name))
+            elif arg.semantics is SEM_KEEP or arg.semantics is None:
+                passed_args.append("%s.data" % arg.name)
+
             else:
-                input_args.append("%s const &%s" % (arg_cls, "arg_"+arg.name))
+                raise RuntimeError("unexpected semantics: %s" % arg.semantics)
+
+            input_args.append(arg.name)
 
             docs.append(arg_descr)
 
@@ -771,44 +963,31 @@ def write_wrapper(outf, meth):
             if arg.semantics is not SEM_GIVE:
                 raise SignatureNotSupported("non-give secondary ptr return value")
 
-            ret_cls = arg.base_type[4:]
+            pre_call(
+                    '_retptr_{name} = ffi.new("{cls} *")'
+                    .format(name=arg.name, cls=arg.base_type))
 
-            arg_names.pop()
-            body.append("%s *ret_%s;" % (arg.base_type, arg.name))
-            passed_args.append("&ret_%s" % arg.name)
-
-            post_call.append("""
-                py::object py_ret_%(name)s;
-                if (ret_%(name)s)
-                {
-                  std::auto_ptr<%(ret_cls)s> auto_ret_%(name)s(
-                    new %(ret_cls)s(ret_%(name)s));
-                  py_ret_%(name)s = py::object(
-                    handle_from_new_ptr(auto_ret_%(name)s.get()));
-                  auto_ret_%(name)s.release();
-                }
-                """ % dict(name=arg.name, ret_cls=ret_cls))
-
-            extra_ret_vals.append("py_ret_%s" % arg.name)
-            extra_ret_descrs.append(
-                    "%s (:class:`%s`)" % (arg.name, to_py_class(ret_cls)))
+            passed_args.append("ffi.addressof(_retptr_{name})".format(name=arg.name))
 
-            # }}}
+            py_cls = isl_class_to_py_class(arg.base_type)
+            post_call("""
+                if _retptr_{name} == ffi.NULL:
+                    _ret_{name} = None
+                else:
+                    _ret_{name} = _instantiate({py_cls}, _retptr_{name})
+                """
+                .format(name=arg.name, cls=arg.base_type, py_cls=py_cls))
 
-        elif arg.base_type == "FILE" and arg.ptr == "*":
-            if sys.version_info >= (3,):
-                raise SignatureNotSupported(
-                        "arg type %s %s" % (arg.base_type, arg.ptr))
+            ret_vals.append("_ret_" + arg.name)
+            ret_descrs.append("%s (:class:`%s`)" % (arg.name, py_cls))
 
-            passed_args.append("PyFile_AsFile(arg_%s.ptr())" % arg.name)
-            input_args.append("py::object %s" % ("arg_"+arg.name))
-            docs.append(":param %s: :class:`file`-like "
-                    "(NOTE: This will cease to be supported in Python 3.)"
-                    % arg.name)
+            # }}}
 
         elif (arg.base_type == "void"
                 and arg.ptr == "*"
                 and arg.name == "user"):
+            raise SignatureNotSupported("void user")
+
             body.append("Py_INCREF(arg_%s.ptr());" % arg.name)
             passed_args.append("arg_%s.ptr()" % arg.name)
             input_args.append("py::object %s" % ("arg_"+arg.name))
@@ -821,72 +1000,35 @@ def write_wrapper(outf, meth):
             raise SignatureNotSupported("arg type %s %s" % (arg.base_type, arg.ptr))
 
         arg_idx += 1
+        pre_call("")
 
-    processed_return_type = "%s %s" % (meth.return_base_type, meth.return_ptr)
-
-    if meth.return_base_type == "void" and not meth.return_ptr:
-        result_capture = ""
-    else:
-        result_capture = "%s %sresult = " % (meth.return_base_type, meth.return_ptr)
-
-    body = checks + body
-
-    body.append("%s%s(%s);" % (
-        result_capture, meth.c_name, ", ".join(passed_args)))
-
-    body += post_call
+    pre_call(
+            "_result = lib.{c_name}({args})"
+            .format(c_name=meth.c_name, args=", ".join(passed_args)))
+    pre_call("")
 
     # {{{ return value processing
 
-    if meth.return_base_type in ["int", "isl_stat"] and not meth.return_ptr:
-        body.append("""
-            #if !defined(ISLPY_ISL_VERSION) || (ISLPY_ISL_VERSION >= 15)
-              if (result == isl_stat_error)
-            #else
-              if (result == -1)
-            #endif
-            {
-              throw isl::error("call to isl_%(cls)s_%(name)s failed");
-            }""" % {"cls": meth.cls, "name": meth.name})
-
-        if meth.name.startswith("is_") or meth.name.startswith("has_"):
-            processed_return_type = "bool"
-
-        ret_descr = processed_return_type
-
-        if extra_ret_vals:
-            if len(extra_ret_vals) == 1:
-                processed_return_type = "py::object"
-                body.append("return py::object(%s);" % extra_ret_vals[0])
-                ret_descr = extra_ret_descrs[0]
-            else:
-                processed_return_type = "py::object"
-                body.append("return py::make_tuple(%s);" % ", ".join(extra_ret_vals))
-                ret_descr = "tuple: (%s)" % (", ".join(extra_ret_descrs))
-        else:
-            body.append("return result;")
+    if meth.return_base_type == "isl_stat" and not meth.return_ptr:
+        post_call("if _result == lib.isl_stat_error:")
+        with Indentation(post_call):
+            post_call('raise Error("call to \\"{}\\" failed")'.format(meth.c_name))
 
     elif meth.return_base_type == "isl_bool" and not meth.return_ptr:
-        if extra_ret_vals:
-            raise NotImplementedError("extra ret val with isl_bool")
-
-        body.append("""
-            if (result == isl_bool_error)
-            {
-              throw isl::error("call to isl_%(cls)s_%(name)s failed");
-            }""" % {"cls": meth.cls, "name": meth.name})
-
-        processed_return_type = "bool"
-        ret_descr = "bool"
+        post_call("if _result == lib.isl_bool_error:")
+        with Indentation(post_call):
+            post_call('raise Error("call to \\"{}\\" failed")'.format(meth.c_name))
 
-        body.append("return result;")
+        ret_vals.insert(0, "_result == lib.isl_bool_true")
+        ret_descrs.insert(0, "bool")
 
     elif meth.return_base_type in SAFE_TYPES and not meth.return_ptr:
-        if extra_ret_vals:
-            raise NotImplementedError("extra ret val with safe type")
+        ret_vals.insert(0, "_result")
+        ret_descrs.insert(0, meth.return_base_type)
 
-        body.append("return result;")
-        ret_descr = processed_return_type
+    elif (meth.return_base_type.startswith("isl_")
+            and meth.return_semantics is SEM_NULL):
+        assert not meth.is_mutator
 
     elif meth.return_base_type.startswith("isl_"):
         assert meth.return_ptr == "*", meth
@@ -894,94 +1036,59 @@ def write_wrapper(outf, meth):
         ret_cls = meth.return_base_type[4:]
 
         if meth.is_mutator:
-            if extra_ret_vals:
+            if ret_vals:
                 meth.mutator_veto = True
                 raise Retry()
 
-            processed_return_type = "isl::%s &" % ret_cls
-            body.append("arg_%s.m_data = result;" % meth.args[0].name)
-            body.append("return arg_%s;" % meth.args[0].name)
+            post_call("%s._reset(_result)" % meth.args[0].name)
 
-            ret_descr = ":class:`%s` (self)" % to_py_class(ret_cls)
+            ret_vals.insert(0, meth.args[0].name)
+            ret_descrs.insert(0,
+                    ":class:`%s` (self)" % isl_class_to_py_class(ret_cls))
         else:
-            processed_return_type = "py::object"
-            isl_obj_ret_val = \
-                    "py::object(handle_from_new_ptr(new %s(result)))" % ret_cls
-
-            if extra_ret_vals:
-                isl_obj_ret_val = "py::make_tuple(%s, %s)" % (
-                        isl_obj_ret_val, ", ".join(extra_ret_vals))
-                ret_descr = "tuple: (:class:`%s`, %s)" % (
-                        to_py_class(ret_cls), ", ".join(extra_ret_descrs))
-            else:
-                ret_descr = ":class:`%s`" % to_py_class(ret_cls)
-
             if meth.return_semantics is None and ret_cls != "ctx":
                 raise Undocumented(meth)
 
             if meth.return_semantics is not SEM_GIVE and ret_cls != "ctx":
                 raise SignatureNotSupported("non-give return")
 
-            body.append("""
-                if (result)
-                {
-                  try
-                  { return %(ret_val)s; }
-                  catch (...)
-                  {
-                    isl_%(ret_cls)s_free(result);
-                    throw;
-                  }
-                }
-                else
-                {
-                  throw isl::error("call to isl_%(cls)s_%(name)s failed");
-                }
-                """ % {
-                    "ret_cls": ret_cls,
-                    "ret_val": isl_obj_ret_val,
-                    "cls": meth.cls,
-                    "name": meth.name,
-                    })
+            post_call("if _result == ffi.NULL:")
+            with Indentation(post_call):
+                post_call(
+                        'raise Error("call to \\"{}\\" failed")'
+                        .format(meth.c_name))
+
+            py_ret_cls = isl_class_to_py_class(ret_cls)
+            ret_vals.insert(0, "_instantiate({}, _result)".format(py_ret_cls))
+            ret_descrs.insert(0,  ":class:`%s`" % py_ret_cls)
 
     elif meth.return_base_type in ["const char", "char"] and meth.return_ptr == "*":
-        if extra_ret_vals:
-            raise NotImplementedError("extra ret val with string")
+        post_call("if _result != ffi.NULL:")
+        with Indentation(post_call):
+            post_call("_str_ret = ffi.string(_result)")
+        post_call("else:")
+        with Indentation(post_call):
+            post_call("_str_ret = None")
+
+        ret_vals.insert(0, "_str_ret")
 
-        processed_return_type = "py::object"
-        body.append("""
-            if (result)
-              return py::object(std::string(result));
-            else
-              return py::object();
-            """)
         if meth.return_semantics is SEM_GIVE:
-            body.append("free(result);")
+            post_call("libc.free(_result)")
 
-        ret_descr = "string"
+        ret_descrs.insert(0, "string")
 
     elif (meth.return_base_type == "void"
             and meth.return_ptr == "*"
             and meth.name == "get_user"):
 
+        raise SignatureNotSupported("get_user")
         body.append("""
-            return py::object(py::handle<>(py::borrowed((PyObject *) result)));
+            return py::object(py::handle<>(py::borrowed((PyObject *) _result)));
             """)
         ret_descr = "a user-specified python object"
-        processed_return_type = "py::object"
 
     elif meth.return_base_type == "void" and not meth.return_ptr:
-        if extra_ret_vals:
-            processed_return_type = "py::object"
-            if len(extra_ret_vals) == 1:
-                body.append("return %s;" % extra_ret_vals[0])
-                ret_descr = extra_ret_descrs[0]
-            else:
-                body.append("return py::make_tuple(%s);"
-                        % ", ".join(extra_ret_vals))
-                ret_descr = "tuple: " + ", ".join(extra_ret_descrs)
-        else:
-            ret_descr = "None"
+        pass
 
     else:
         raise SignatureNotSupported("ret type: %s %s in %s" % (
@@ -989,109 +1096,48 @@ def write_wrapper(outf, meth):
 
     # }}}
 
-    outf.write("""
-        %s
-        %s %s_%s(%s)
-        {
-          %s
-        }
-        """ % (
-            "\n".join(preamble),
-            processed_return_type, meth.cls, meth.name,
-            ", ".join(input_args),
-            "\n".join(body)))
-
-    docs = (["%s(%s)" % (meth.name, ", ".join(arg_names)), ""]
-            + docs
-            + [":return: %s" % ret_descr])
-
-    return arg_names, "\n".join(docs)
-
-# }}}
-
-
-# {{{ exposer generator
+    assert len(ret_vals) == len(ret_descrs)
 
-def write_exposer(outf, meth, arg_names, doc_str, static_decls):
-    func_name = "isl::%s_%s" % (meth.cls, meth.name)
-    py_name = meth.name
+    post_call("")
+    if len(ret_vals) == 0:
+        ret_descr = "(nothing)"
 
-    args_str = (", py::args(%s)"
-            % ", ".join('"%s"' % arg_name for arg_name in arg_names))
+    elif len(ret_vals) == 1:
+        post_call("return " + ret_vals[0])
+        ret_descr = ret_descrs[0]
 
-    if meth.name == "size" and len(meth.args) == 1:
-        py_name = "__len__"
-
-    if meth.name == "get_hash" and len(meth.args) == 1:
-        py_name = "__hash__"
-
-    extra_py_names = []
-
-    #if meth.is_static:
-    #    doc_str = "(static method)\n" + doc_str
-
-    doc_str_arg = ", \"%s\"" % doc_str.replace("\n", "\\n")
-
-    extra_stuff = args_str+doc_str_arg
-    if meth.is_mutator:
-        extra_stuff = extra_stuff+", py::return_self<>()"
+    else:
+        post_call("return " + ", ".join(ret_vals))
+        ret_descr = "(%s)" % ", ".join(ret_descrs)
 
-    wrap_class = CLASS_MAP.get(meth.cls, meth.cls)
+    docs = (["%s(%s)" % (meth.name, ", ".join(input_args)), ""]
+            + docs
+            + [":return: %s" % ret_descr])
 
-    for exp_py_name in [py_name]+extra_py_names:
-        outf.write("wrap_%s.def(\"%s\", %s%s);\n" % (
-            wrap_class, exp_py_name, func_name, extra_stuff))
-        if meth.is_static:
-            static_decls.append("wrap_%s.staticmethod(\"%s\");\n" % (
-                wrap_class, exp_py_name))
+    gen("def {name}({input_args}):"
+            .format(name=meth.name, input_args=", ".join(input_args)))
+    gen.indent()
+    gen(repr("\n".join(docs)))
+    gen("")
+    gen.extend(pre_call)
+    gen.extend(post_call)
+    gen.dedent()
+    gen("")
+
+    method_val = meth.name
+    if meth.is_static:
+        method_val = "staticmethod(%s)" % method_val
+
+    gen("{py_cls}.{name} = {method_val}"
+            .format(
+                py_cls=isl_class_to_py_class(meth.cls),
+                name=meth.name,
+                method_val=method_val))
+    gen("")
 
 # }}}
 
 
-def write_wrappers(expf, wrapf, methods):
-    undoc = []
-    static_decls = []
-
-    for meth in methods:
-        #print "TRY_WRAP:", meth
-        if meth.name.endswith("_si") or meth.name.endswith("_ui"):
-            val_versions = [
-                    meth2
-                    for meth2 in methods
-                    if meth2.cls == meth.cls
-                    and (
-                        meth2.name == meth.name[:-3]
-                        or meth2.name == meth.name[:-3] + "_val"
-                        )
-                    ]
-
-            if val_versions:
-                # no need to expose C integer versions of things
-                print("SKIP (val version available): %s -> %s"
-                        % (meth, ", ".join(str(s) for s in val_versions)))
-                continue
-
-        try:
-            arg_names, doc_str = write_wrapper(wrapf, meth)
-            write_exposer(expf, meth, arg_names, doc_str, static_decls)
-        except Undocumented:
-            undoc.append(str(meth))
-        except Retry:
-            arg_names, doc_str = write_wrapper(wrapf, meth)
-            write_exposer(expf, meth, arg_names, doc_str, static_decls)
-        except SignatureNotSupported:
-            _, e, _ = sys.exc_info()
-            print("SKIP (sig not supported: %s): %s" % (e, meth))
-        else:
-            #print "WRAPPED:", meth
-            pass
-
-    for static_decl in static_decls:
-        expf.write(static_decl)
-
-    print("SKIP (%d undocumented methods): %s" % (len(undoc), ", ".join(undoc)))
-
-
 ADD_VERSIONS = {
         "union_pw_aff": 15,
         "multi_union_pw_aff": 15,
@@ -1103,11 +1149,7 @@ ADD_VERSIONS = {
 
 def gen_wrapper(include_dirs, include_barvinok=False, isl_version=None):
     fdata = FunctionData(["."] + include_dirs)
-    if isl_version is None:
-        fdata.read_header("isl_declaration_macros_expanded.h")
-    else:
-        fdata.read_header("isl_declaration_macros_expanded_v%d.h"
-                % isl_version)
+    fdata.read_header("isl/ctx.h")
     fdata.read_header("isl/id.h")
     fdata.read_header("isl/space.h")
     fdata.read_header("isl/set.h")
@@ -1131,28 +1173,107 @@ def gen_wrapper(include_dirs, include_barvinok=False, isl_version=None):
     fdata.read_header("isl/ast.h")
     fdata.read_header("isl/ast_build.h")
 
+    if isl_version is None:
+        fdata.read_header("isl_declaration_macros_expanded.h")
+    else:
+        fdata.read_header("isl_declaration_macros_expanded_v%d.h"
+                % isl_version)
+
     if include_barvinok:
         fdata.read_header("barvinok/isl.h")
 
-    for part, classes in PART_TO_CLASSES.items():
-        expf = open("src/wrapper/gen-expose-%s.inc" % part, "wt")
-        wrapf = open("src/wrapper/gen-wrap-%s.inc" % part, "wt")
-
-        classes = [
-                cls
-                for cls in classes
-                if isl_version is None
-                or ADD_VERSIONS.get(cls) is None
-                or ADD_VERSIONS.get(cls) <= isl_version]
-
-        write_wrappers(expf, wrapf, [
-            meth
-            for cls in classes
-            for meth in fdata.classes_to_methods.get(cls, [])])
+    undoc = []
 
-        expf.close()
-        wrapf.close()
+    with open("islpy/wrapped-functions.h", "wt") as header_f:
+        with open("islpy/_isl.py", "wt") as wrapper_f:
+            write_classes_to_header(header_f)
+            header_f.write(
+                    "// AUTOMATICALLY GENERATED by gen_wrap.py -- do not edit\n\n")
+            header_f.write(HEADER_PREAMBLE)
+
+            wrapper_f.write(
+                    "# AUTOMATICALLY GENERATED by gen_wrap.py -- do not edit\n")
+            wrapper_f.write(PY_PREAMBLE)
+            write_classes_to_wrapper(wrapper_f)
+
+            wrapper_gen = PythonCodeGenerator()
+            wrapper_gen("# {{{ wrappers")
+            wrapper_gen("")
+            wrapper_gen("def _add_methods():")
+
+            with Indentation(wrapper_gen):
+                for cls_name in CLASSES:
+                    if not (
+                            isl_version is None
+                            or ADD_VERSIONS.get(cls_name) is None
+                            or ADD_VERSIONS.get(cls_name) <= isl_version):
+                        continue
+
+                    methods = [
+                            meth
+                            for meth in fdata.classes_to_methods.get(cls_name, [])]
+
+                    wrapper_gen("# {{{ " + cls_name)
+                    wrapper_gen("")
+
+                    for meth in methods:
+                        if meth.name.endswith("_si") or meth.name.endswith("_ui"):
+                            val_versions = [
+                                    meth2
+                                    for meth2 in methods
+                                    if meth2.cls == meth.cls
+                                    and (
+                                        meth2.name == meth.name[:-3]
+                                        or meth2.name == meth.name[:-3] + "_val"
+                                        )
+                                    ]
+
+                            if val_versions:
+                                # no need to expose C integer versions of things
+                                print("SKIP (val version available): %s -> %s"
+                                        % (meth, ", ".join(str(s)
+                                            for s in val_versions)))
+                                continue
+
+                        write_method_header(header_f, meth)
+
+                        if meth.name == "free":
+                            continue
+
+                        try:
+                            write_method_wrapper(wrapper_gen, cls_name, meth)
+                        except Retry:
+                            write_method_wrapper(wrapper_gen, cls_name, meth)
+                        except Undocumented:
+                            undoc.append(str(meth))
+                        except SignatureNotSupported:
+                            _, e, _ = sys.exc_info()
+                            print("SKIP (sig not supported: %s): %s" % (e, meth))
+                        else:
+                            #print "WRAPPED:", meth
+                            pass
+
+                    wrapper_gen("# }}}")
+                    wrapper_gen("")
+
+            wrapper_gen("")
+            wrapper_gen("# }}}")
+            wrapper_gen("")
+            wrapper_gen("_add_methods()")
+
+            wrapper_f.write("\n" + wrapper_gen.get())
+            wrapper_f.write("\n\n# vim: fdm=marker\n")
+
+    with open("class_list.py", "wt") as clist_f:
+        py_classes = []
+
+        for cls_name in CLASSES:
+            py_cls = isl_class_to_py_class(cls_name)
+            py_classes.append(py_cls)
+            clist_f.write("{py_cls} = _isl.{py_cls}\n".format(py_cls=py_cls))
+        clist_f.write("\nALL_CLASSES = [{}]\n".format(", ".join(py_classes)))
 
+    print("SKIP (%d undocumented methods): %s" % (len(undoc), ", ".join(undoc)))
 
 if __name__ == "__main__":
     from os.path import expanduser
diff --git a/islpy/__init__.py b/islpy/__init__.py
index cd39385..4ef1ef7 100644
--- a/islpy/__init__.py
+++ b/islpy/__init__.py
@@ -1,25 +1,119 @@
-from __future__ import division
-from __future__ import absolute_import
+from __future__ import division, absolute_import
 
 
-from islpy._isl import *  # noqa
-from islpy.version import *  # noqa
+import islpy._isl as _isl
+from islpy.version import VERSION, VERSION_TEXT  # noqa
 import six
 from six.moves import range
 
 
+Error = _isl.Error
+
+# {{{ generated by gen_wrap as class_list.py
+
+Options = _isl.Options
+Context = _isl.Context
+IdList = _isl.IdList
+ValList = _isl.ValList
+BasicSetList = _isl.BasicSetList
+BasicMapList = _isl.BasicMapList
+SetList = _isl.SetList
+MapList = _isl.MapList
+UnionSetList = _isl.UnionSetList
+ConstraintList = _isl.ConstraintList
+AffList = _isl.AffList
+PwAffList = _isl.PwAffList
+BandList = _isl.BandList
+AstExprList = _isl.AstExprList
+AstNodeList = _isl.AstNodeList
+IdToAstExpr = _isl.IdToAstExpr
+Printer = _isl.Printer
+Val = _isl.Val
+MultiVal = _isl.MultiVal
+Vec = _isl.Vec
+Mat = _isl.Mat
+Aff = _isl.Aff
+PwAff = _isl.PwAff
+UnionPwAff = _isl.UnionPwAff
+MultiAff = _isl.MultiAff
+MultiPwAff = _isl.MultiPwAff
+PwMultiAff = _isl.PwMultiAff
+UnionPwMultiAff = _isl.UnionPwMultiAff
+MultiUnionPwAff = _isl.MultiUnionPwAff
+Id = _isl.Id
+Constraint = _isl.Constraint
+Space = _isl.Space
+LocalSpace = _isl.LocalSpace
+BasicSet = _isl.BasicSet
+BasicMap = _isl.BasicMap
+Set = _isl.Set
+Map = _isl.Map
+UnionMap = _isl.UnionMap
+UnionSet = _isl.UnionSet
+Point = _isl.Point
+Vertex = _isl.Vertex
+Cell = _isl.Cell
+Vertices = _isl.Vertices
+QPolynomialFold = _isl.QPolynomialFold
+PwQPolynomialFold = _isl.PwQPolynomialFold
+UnionPwQPolynomialFold = _isl.UnionPwQPolynomialFold
+UnionPwQPolynomial = _isl.UnionPwQPolynomial
+QPolynomial = _isl.QPolynomial
+PwQPolynomial = _isl.PwQPolynomial
+Term = _isl.Term
+Band = _isl.Band
+Schedule = _isl.Schedule
+ScheduleConstraints = _isl.ScheduleConstraints
+ScheduleNode = _isl.ScheduleNode
+AccessInfo = _isl.AccessInfo
+Flow = _isl.Flow
+Restriction = _isl.Restriction
+UnionAccessInfo = _isl.UnionAccessInfo
+UnionFlow = _isl.UnionFlow
+AstExpr = _isl.AstExpr
+AstNode = _isl.AstNode
+AstPrintOptions = _isl.AstPrintOptions
+AstBuild = _isl.AstBuild
+
+ALL_CLASSES = [Options, Context, IdList, ValList, BasicSetList, BasicMapList,
+        SetList, MapList, UnionSetList, ConstraintList, AffList, PwAffList,
+        BandList, AstExprList, AstNodeList, IdToAstExpr, Printer, Val,
+        MultiVal, Vec, Mat, Aff, PwAff, UnionPwAff, MultiAff, MultiPwAff,
+        PwMultiAff, UnionPwMultiAff, MultiUnionPwAff, Id, Constraint, Space,
+        LocalSpace, BasicSet, BasicMap, Set, Map, UnionMap, UnionSet, Point,
+        Vertex, Cell, Vertices, QPolynomialFold, PwQPolynomialFold,
+        UnionPwQPolynomialFold, UnionPwQPolynomial, QPolynomial, PwQPolynomial,
+        Term, Band, Schedule, ScheduleConstraints, ScheduleNode, AccessInfo,
+        Flow, Restriction, UnionAccessInfo, UnionFlow, AstExpr, AstNode,
+        AstPrintOptions, AstBuild]
+
+# }}}
+
+
+class dim_type:  # noqa
+    cst = _isl.lib.isl_dim_cst
+    param = _isl.lib.isl_dim_param
+    in_ = _isl.lib.isl_dim_in
+    out = _isl.lib.isl_dim_out
+    set = _isl.lib.isl_dim_set
+    div = _isl.lib.isl_dim_div
+    all = _isl.lib.isl_dim_all
+
+
 _CHECK_DIM_TYPES = [
         dim_type.in_, dim_type.param, dim_type.set]
 
-ALL_CLASSES = tuple(getattr(_isl, cls) for cls in dir(_isl) if cls[0].isupper())
 EXPR_CLASSES = tuple(cls for cls in ALL_CLASSES
         if "Aff" in cls.__name__ or "Polynomial" in cls.__name__)
 
-_DEFAULT_CONTEXT = Context()
-
 
 def _add_functionality():
-    import islpy._isl as _isl  # noqa
+    def context_init(self):
+        new_ctx = Context.alloc()
+        self._setup(new_ctx.data)
+        new_ctx._release()
+
+    Context.__init__ = context_init
 
     # {{{ generic initialization, pickling
 
@@ -335,7 +429,7 @@ def _add_functionality():
 
     Id.__new__ = staticmethod(id_new)
     Id.__init__ = id_bogus_init
-    Id.user = property(Id.get_user)
+    #Id.user = property(Id.get_user)  # FIXME: reenable
     Id.name = property(Id.get_name)
 
     # }}}
@@ -820,6 +914,9 @@ def _add_functionality():
 _add_functionality()
 
 
+_DEFAULT_CONTEXT = Context()
+
+
 def _back_to_basic(new_obj, old_obj):
     # Work around set_dim_id not being available for Basic{Set,Map}
     if isinstance(old_obj, BasicSet) and isinstance(new_obj, Set):
diff --git a/py_codegen.py b/py_codegen.py
new file mode 100644
index 0000000..524a9da
--- /dev/null
+++ b/py_codegen.py
@@ -0,0 +1,101 @@
+from __future__ import division, with_statement
+
+__copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
+
+__license__ = """
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+"""
+
+
+# loosely based on
+# http://effbot.org/zone/python-code-generator.htm
+
+class Indentation(object):
+    def __init__(self, generator):
+        self.generator = generator
+
+    def __enter__(self):
+        self.generator.indent()
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        self.generator.dedent()
+
+
+class PythonCodeGenerator(object):
+    def __init__(self):
+        self.code = []
+        self.level = 0
+
+    def extend(self, sub_generator):
+        for line in sub_generator.code:
+            self.code.append(" "*(4*self.level) + line)
+
+    def get(self):
+        result = "\n".join(self.code)
+        return result
+
+    def __call__(self, s):
+        if not s.strip():
+            self.code.append("")
+        else:
+            if "\n" in s:
+                s = remove_common_indentation(s)
+
+            for l in s.split("\n"):
+                self.code.append(" "*(4*self.level) + l)
+
+    def indent(self):
+        self.level += 1
+
+    def dedent(self):
+        if self.level == 0:
+            raise RuntimeError("internal error in python code generator")
+        self.level -= 1
+
+
+# {{{ remove common indentation
+
+def remove_common_indentation(code, require_leading_newline=True):
+    if "\n" not in code:
+        return code
+
+    # accommodate pyopencl-ish syntax highlighting
+    code = code.lstrip("//CL//")
+
+    if require_leading_newline and not code.startswith("\n"):
+        return code
+
+    lines = code.split("\n")
+    while lines[0].strip() == "":
+        lines.pop(0)
+    while lines[-1].strip() == "":
+        lines.pop(-1)
+
+    if lines:
+        base_indent = 0
+        while lines[0][base_indent] in " \t":
+            base_indent += 1
+
+        for line in lines[1:]:
+            if line[:base_indent].strip():
+                raise ValueError("inconsistent indentation")
+
+    return "\n".join(line[base_indent:] for line in lines)
+
+# }}}
diff --git a/setup.py b/setup.py
index 4ce9aa8..47217eb 100644
--- a/setup.py
+++ b/setup.py
@@ -2,14 +2,11 @@
 
 
 def get_config_schema():
-    from aksetup_helper import ConfigSchema, \
-            IncludeDir, LibraryDir, Libraries, BoostLibraries, \
-            Switch, StringListOption, make_boost_base_options
+    from aksetup_helper import (ConfigSchema,
+            IncludeDir, LibraryDir, Libraries,
+            Switch, StringListOption)
 
-    return ConfigSchema(make_boost_base_options() + [
-        BoostLibraries("python"),
-
-        Switch("USE_SHIPPED_BOOST", True, "Use included Boost library"),
+    return ConfigSchema([
         Switch("USE_SHIPPED_ISL", True, "Use included copy of isl"),
         Switch("USE_SHIPPED_IMATH", True, "Use included copy of imath in isl"),
         Switch("USE_BARVINOK", False, "Include wrapper for Barvinok"),
@@ -36,7 +33,6 @@ def get_config_schema():
 def main():
     from aksetup_helper import (hack_distutils,
             get_config, setup, Extension,
-            set_up_shipped_boost_if_requested,
             check_git_submodules)
 
     check_git_submodules()
@@ -44,15 +40,15 @@ def main():
     hack_distutils(what_opt=None)
     conf = get_config(get_config_schema(), warn_about_no_config=False)
 
-    EXTRA_OBJECTS, EXTRA_DEFINES = set_up_shipped_boost_if_requested("islpy", conf)
-
-    INCLUDE_DIRS = conf["BOOST_INC_DIR"] + ["src/wrapper"]
-    LIBRARY_DIRS = conf["BOOST_LIB_DIR"]
-    LIBRARIES = conf["BOOST_PYTHON_LIBNAME"]
+    EXTRA_OBJECTS = []  # noqa
+    EXTRA_DEFINES = []  # noqa
+    INCLUDE_DIRS = conf["BOOST_INC_DIR"] + ["src/wrapper"]  # noqa
+    LIBRARY_DIRS = conf["BOOST_LIB_DIR"]  # noqa
+    LIBRARIES = conf["BOOST_PYTHON_LIBNAME"]  # noqa
 
     if conf["USE_SHIPPED_ISL"]:
         from glob import glob
-        ISL_BLACKLIST = [
+        isl_blacklist = [
                 "_templ.c", "mp_get",
                 "isl_multi_templ.c",
                 "isl_multi_apply_set.c",
@@ -64,7 +60,7 @@ def main():
 
         for fn in glob("isl/*.c"):
             blacklisted = False
-            for bl in ISL_BLACKLIST:
+            for bl in isl_blacklist:
                 if bl in fn:
                     blacklisted = True
                     break
@@ -167,12 +163,13 @@ def main():
               'Natural Language :: English',
               'Programming Language :: C++',
               'Programming Language :: Python',
-              'Programming Language :: Python :: 2.5',
               'Programming Language :: Python :: 2.6',
               'Programming Language :: Python :: 2.7',
               'Programming Language :: Python :: 3',
-              'Programming Language :: Python :: 3.2',
               'Programming Language :: Python :: 3.3',
+              'Programming Language :: Python :: 3.4',
+              'Programming Language :: Python :: Implementation :: CPython'
+              'Programming Language :: Python :: Implementation :: PyPy'
               'Topic :: Multimedia :: Graphics :: 3D Modeling',
               'Topic :: Scientific/Engineering',
               'Topic :: Scientific/Engineering :: Mathematics',
@@ -183,8 +180,11 @@ def main():
 
           packages=["islpy"],
 
+          setup_requires=["cffi>=1.1.0"],
+          cffi_modules=["simple_example_build.py:ffi"],
           install_requires=[
               "pytest>=2",
+              "cffi>=1.1.0",
               # "Mako>=0.3.6",
               "six",
               ],
diff --git a/src/wrapper/wrap_helpers.hpp b/src/wrapper/wrap_helpers.hpp
deleted file mode 100644
index 083bc3a..0000000
--- a/src/wrapper/wrap_helpers.hpp
+++ /dev/null
@@ -1,77 +0,0 @@
-#ifndef PYCUDA_WRAP_HELPERS_HEADER_SEEN
-#define PYCUDA_WRAP_HELPERS_HEADER_SEEN
-
-
-
-
-#include <boost/version.hpp>
-#include <boost/python.hpp>
-#include <boost/python/stl_iterator.hpp>
-
-
-
-
-namespace py = boost::python;
-
-
-
-
-#if (BOOST_VERSION/100) < 1035
-#warning *******************************************************************
-#warning **** Your version of Boost C++ is likely too old for islpy.    ****
-#warning *******************************************************************
-#endif
-
-
-
-
-#define PYTHON_ERROR(TYPE, REASON) \
-{ \
-  PyErr_SetString(PyExc_##TYPE, REASON); \
-  throw boost::python::error_already_set(); \
-}
-
-#define ENUM_VALUE(PREFIX, NAME) \
-  value(#NAME, PREFIX##NAME)
-
-#define DEF_SIMPLE_METHOD(NAME) \
-  def(#NAME, &cls::NAME)
-
-#define DEF_SIMPLE_METHOD_WITH_ARGS(NAME, ARGS) \
-  def(#NAME, &cls::NAME, boost::python::args ARGS)
-
-#define DEF_SIMPLE_FUNCTION(NAME) \
-  boost::python::def(#NAME, &NAME)
-
-#define DEF_SIMPLE_FUNCTION_WITH_ARGS(NAME, ARGS) \
-  boost::python::def(#NAME, &NAME, boost::python::args ARGS)
-
-#define DEF_SIMPLE_RO_MEMBER(NAME) \
-  def_readonly(#NAME, &cls::NAME)
-
-#define DEF_SIMPLE_RW_MEMBER(NAME) \
-  def_readwrite(#NAME, &cls::NAME)
-
-#define PYTHON_FOREACH(NAME, ITERABLE) \
-  BOOST_FOREACH(boost::python::object NAME, \
-      std::make_pair( \
-        boost::python::stl_input_iterator<boost::python::object>(ITERABLE), \
-        boost::python::stl_input_iterator<boost::python::object>()))
-
-#define COPY_PY_LIST(TYPE, NAME) \
-  std::copy( \
-      boost::python::stl_input_iterator<TYPE>(py_##NAME), \
-      boost::python::stl_input_iterator<TYPE>(), \
-      std::back_inserter(NAME));
-
-namespace
-{
-  template <typename T>
-  inline boost::python::handle<> handle_from_new_ptr(T *ptr)
-  {
-    return boost::python::handle<>(
-        typename boost::python::manage_new_object::apply<T *>::type()(ptr));
-  }
-}
-
-#endif
diff --git a/src/wrapper/wrap_isl.cpp b/src/wrapper/wrap_isl.cpp
deleted file mode 100644
index fbea3ee..0000000
--- a/src/wrapper/wrap_isl.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-#include "wrap_isl.hpp"
-
-void islpy_expose_part1();
-void islpy_expose_part2();
-void islpy_expose_part3();
-
-namespace isl
-{
-  ctx_use_map_t ctx_use_map;
-}
-
-
-
-namespace
-{
-  py::handle<> ISLError;
-
-  void translate_isl_error(const isl::error &err)
-  {
-    PyErr_SetObject(ISLError.get(), py::object(err.what()).ptr());
-  }
-}
-
-
-
-BOOST_PYTHON_MODULE(_isl)
-{
-  ISLError = py::handle<>(PyErr_NewException("islpy.Error", PyExc_RuntimeError, NULL));
-  py::scope().attr("Error") = ISLError;
-  py::register_exception_translator<isl::error>(translate_isl_error);
-
-  py::docstring_options doc_opt(true, false, false);
-
-  /*
-  {
-    typedef isl_options cls;
-    py::class_<cls>("Options")
-      .DEF_SIMPLE_RW_MEMBER(lp_solver)
-      .DEF_SIMPLE_RW_MEMBER(ilp_solver)
-      .DEF_SIMPLE_RW_MEMBER(pip)
-      .DEF_SIMPLE_RW_MEMBER(context)
-      .DEF_SIMPLE_RW_MEMBER(gbr)
-      .DEF_SIMPLE_RW_MEMBER(gbr_only_first)
-      .DEF_SIMPLE_RW_MEMBER(closure)
-      .DEF_SIMPLE_RW_MEMBER(bound)
-      .DEF_SIMPLE_RW_MEMBER(bernstein_recurse)
-      .DEF_SIMPLE_RW_MEMBER(bernstein_triangulate)
-      .DEF_SIMPLE_RW_MEMBER(pip_symmetry)
-      .DEF_SIMPLE_RW_MEMBER(convex)
-      .DEF_SIMPLE_RW_MEMBER(schedule_parametric)
-      .DEF_SIMPLE_RW_MEMBER(schedule_outer_zero_distance)
-      .DEF_SIMPLE_RW_MEMBER(schedule_split_parallel)
-      ;
-  }
-  */
-
-#if !defined(ISLPY_ISL_VERSION) || (ISLPY_ISL_VERSION >= 15)
-  py::enum_<isl_error>("error")
-    .ENUM_VALUE(isl_error_, none)
-    .ENUM_VALUE(isl_error_, abort)
-    .ENUM_VALUE(isl_error_, unknown)
-    .ENUM_VALUE(isl_error_, internal)
-    .ENUM_VALUE(isl_error_, invalid)
-    .ENUM_VALUE(isl_error_, unsupported)
-    ;
-
-  py::enum_<isl_stat>("stat")
-    .ENUM_VALUE(isl_stat_, error)
-    .ENUM_VALUE(isl_stat_, ok)
-    ;
-#endif
-
-  py::enum_<isl_dim_type>("dim_type")
-    .ENUM_VALUE(isl_dim_, cst)
-    .ENUM_VALUE(isl_dim_, param)
-    .value("in_", isl_dim_in)
-    .ENUM_VALUE(isl_dim_, out)
-    .ENUM_VALUE(isl_dim_, set)
-    .ENUM_VALUE(isl_dim_, div)
-    .ENUM_VALUE(isl_dim_, all)
-    ;
-
-  py::enum_<isl_fold>("fold")
-    .ENUM_VALUE(isl_fold_, min)
-    .ENUM_VALUE(isl_fold_, max)
-    .ENUM_VALUE(isl_fold_, list)
-    ;
-
-  py::enum_<isl_ast_op_type>("ast_op_type")
-    .ENUM_VALUE(isl_ast_op_, error)
-    .ENUM_VALUE(isl_ast_op_, and)
-    .ENUM_VALUE(isl_ast_op_, and_then)
-    .ENUM_VALUE(isl_ast_op_, or)
-    .ENUM_VALUE(isl_ast_op_, or_else)
-    .ENUM_VALUE(isl_ast_op_, max)
-    .ENUM_VALUE(isl_ast_op_, min)
-    .ENUM_VALUE(isl_ast_op_, minus)
-    .ENUM_VALUE(isl_ast_op_, add)
-    .ENUM_VALUE(isl_ast_op_, sub)
-    .ENUM_VALUE(isl_ast_op_, mul)
-    .ENUM_VALUE(isl_ast_op_, div)
-    .ENUM_VALUE(isl_ast_op_, fdiv_q)
-    .ENUM_VALUE(isl_ast_op_, pdiv_q)
-    .ENUM_VALUE(isl_ast_op_, pdiv_r)
-    .ENUM_VALUE(isl_ast_op_, cond)
-    .ENUM_VALUE(isl_ast_op_, select)
-    .ENUM_VALUE(isl_ast_op_, eq)
-    .ENUM_VALUE(isl_ast_op_, le)
-    .ENUM_VALUE(isl_ast_op_, lt)
-    .ENUM_VALUE(isl_ast_op_, ge)
-    .ENUM_VALUE(isl_ast_op_, gt)
-    .ENUM_VALUE(isl_ast_op_, call)
-    .ENUM_VALUE(isl_ast_op_, access)
-    .ENUM_VALUE(isl_ast_op_, member)
-    ;
-
-  py::enum_<isl_ast_expr_type>("ast_expr_type")
-    .ENUM_VALUE(isl_ast_expr_, error)
-    .ENUM_VALUE(isl_ast_expr_, op)
-    .ENUM_VALUE(isl_ast_expr_, id)
-    .ENUM_VALUE(isl_ast_expr_, int)
-    ;
-
-  py::enum_<isl_ast_node_type>("ast_node_type")
-    .ENUM_VALUE(isl_ast_node_, error)
-    .ENUM_VALUE(isl_ast_node_, for)
-    .ENUM_VALUE(isl_ast_node_, if)
-    .ENUM_VALUE(isl_ast_node_, block)
-    .ENUM_VALUE(isl_ast_node_, user)
-    ;
-
-#define FORMAT_ATTR(name) cls_format.attr(#name) = ISL_FORMAT_##name
-  py::class_<isl::format> cls_format("format", py::no_init);
-  FORMAT_ATTR(ISL);
-  FORMAT_ATTR(POLYLIB);
-  FORMAT_ATTR(POLYLIB_CONSTRAINTS);
-  FORMAT_ATTR(OMEGA);
-  FORMAT_ATTR(C);
-  FORMAT_ATTR(LATEX);
-  FORMAT_ATTR(EXT_POLYLIB);
-
-  islpy_expose_part1();
-  islpy_expose_part2();
-  islpy_expose_part3();
-
-  py::implicitly_convertible<isl::basic_set, isl::set>();
-  py::implicitly_convertible<isl::basic_map, isl::map>();
-  py::implicitly_convertible<isl::set, isl::union_set>();
-  py::implicitly_convertible<isl::map, isl::union_map>();
-  py::implicitly_convertible<isl::space, isl::local_space>();
-  py::implicitly_convertible<isl::aff, isl::pw_aff>();
-}
diff --git a/src/wrapper/wrap_isl.hpp b/src/wrapper/wrap_isl.hpp
deleted file mode 100644
index e852589..0000000
--- a/src/wrapper/wrap_isl.hpp
+++ /dev/null
@@ -1,298 +0,0 @@
-#include "wrap_helpers.hpp"
-#include <isl/ctx.h>
-#include <isl/space.h>
-#include <isl/set.h>
-#include <isl/map.h>
-#include <isl/union_set.h>
-#include <isl/union_map.h>
-#include <isl/point.h>
-#include <isl/printer.h>
-#include <isl/local_space.h>
-#include <isl/vec.h>
-#include <isl/mat.h>
-#include <isl/polynomial.h>
-#include <isl/aff.h>
-#include <isl/aff.h>
-#include <isl/vertices.h>
-#include <isl/band.h>
-#include <isl/schedule.h>
-#if !defined(ISLPY_ISL_VERSION) || (ISLPY_ISL_VERSION >= 15)
-#include <isl/schedule_node.h>
-#endif
-#include <isl/flow.h>
-#include <isl/ast.h>
-#include <isl/ast_build.h>
-#include <isl/options.h>
-
-#ifdef ISLPY_INCLUDE_BARVINOK
-#include <barvinok/isl.h>
-#endif
-
-#include <stdexcept>
-#include <boost/python.hpp>
-#include <boost/unordered_map.hpp>
-
-
-// TODO: flow.h
-// TODO: better error reporting
-
-namespace py = boost::python;
-
-namespace isl
-{
-  class error : public std::runtime_error
-  {
-    public:
-      explicit error (const std::string &what)
-        : std::runtime_error(what)
-      { }
-  };
-
-  struct ctx;
-
-  typedef boost::unordered_map<isl_ctx *, unsigned> ctx_use_map_t;
-  extern ctx_use_map_t ctx_use_map;
-
-  inline void deref_ctx(isl_ctx *ctx)
-  {
-    ctx_use_map[ctx] -= 1;
-    if (ctx_use_map[ctx] == 0)
-      isl_ctx_free(ctx);
-  }
-
-#define WRAP_CLASS(name) \
-  struct name { WRAP_CLASS_CONTENT(name) }
-
-#define MAKE_CAST_CTOR(name, from_type, cast_func) \
-      name(from_type &data) \
-      : m_valid(false) \
-      { \
-        m_ctx = isl_##from_type##_get_ctx(data.m_data); \
-        \
-        isl_##from_type *copy = isl_##from_type##_copy(data.m_data); \
-        if (!copy) \
-          throw std::runtime_error("isl_" #from_type "_copy failed"); \
-        m_data = cast_func(copy); \
-        if (!m_data) \
-          throw std::runtime_error(#cast_func " failed"); \
-        \
-        m_valid = true; \
-        ctx_use_map[m_ctx] += 1; \
-      }
-
-#define WRAP_CLASS_CONTENT(name) \
-    private: \
-      bool              m_valid; \
-      isl_ctx           *m_ctx; \
-    public: \
-      isl_##name        *m_data; \
-      \
-      name(isl_##name *data) \
-      : m_valid(true), m_data(data) \
-      { \
-        m_ctx = isl_##name##_get_ctx(data); \
-        ctx_use_map[m_ctx] += 1; \
-      } \
-      \
-      void invalidate() \
-      { \
-        deref_ctx(m_ctx); \
-        m_valid = false; \
-      } \
-      \
-      bool is_valid() const \
-      { \
-        return m_valid; \
-      } \
-      \
-      ~name() \
-      { \
-        if (m_valid) \
-        { \
-          isl_##name##_free(m_data); \
-          deref_ctx(m_ctx); \
-        } \
-      }
-
-  struct ctx \
-  {
-    public:
-      isl_ctx           *m_data;
-
-      ctx(isl_ctx *data)
-      : m_data(data)
-      {
-        ctx_use_map_t::iterator it(ctx_use_map.find(m_data));
-        if (it == ctx_use_map.end())
-          ctx_use_map[data] = 1;
-        else
-          ctx_use_map[m_data] += 1;
-      }
-
-      bool is_valid() const
-      {
-        return true;
-      }
-      ~ctx()
-      {
-        deref_ctx(m_data);
-      }
-  };
-
-  WRAP_CLASS(printer);
-  WRAP_CLASS(val);
-  WRAP_CLASS(multi_val);
-  WRAP_CLASS(vec);
-  WRAP_CLASS(mat);
-  WRAP_CLASS(id);
-
-  WRAP_CLASS(aff);
-
-  struct pw_aff
-  {
-    WRAP_CLASS_CONTENT(pw_aff);
-    MAKE_CAST_CTOR(pw_aff, aff, isl_pw_aff_from_aff);
-  };
-
-#if !defined(ISLPY_ISL_VERSION) || (ISLPY_ISL_VERSION >= 15)
-  WRAP_CLASS(union_pw_aff);
-#endif
-
-  WRAP_CLASS(multi_aff);
-  WRAP_CLASS(multi_pw_aff);
-  WRAP_CLASS(pw_multi_aff);
-  WRAP_CLASS(union_pw_multi_aff);
-#if !defined(ISLPY_ISL_VERSION) || (ISLPY_ISL_VERSION >= 15)
-  WRAP_CLASS(multi_union_pw_aff);
-#endif
-
-  WRAP_CLASS(constraint);
-  WRAP_CLASS(space);
-
-  struct local_space
-  {
-    WRAP_CLASS_CONTENT(local_space);
-    MAKE_CAST_CTOR(local_space, space, isl_local_space_from_space);
-  };
-
-  WRAP_CLASS(basic_set);
-  WRAP_CLASS(basic_map);
-
-  struct set
-  {
-    WRAP_CLASS_CONTENT(set);
-    MAKE_CAST_CTOR(set, basic_set, isl_set_from_basic_set);
-  };
-
-  struct map
-  {
-    WRAP_CLASS_CONTENT(map);
-    MAKE_CAST_CTOR(map, basic_map, isl_map_from_basic_map);
-  };
-
-  struct union_set
-  {
-    WRAP_CLASS_CONTENT(union_set);
-    MAKE_CAST_CTOR(union_set, set, isl_union_set_from_set);
-  };
-  struct union_map
-  {
-    WRAP_CLASS_CONTENT(union_map);
-    MAKE_CAST_CTOR(union_map, map, isl_union_map_from_map);
-  };
-
-  WRAP_CLASS(point);
-  WRAP_CLASS(vertex);
-  WRAP_CLASS(cell);
-  WRAP_CLASS(vertices);
-
-  WRAP_CLASS(qpolynomial);
-  WRAP_CLASS(pw_qpolynomial);
-  WRAP_CLASS(qpolynomial_fold);
-  WRAP_CLASS(pw_qpolynomial_fold);
-  WRAP_CLASS(union_pw_qpolynomial);
-  WRAP_CLASS(union_pw_qpolynomial_fold);
-  WRAP_CLASS(term);
-
-  // matches order in isl_declaration_macros.h
-
-  WRAP_CLASS(id_list);
-
-  WRAP_CLASS(val_list);
-  WRAP_CLASS(aff_list);
-  WRAP_CLASS(pw_aff_list);
-  WRAP_CLASS(constraint_list);
-
-  WRAP_CLASS(basic_set_list);
-#if !defined(ISLPY_ISL_VERSION) || (ISLPY_ISL_VERSION >= 15)
-  WRAP_CLASS(basic_map_list);
-#endif
-  WRAP_CLASS(set_list);
-#if !defined(ISLPY_ISL_VERSION) || (ISLPY_ISL_VERSION >= 15)
-  WRAP_CLASS(map_list);
-  WRAP_CLASS(union_set_list);
-#endif
-
-  WRAP_CLASS(ast_expr_list);
-  WRAP_CLASS(ast_node_list);
-  WRAP_CLASS(band_list);
-
-  // end match
-
-  WRAP_CLASS(id_to_ast_expr);
-
-  WRAP_CLASS(band);
-  WRAP_CLASS(schedule);
-  WRAP_CLASS(schedule_constraints);
-#if !defined(ISLPY_ISL_VERSION) || (ISLPY_ISL_VERSION >= 15)
-  WRAP_CLASS(schedule_node);
-#endif
-
-  WRAP_CLASS(access_info);
-  WRAP_CLASS(flow);
-  WRAP_CLASS(restriction);
-#if !defined(ISLPY_ISL_VERSION) || (ISLPY_ISL_VERSION >= 15)
-  WRAP_CLASS(union_access_info);
-  WRAP_CLASS(union_flow);
-#endif
-
-  WRAP_CLASS(ast_expr);
-  WRAP_CLASS(ast_node);
-  WRAP_CLASS(ast_build);
-  WRAP_CLASS(ast_print_options);
-
-  inline ctx *alloc_ctx()
-  {
-    isl_ctx *result = isl_ctx_alloc();
-    if (result)
-    {
-      try
-      { return new ctx(result); }
-      catch (...)
-      {
-        isl_ctx_free(result);
-        throw;
-      }
-    }
-    else
-      PYTHON_ERROR(RuntimeError, "failed to create context");
-  }
-
-  class format { };
-
-  inline void my_decref(void *user)
-  {
-    Py_DECREF((PyObject *) user);
-  }
-}
-
-
-
-
-
-#define MAKE_WRAP(name, py_name) \
-  py::class_<isl::name, boost::noncopyable> wrap_##name(#py_name, py::no_init); \
-  wrap_##name.def("is_valid", &isl::name::is_valid, "Return whether current object is still valid."); \
-  wrap_##name.attr("_base_name") = #name; \
-  wrap_##name.attr("_isl_name") = "isl_"#name;
-
diff --git a/src/wrapper/wrap_isl_part1.cpp b/src/wrapper/wrap_isl_part1.cpp
deleted file mode 100644
index 0181aaf..0000000
--- a/src/wrapper/wrap_isl_part1.cpp
+++ /dev/null
@@ -1,122 +0,0 @@
-#include "wrap_isl.hpp"
-
-namespace isl
-{
-#include "gen-wrap-part1.inc"
-
-  class constants { };
-}
-
-namespace islpy
-{
-  bool id_eq(isl::id const *self, isl::id const *other)
-  {
-    return self == other;
-  }
-
-  bool id_ne(isl::id const *self, isl::id const *other)
-  {
-    return self != other;
-  }
-}
-
-void islpy_expose_part1()
-{
-  {
-    typedef isl::ctx cls;
-    py::class_<cls, boost::shared_ptr<cls>, boost::noncopyable>
-      wrap_ctx("Context", py::no_init);
-    wrap_ctx.def("__init__", py::make_constructor(isl::alloc_ctx));
-    wrap_ctx.attr("_base_name") = "ctx";
-    wrap_ctx.attr("_isl_name") = "isl_ctx";
-  }
-
-#define CONST(NAME) cls.attr(#NAME) = ISL_##NAME
-  {
-    py::class_<isl::constants> cls("constants", py::no_init);
-    CONST(BOUND_BERNSTEIN);
-    CONST(BOUND_RANGE);
-    CONST(ON_ERROR_WARN);
-    CONST(ON_ERROR_CONTINUE);
-    CONST(ON_ERROR_ABORT);
-    CONST(SCHEDULE_ALGORITHM_ISL);
-    CONST(SCHEDULE_ALGORITHM_FEAUTRIER);
-  }
-
-  // {{{ lists
-
-  MAKE_WRAP(id_list, IdList);
-
-  MAKE_WRAP(val_list, ValList);
-  MAKE_WRAP(aff_list, AffList);
-  MAKE_WRAP(pw_aff_list, PwAffList);
-
-  MAKE_WRAP(basic_set_list, BasicSetList);
-#if !defined(ISLPY_ISL_VERSION) || (ISLPY_ISL_VERSION >= 15)
-  MAKE_WRAP(basic_map_list, BasicMapList);
-#endif
-  MAKE_WRAP(set_list, SetList);
-#if !defined(ISLPY_ISL_VERSION) || (ISLPY_ISL_VERSION >= 15)
-  MAKE_WRAP(map_list, MapList);
-  MAKE_WRAP(union_set_list, UnionSetList);
-#endif
-
-  MAKE_WRAP(ast_expr_list, AstExprList);
-  MAKE_WRAP(ast_node_list, AstNodeList);
-  MAKE_WRAP(band_list, BandList);
-
-  // }}}
-
-  // {{{ maps
-
-  MAKE_WRAP(id_to_ast_expr, IdToAstExpr);
-
-  // }}}
-
-  MAKE_WRAP(printer, Printer);
-  MAKE_WRAP(val, Val);
-
-  MAKE_WRAP(multi_val, MultiVal);
-  MAKE_WRAP(vec, Vec);
-  MAKE_WRAP(mat, Mat);
-
-  MAKE_WRAP(aff, Aff);
-  wrap_aff.enable_pickling();
-  MAKE_WRAP(pw_aff, PwAff);
-  wrap_pw_aff.enable_pickling();
-#if !defined(ISLPY_ISL_VERSION) || (ISLPY_ISL_VERSION >= 15)
-  MAKE_WRAP(union_pw_aff, UnionPwAff);
-  wrap_union_pw_aff.enable_pickling();
-#endif
-  MAKE_WRAP(multi_aff, MultiAff);
-  wrap_multi_aff.enable_pickling();
-  MAKE_WRAP(multi_pw_aff, MultiPwAff);
-  wrap_multi_pw_aff.enable_pickling();
-  MAKE_WRAP(pw_multi_aff, PwMultiAff);
-  wrap_pw_multi_aff.enable_pickling();
-  MAKE_WRAP(union_pw_multi_aff, UnionPwMultiAff);
-  wrap_union_pw_multi_aff.enable_pickling();
-#if !defined(ISLPY_ISL_VERSION) || (ISLPY_ISL_VERSION >= 15)
-  MAKE_WRAP(multi_union_pw_aff, MultiUnionPwAff);
-  wrap_multi_union_pw_aff.enable_pickling();
-#endif
-
-  MAKE_WRAP(id, Id);
-  wrap_id.def("__eq__", islpy::id_eq, py::args("self", "other"),
-      "__eq__(self, other)\n\n"
-      ":param self: :class:`Id`\n"
-      ":param other: :class:`Id`\n"
-      ":return: bool ");
-  wrap_id.def("__ne__", islpy::id_ne, py::args("self", "other"),
-      "__ne__(self, other)\n\n"
-      ":param self: :class:`Id`\n"
-      ":param other: :class:`Id`\n"
-      ":return: bool ");
-
-  MAKE_WRAP(constraint, Constraint);
-  wrap_constraint.enable_pickling();
-  MAKE_WRAP(space, Space);
-  MAKE_WRAP(local_space, LocalSpace);
-
-#include "gen-expose-part1.inc"
-}
diff --git a/src/wrapper/wrap_isl_part2.cpp b/src/wrapper/wrap_isl_part2.cpp
deleted file mode 100644
index 4c8e795..0000000
--- a/src/wrapper/wrap_isl_part2.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-#include "wrap_isl.hpp"
-
-namespace isl
-{
-#include "gen-wrap-part2.inc"
-}
-
-void islpy_expose_part2()
-{
-  MAKE_WRAP(basic_set, BasicSet);
-  wrap_basic_set.enable_pickling();
-  MAKE_WRAP(basic_map, BasicMap);
-  wrap_basic_map.enable_pickling();
-  MAKE_WRAP(set, Set);
-  wrap_set.enable_pickling();
-  MAKE_WRAP(map, Map);
-  wrap_map.enable_pickling();
-  MAKE_WRAP(union_set, UnionSet);
-  wrap_union_set.enable_pickling();
-  MAKE_WRAP(union_map, UnionMap);
-  wrap_union_map.enable_pickling();
-
-  MAKE_WRAP(point, Point);
-  wrap_point.enable_pickling();
-  MAKE_WRAP(vertex, Vertex);
-  wrap_vertex.enable_pickling();
-  MAKE_WRAP(cell, Cell);
-  wrap_cell.enable_pickling();
-  MAKE_WRAP(vertices, Vertices);
-
-#include "gen-expose-part2.inc"
-}
diff --git a/src/wrapper/wrap_isl_part3.cpp b/src/wrapper/wrap_isl_part3.cpp
deleted file mode 100644
index db82d44..0000000
--- a/src/wrapper/wrap_isl_part3.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-#include "wrap_isl.hpp"
-
-namespace isl
-{
-#include "gen-wrap-part3.inc"
-}
-
-void islpy_expose_part3()
-{
-  MAKE_WRAP(qpolynomial_fold, QPolynomialFold);
-  MAKE_WRAP(pw_qpolynomial_fold, PwQPolynomialFold);
-  MAKE_WRAP(union_pw_qpolynomial_fold, UnionPwQPolynomialFold);
-  MAKE_WRAP(union_pw_qpolynomial, UnionPwQPolynomial);
-
-  MAKE_WRAP(qpolynomial, QPolynomial);
-  MAKE_WRAP(pw_qpolynomial, PwQPolynomial);
-
-  MAKE_WRAP(term, Term);
-
-  MAKE_WRAP(band, Band);
-  MAKE_WRAP(schedule, Schedule);
-  MAKE_WRAP(schedule_constraints, ScheduleConstraints);
-#if !defined(ISLPY_ISL_VERSION) || (ISLPY_ISL_VERSION >= 15)
-  MAKE_WRAP(schedule_node, ScheduleNode);
-#endif
-
-  MAKE_WRAP(access_info, AccessInfo);
-  MAKE_WRAP(flow, Flow);
-  MAKE_WRAP(restriction, Restriction);
-#if !defined(ISLPY_ISL_VERSION) || (ISLPY_ISL_VERSION >= 15)
-  MAKE_WRAP(union_access_info, UnionAccessInfo);
-  MAKE_WRAP(union_flow, UnionFlow);
-#endif
-
-  MAKE_WRAP(ast_expr, AstExpr);
-  MAKE_WRAP(ast_node, AstNode);
-  MAKE_WRAP(ast_build, AstBuild);
-  MAKE_WRAP(ast_print_options, AstPrintOptions);
-
-#include "gen-expose-part3.inc"
-}
-
-- 
GitLab