cpp: support methods and constructors
commit7d3c260fb555700559490aad0986ee45a1d4e1d3
authorTobias Grosser <tobias@grosser.es>
Thu, 13 Apr 2017 19:42:03 +0000 (13 21:42 +0200)
committerSven Verdoolaege <skimo@kotnet.org>
Thu, 27 Apr 2017 20:35:57 +0000 (27 22:35 +0200)
tree580ea8d6be2ae84c551fe16f33935e4d553ca148
parent81c38ef4ce0517aa355e2c1ca74097be39ecaec1
cpp: support methods and constructors

This commit generates code for static and non-static methods and
constructors. The following examples show some of the exported
methods:

  isl::set set::subtract(isl::set set2) const;
  std::string ast_expr::to_C_str() const;
  int val::cmp_si(long i) const;

  static isl::val val::infty(isl::ctx ctx);

All functions are declared 'inline' to avoid conflicts when the same
function is embedded into multiple translation units that are later
linked together.

Methods that take (as first argument) and return the same isl object
type could have been implemented as inplace methods. This commit
does not generate inplace methods to remain consistent with the look and
feel of C-isl, the python bindings, as well as the C++ bindings for
functions which return a different type. The same choice has also been
taken in Andreas Simbuerger's C++ bindings. In the future, inplace
methods may be introduced additionally.

It would also have been possible to generate (at least some) functions as
free functions, as some functions such as union or intersect do not have
a stronger connection to their first operand than to their second
operand and a free function better reflects the idea of an operation on
two independent (and equally important) operands. As the goal of this
commit is to establish a core interface that is close in spirit to the
interface exported by the python bindings, free functions are not
introduced in this commit. In the future free methods may be introduced
(e.g., via operator overloading).

As we cannot use C++ keywords as method names, this commit also replaces
each C++ keyword with an alternative name. Currently the only such keyword
is 'union', which is mapped to 'unite'. Another option to handle
this issue is to append an underscore and generate Set::union_().
Appending an underscore has the benefit that we can keep the name
matches closer to existing isl documentation and also better matches the
terminology of operations on sets, but overall looks less consistent.
Even though the use of 'unite' is not very common in this context, it
is used because the result of a uniting process is a union (in other
meanings of the word union) and because of the analogy with 'intersect'.

For isl_ctx *, isl_bool, and isl_stat three interface classes have
been introduced.

----------------------------------------------------------------------
isl_ctx -> isl::ctx
----------------------------------------------------------------------
For static functions that take an isl_ctx, the following interface
is generated:

static inline isl::val infty(isl::ctx ctx); static inline isl::val
zero(isl::ctx ctx);

Instead of using an isl_ctx * parameter, a C++ isl::ctx class is used:

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;
  }
};

isl::ctx is a non-owning class, which allows for implicit conversions from
isl_ctx *, such that isl++ objects can also be directly created from a C
style isl_ctx *. The following functions are both valid uses of isl::val:

isl::val foo(isl_ctx *ctx) {
return isl::val::infty(ctx);
}

isl::val foo(isl::ctx ctx) {
return isl::val::zero(ctx);
}

We currently do not manage the isl_ctx or provide some way to construct
objects without an isl context. This is very much in spirit of the isl
C API, where the user himself explicitly manages the isl context.

In the future, a managed isl context class could be added
to support more script style uses of these C++ bindings.
----------------------------------------------------------------------

----------------------------------------------------------------------
isl_bool -> isl::boolean
---------------------------------------------------------------------- For
functions that return an isl_bool, the following interface is generated:

inline isl::boolean set::is_empty() const; inline isl::boolean
set::is_universe() const;

In isl, boolean functions such as is_empty do not only return
isl_bool_true and isl_bool_false, but may also return an error state
isl_bool_error. As the C++ bindings may be used in an environment without
exceptions support, it is not possible to return values as binary boolean
values. Instead, this commit introduces a new class isl::boolean which
implements a tri-boolean value.

class boolean { private:
  friend boolean manage(isl_bool val);

  boolean()
      : val(isl_bool_error) {}

  /* implicit */ boolean(bool val)
      : val(val ? isl_bool_true : isl_bool_false) {}

If used in conditional statements such as 'if' this value converts to
bool and prints an error in case its state is 'error'.

  explicit boolean::operator bool()

To allow the user to handle errors gracefully, isl::boolean also provides
an interface to explicitly query its state:

  bool is_error() bool is_false() bool is_true()

The isl::boolean interface has been designed by Michael Kruse.
----------------------------------------------------------------------

----------------------------------------------------------------------
isl_stat -> isl::stat
----------------------------------------------------------------------

enum class stat {
  ok = isl_stat_ok, error = isl_stat_error
};

This commit introduces named enums to provide
a type-safe version of the isl_stat interface.
----------------------------------------------------------------------

We also add a C++ constructor for each isl function that is marked as
__isl_constructor. A typical set of constructors emitted today is:

  inline explicit ast_build(isl::ctx ctx); inline explicit val(isl::ctx
  ctx, long i); inline explicit val(isl::ctx ctx, const std::string &str);

This commit generates all constructors as explicit, meaning the user
needs to explicitly invoke the constructor. Implicit conversions are
only allowed for single arguments constructors, where the type of the new
object is a generalization of the type of the object that is converted,
according to the __isl_subclass annotations.

  inline /* implicit */ set(isl::basic_set obj); inline /* implicit */
  set(isl::point obj);

Single argument conversion constructors are often paired with
corresponding assignment operators. This commit does not introduce new
assignment operators. For implicit conversion operators, there is no need
to print assignment operators as the default assignment operators in
combination with the added implicit conversion constructors allow such
assignments. This commit also does not emit assignment operators for
objects that are not a generalization of the object that is assigned, so
as not to encourage such implicit type conversions. Instead, the user is
expected to explicitly call the conversion constructor before assignment.

A function in the C bindings with a callback such as

  isl_stat isl_union_map_foreach_map(__isl_keep isl_union_map *umap,
    isl_stat (*fn)(__isl_take isl_map *map, void *user), void *user);

is translated to the following C++ binding function:

  stat union_map::foreach_map(const std::function<stat(map)> &fn) const {
    auto fn_lambda = [](isl_map *arg_0, void *arg_1) -> isl_stat {
       auto *func = (std::function<stat(map)> *)arg_1;
      stat ret = (*func) (isl::manage(arg_0)); return isl_stat(ret);
    }; auto res = isl_union_map_foreach_map(get(), fn_lambda, (void)
    &fn); return stat(res);
  }

We provide an interface based on std::function, which allows the user
to use any std::function as callback, e.g., lambdas that capture local
state. The callback function can then be used as follows:

  union_map umap;

  auto callback = [](isl::map m) -> isl::stat {
    isl_map_dump(m.get()); return isl::stat::ok;
  };

  umap.foreach_map(callback);

Performance optimizations that exploit move semantics by using r-value
references have not been included in order to focus on core functionality,
but can easily be introduced later.

These bindings require C++11 as the callback interface uses C++11
features such as std::function, which were not yet available in earlier
C++ versions. We also choose to use C++ named enums to increase language
safety, inline name spaces, nullptr, and expect to use r-value references
soon to reduce the need for some temporary copies. All these features
provide direct benefits to the bindings and creating bindings without them
does not seem to be a good idea. As we are now 5 years after C++11 and
this is an entirely new binding, it seems OK to require C++11. Whoever
cannot use C++11 can always keep using the C interface.

These bindings are not installed as part of "make install" because such
an installation is not needed for the Polly use case.

Signed-off-by: Michael Kruse <isl@meinersbur.de>
Signed-off-by: Tobias Grosser <tobias@grosser.es>
Signed-off-by: Sven Verdoolaege <skimo@kotnet.org>
Makefile.am
interface/Makefile.am
interface/cpp.cc
interface/cpp.h
interface/extract_interface.cc
interface/generator.cc
interface/generator.h
interface/isl.h.top