/* * Copyright 2016, 2017 Tobias Grosser. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * THIS SOFTWARE IS PROVIDED BY TOBIAS GROSSER ''AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TOBIAS GROSSER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation * are those of the authors and should not be interpreted as * representing official policies, either expressed or implied, of * Tobias Grosser. */ #include #include #include #include "cpp.h" #include "isl_config.h" /* Determine the isl types from which the given class can be implicitly * constructed using a unary constructor. * * Look through all constructors for implicit conversion constructors that take * an isl type and add those types, along with the corresponding * constructor argument. */ void cpp_generator::set_class_construction_types(isl_class &clazz) { for (const auto &cons : clazz.constructors) { ParmVarDecl *param; QualType type; std::string arg_type; if (!is_implicit_conversion(Method(clazz, cons))) continue; param = cons->getParamDecl(0); type = param->getOriginalType(); arg_type = extract_type(type); clazz.construction_types.emplace(arg_type, param); } } /* Determine the isl types from which any (proper) class can be constructed * using a unary constructor. */ void cpp_generator::set_construction_types() { for (auto &kvp : classes) { auto &clazz = kvp.second; set_class_construction_types(clazz); } } /* Construct a generator for C++ bindings. * * The classes and methods are extracted by the constructor * of the generator superclass. * * Additionally extract information about types * that can be converted to a class and copy all methods * from superclasses that can be converted to a given class * to that class. */ cpp_generator::cpp_generator(SourceManager &SM, set &exported_types, set exported_functions, set functions) : generator(SM, exported_types, exported_functions, functions) { set_construction_types(); copy_super_methods(); } /* Copy the method called "name" described by "fd" from "super" to "clazz" * with the distance to the original ancestor given by "depth". * * In particular, keep track of "fd" as well as the superclass * from which it was copied and the distance to the original ancestor. */ static void copy_method(isl_class &clazz, const isl_class &super, const std::string &name, FunctionDecl *fd, int depth) { clazz.methods[name].insert(fd); clazz.copied_from.emplace(fd, super); clazz.copy_depth.emplace(fd, depth); } /* Do "fd1" and "fd2" have the same signature (ignoring the first argument * which represents the object class on which the corresponding method * gets called). */ static bool same_signature(FunctionDecl *fd1, FunctionDecl *fd2) { int n1 = fd1->getNumParams(); int n2 = fd2->getNumParams(); if (n1 != n2) return false; for (int i = 1; i < n1; ++i) { ParmVarDecl *p1 = fd1->getParamDecl(i); ParmVarDecl *p2 = fd2->getParamDecl(i); if (p1->getOriginalType() != p2->getOriginalType()) return false; } return true; } /* Return the distance between "clazz" and the ancestor * from which "fd" got copied. * If no distance was recorded, then the method has not been copied * but appears in "clazz" itself and so the distance is zero. */ static int copy_depth(const isl_class &clazz, FunctionDecl *fd) { if (clazz.copy_depth.count(fd) == 0) return 0; return clazz.copy_depth.at(fd); } /* Is the method derived from "fd", with method name "name" and * with distance to the original ancestor "depth", * overridden by a method already in "clazz"? * * A method is considered to have been overridden if there * is a method with the same name in "clazz" that has the same signature and * that comes from an ancestor closer to "clazz", * where an ancestor is closer if the distance in the class hierarchy * is smaller or the distance is the same and the ancestor appears * closer in the declaration of the type (in which case it gets added first). * * If a method with the same signature has already been added, * but it does not override the method derived from "fd", * then this method is removed since it is overridden by "fd". */ static bool is_overridden(FunctionDecl *fd, isl_class &clazz, const std::string &name, int depth) { if (clazz.methods.count(name) == 0) return false; for (const auto &m : clazz.methods.at(name)) { if (!same_signature(fd, m)) continue; if (copy_depth(clazz, m) <= depth) return true; clazz.methods[name].erase(m); return false; } return false; } /* Add the methods "methods" with method name "name" from "super" to "clazz" * provided they have not been overridden by a method already in "clazz". * * Methods that are static in their original class are not copied. */ void cpp_generator::copy_methods(isl_class &clazz, const std::string &name, const isl_class &super, const function_set &methods) { for (auto fd : methods) { int depth; if (method2class(fd)->is_static(fd)) continue; depth = copy_depth(super, fd) + 1; if (is_overridden(fd, clazz, name, depth)) continue; copy_method(clazz, super, name, fd, depth); } } /* Add all methods from "super" to "clazz" that have not been overridden * by a method already in "clazz". * * Look through all groups of methods with the same name. */ void cpp_generator::copy_super_methods(isl_class &clazz, const isl_class &super) { for (const auto &kvp : super.methods) { const auto &name = kvp.first; const auto &methods = kvp.second; copy_methods(clazz, name, super, methods); } } /* Copy methods from the superclasses of "clazz" * if an object of this class can be implicitly converted to an object * from the superclass, keeping track * of the classes that have already been handled in "done". * * Make sure the superclasses have copied methods from their superclasses first * since those methods could be copied further down to this class. * * Consider the superclass that appears closest to the subclass first. */ void cpp_generator::copy_super_methods(isl_class &clazz, set &done) { auto supers = find_superclasses(clazz.type); for (const auto &super : supers) if (done.count(super) == 0) copy_super_methods(classes[super], done); done.insert(clazz.name); for (const auto &super_name : supers) { const auto &super = classes[super_name]; if (super.construction_types.count(clazz.name) == 0) continue; copy_super_methods(clazz, super); } } /* For each (proper) class, copy methods from its superclasses, * if an object from the class can be converted to an object * from the superclass. * * Type based subclasses are not considered for now since * they do not have any explicit superclasses. * * Iterate through all (proper) classes and copy methods * from their superclasses, * unless they have already been determined by a recursive call. */ void cpp_generator::copy_super_methods() { set done; for (auto &kvp : classes) { auto &clazz = kvp.second; if (clazz.is_type_subclass()) continue; if (done.count(clazz.name) != 0) continue; copy_super_methods(clazz, done); } } /* Print declarations or implementations of constructors. * * For each isl function that is marked as __isl_constructor, * add a corresponding C++ constructor. * * Example of declarations: * * inline /\* implicit *\/ union_set(basic_set bset); * inline /\* implicit *\/ union_set(set set); * inline explicit val(ctx ctx, long i); * inline explicit val(ctx ctx, const std::string &str); */ void cpp_generator::class_printer::print_constructors() { for (const auto &cons : clazz.constructors) print_method(Method(clazz, cons)); } /* Print declarations or definitions for methods in the class. */ void cpp_generator::class_printer::print_methods() { for (const auto &kvp : clazz.methods) print_method_group(kvp.second, kvp.first); } /* Print declarations or implementations for the methods derived from "fd", * which sets an enum. * * A method is generated for each value in the enum, setting * the enum to that value. */ void cpp_generator::class_printer::print_set_enums(FunctionDecl *fd) { for (const auto &set : clazz.set_enums.at(fd)) { EnumMethod method(clazz, fd, set.method_name, set.name); print_method(method); } } /* Print declarations or implementations for methods derived from functions * that set an enum. */ void cpp_generator::class_printer::print_set_enums() { for (const auto &kvp : clazz.set_enums) print_set_enums(kvp.first); } /* Update "convert" to reflect the next combination of automatic conversions * for the arguments of "fd", * returning false if there are no more combinations. * * In particular, find the last argument for which an automatic * conversion function is available mapping to the type of this argument and * that is not already marked for conversion. * Mark this argument, if any, for conversion and clear the markings * of all subsequent arguments. * Repeated calls to this method therefore run through * all possible combinations. * * Note that the first function argument is never considered * for automatic conversion since this is the argument * from which the isl_ctx used in the conversion is extracted. */ bool cpp_generator::class_printer::next_variant(FunctionDecl *fd, std::vector &convert) { size_t n = convert.size(); for (int i = n - 1; i >= 1; --i) { ParmVarDecl *param = fd->getParamDecl(i); const Type *type = param->getOriginalType().getTypePtr(); if (generator.conversions.count(type) == 0) continue; if (convert[i]) continue; convert[i] = true; for (size_t j = i + 1; j < n; ++j) convert[j] = false; return true; } return false; } /* Print a declaration or definition for a method called "name" * derived from "fd". * * If the method was copied from a superclass, then print a definition * that calls the corresponding method in the superclass. * Otherwise, for methods that are identified as "get" methods, also * print a declaration or definition for the method * using a name that includes the "get_" prefix. * * If the generated method is an object method, then check * whether any of its arguments can be automatically converted * from something else, and, if so, generate a method * for each combination of converted arguments. * Do so by constructing a ConversionMethod that changes the converted arguments * to those of the sources of the conversions. * * Note that a method may be both copied from a superclass and * have arguments that can be automatically converted. * In this case, the conversion methods for the arguments * call the corresponding method in this class, which * in turn will call the method in the superclass. */ void cpp_generator::class_printer::print_method_variants(FunctionDecl *fd, const std::string &name) { Method method(clazz, fd, name); std::vector convert(method.num_params()); if (method.clazz.copied_from.count(method.fd) == 0) { print_method(method); if (clazz.is_get_method(fd)) print_get_method(fd); } else { auto super = method.clazz.copied_from.at(method.fd); print_method(ConversionMethod(method, super.name)); } if (method.kind != Method::Kind::member_method) return; while (next_variant(fd, convert)) { print_method(ConversionMethod(method, [&] (int pos) { return get_param(fd, pos, convert); })); } } /* Given a function declaration representing a method, * does this method have a single argument (beyond the object * on which the method is called) that corresponds to * an isl object? */ static bool has_single_isl_argument(FunctionDecl *fd) { ParmVarDecl *param; if (fd->getNumParams() != 2) return false; param = fd->getParamDecl(1); return generator::is_isl_type(param->getOriginalType()); } /* Does the set "methods" contain exactly one function declaration * that corresponds to a method of "clazz" itself (i.e., that * was not copied from an ancestor)? */ static FunctionDecl *single_local(const isl_class &clazz, const function_set &methods) { int count = 0; FunctionDecl *local; for (const auto &fn : methods) { if (!clazz.first_arg_matches_class(fn)) continue; ++count; local = fn; } return count == 1 ? local : NULL; } /* Given a function declaration "fd" for a method called "name" * with a single argument representing an isl object, * generate declarations or definitions for methods with the same name, * but with as argument an isl object of a class that can be implicitly * converted to that of the original argument. * In particular, generate methods for converting this argument. */ void cpp_generator::class_printer::print_descendent_overloads( FunctionDecl *fd, const std::string &name) { Method method(clazz, fd, name); ParmVarDecl *param = fd->getParamDecl(1); QualType type = param->getOriginalType(); std::string arg = type->getPointeeType().getAsString(); for (const auto &kvp : generator.classes[arg].construction_types) { const auto sub = kvp.second; print_method(ConversionMethod(method, [&] (int pos) { return sub; })); } } /* Print declarations or definitions for methods called "name" * derived from "methods". * * If want_descendent_overloads signals that variants should be added that take * as arguments those types that can be converted to the original argument type * through a unary constructor and if only one of the methods in the group * was originally defined in "clazz", then effectively add those variants. * Only do this for methods with a single (isl object) argument. */ void cpp_generator::class_printer::print_method_group( const function_set &methods, const std::string &name) { FunctionDecl *local; for (const auto &fd : methods) print_method_variants(fd, name); if (!want_descendent_overloads(methods)) return; local = single_local(clazz, methods); if (!local) return; if (!has_single_isl_argument(local)) return; print_descendent_overloads(local, name); } /* Print the use of the argument at position "pos" to "os". * * Member methods pass the isl object corresponding to "this" * as first argument (at position 0). * Any other arguments are passed along from the method arguments. * * If the argument value is loaded from a this pointer, the original * value must be preserved and must consequently be copied. Values that are * loaded from method parameters do not need to be preserved, as such values * will already be copies of the actual parameters. It is consequently possible * to directly take the pointer from these values, which saves * an unnecessary copy. * * In case the parameter is a callback function, two parameters get printed, * a wrapper for the callback function and a pointer to the actual * callback function. The wrapper is expected to be available * in a previously declared variable _lambda, while * the actual callback function is expected to be stored * in a structure called _data. * The caller of this function must ensure that these variables exist. */ void Method::print_param_use(ostream &os, int pos) const { ParmVarDecl *param = fd->getParamDecl(pos); bool load_from_this_ptr = pos == 0 && kind == member_method; string name = param->getName().str(); QualType type = param->getOriginalType(); if (type->isIntegerType()) { os << name; return; } if (generator::is_string(type)) { os << name << ".c_str()"; return; } if (generator::is_callback(type)) { os << name << "_lambda, "; os << "&" << name << "_data"; return; } if (!load_from_this_ptr) os << name << "."; if (generator::keeps(param)) { os << "get()"; } else { if (load_from_this_ptr) os << "copy()"; else os << "release()"; } } /* Does the isl function from which this method is derived * modify an object of a subclass based on a type function? */ bool Method::is_subclass_mutator() const { return clazz.is_type_subclass() && generator::is_mutator(clazz, fd); } /* Return the C++ return type of the method "method". * * If the corresponding function modifies an object of a subclass, then return * the type of this subclass. * Otherwise, return the C++ counterpart of the actual return type. */ std::string cpp_type_printer::return_type(const Method &method) const { if (method.is_subclass_mutator()) return cpp_generator::type2cpp(method.clazz); else return param(-1, method.fd->getReturnType()); } /* Return the formal parameter at position "pos" of "fd". * However, if this parameter should be converted, as indicated * by "convert", then return the second formal parameter * of the conversion function instead. */ ParmVarDecl *cpp_generator::class_printer::get_param(FunctionDecl *fd, int pos, const std::vector &convert) { ParmVarDecl *param = fd->getParamDecl(pos); if (!convert[pos]) return param; return generator.conversions[param->getOriginalType().getTypePtr()]; } /* Print the header for "method", without newline or semicolon, * using "type_printer" to print argument and return types. * * Print the header of a declaration if this->declarations is set, * otherwise print the header of a method definition. * * This function prints headers for member methods, static methods, and * constructors, either for their declaration or definition. * * Member functions are declared as "const", as they do not change the current * object, but instead create a new object. They always retrieve the first * parameter of the original isl function from the this-pointer of the object, * such that only starting at the second parameter the parameters of the * original function become part of the method's interface. * * A function * * __isl_give isl_set *isl_set_intersect(__isl_take isl_set *s1, * __isl_take isl_set *s2); * * is translated into: * * inline set intersect(set set2) const * * For static functions and constructors all parameters of the original isl * function are exposed. * * Parameters of which no copy is required, are passed * as const reference, which allows the compiler to optimize the parameter * transfer. * * Constructors are marked as explicit using the C++ keyword 'explicit' or as * implicit using a comment in place of the explicit keyword. By annotating * implicit constructors with a comment, users of the interface are made * aware of the potential danger that implicit construction is possible * for these constructors, whereas without a comment not every user would * know that implicit construction is allowed in absence of an explicit keyword. * * Note that in case "method" is a ConversionMethod, the argument returned * by Method::get_param may be different from the original argument. * The name of the argument is, however, derived from the original * function argument. */ void cpp_generator::class_printer::print_method_header( const Method &method, const cpp_type_printer &type_printer) { string rettype_str = type_printer.return_type(method); if (declarations) { os << " "; if (method.kind == Method::Kind::static_method) os << "static "; os << "inline "; if (method.kind == Method::Kind::constructor) { if (generator.is_implicit_conversion(method)) os << "/* implicit */ "; else os << "explicit "; } } if (method.kind != Method::Kind::constructor) os << rettype_str << " "; if (!declarations) os << type_printer.class_type(cppstring) << "::"; if (method.kind != Method::Kind::constructor) os << method.name; else os << cppstring; method.print_cpp_arg_list(os, [&] (int i, int arg) { std::string name = method.fd->getParamDecl(i)->getName().str(); ParmVarDecl *param = method.get_param(i); QualType type = param->getOriginalType(); string cpptype = type_printer.param(arg, type); if (!method.param_needs_copy(i)) os << "const " << cpptype << " &" << name; else os << cpptype << " " << name; }); if (method.kind == Method::Kind::member_method) os << " const"; } /* Generate the list of argument types for a callback function of * type "type", appearing in argument position "arg". * If "cpp" is set, then generate the C++ type list, otherwise * the C type list. * * For a callback of type * * isl_stat (*)(__isl_take isl_map *map, void *user) * * the following C++ argument list is generated: * * map * * The arguments of the callback are considered to appear * after the position of the callback itself. */ std::string cpp_type_printer::generate_callback_args(int arg, QualType type, bool cpp) const { std::string type_str; const FunctionProtoType *callback; int num_params; callback = generator::extract_prototype(type); num_params = callback->getNumArgs(); if (cpp) num_params--; for (long i = 0; i < num_params; i++) { QualType type = callback->getArgType(i); if (cpp) type_str += param(arg + 1 + i, type); else type_str += type.getAsString(); if (!cpp) type_str += "arg_" + ::to_string(i); if (i != num_params - 1) type_str += ", "; } return type_str; } /* Generate the full cpp type of a callback function of type "type", * appearing in argument position "arg". * * For a callback of type * * isl_stat (*)(__isl_take isl_map *map, void *user) * * the following type is generated: * * std::function */ std::string cpp_type_printer::generate_callback_type(int arg, QualType type) const { std::string type_str; const FunctionProtoType *callback = generator::extract_prototype(type); QualType return_type = callback->getReturnType(); string rettype_str = param(arg, return_type); type_str = "std::function<"; type_str += rettype_str; type_str += "("; type_str += generate_callback_args(arg, type, true); type_str += ")>"; return type_str; } /* An array listing functions that must be renamed and the function name they * should be renamed to. We currently rename functions in case their name would * match a reserved C++ keyword, which is not allowed in C++. */ static const char *rename_map[][2] = { { "union", "unite" }, }; /* Rename method "name" in case the method name in the C++ bindings should not * match the name in the C bindings. We do this for example to avoid * C++ keywords. */ static std::string rename_method(std::string name) { for (size_t i = 0; i < sizeof(rename_map) / sizeof(rename_map[0]); i++) if (name.compare(rename_map[i][0]) == 0) return rename_map[i][1]; return name; } /* Translate isl class "clazz" to its corresponding C++ type. * Use the name of the type based subclass, if any. */ string cpp_generator::type2cpp(const isl_class &clazz) { return type2cpp(clazz.subclass_name); } /* Translate type string "type_str" to its C++ name counterpart. */ string cpp_generator::type2cpp(string type_str) { return type_str.substr(4); } /* Return the C++ counterpart to the isl_bool type. * * By default, this is simply "bool" since * the exceptional case is handled through exceptions. */ std::string cpp_type_printer::isl_bool() const { return "bool"; } /* Return the C++ counterpart to the isl_stat type. * * By default, this is simply "void" since * the exceptional case is handled through exceptions. */ string cpp_type_printer::isl_stat() const { return "void"; } /* Return the C++ counterpart to the isl_size type. * * By default, this is simply "unsigned" since * the exceptional case is handled through exceptions. */ string cpp_type_printer::isl_size() const { return "unsigned"; } /* Return the namespace of the generated C++ bindings. * * By default, this is "isl::". */ std::string cpp_type_printer::isl_namespace() const { return "isl::"; } /* Return the class type given the C++ name. * * By default, directly use the C++ name. */ std::string cpp_type_printer::class_type(const std::string &cpp_name) const { return cpp_name; } /* Return the qualified form of the given C++ isl type name appearing * in argument position "arg" (-1 for return type). * * By default, the argument position is ignored. */ std::string cpp_type_printer::qualified(int arg, const std::string &cpp_type) const { return isl_namespace() + cpp_type; } /* Return the C++ counterpart to the given isl type appearing * in argument position "arg" (-1 for return type). */ std::string cpp_type_printer::isl_type(int arg, QualType type) const { auto name = type->getPointeeType().getAsString(); return qualified(arg, cpp_generator::type2cpp(name)); } /* Translate parameter or return type "type" to its C++ name counterpart. * "arg" is the position of the argument, or -1 in case of the return type. * If any callback is involved, then the return type and arguments types * of the callback are considered to start at the position of the callback. */ std::string cpp_type_printer::param(int arg, QualType type) const { if (cpp_generator::is_isl_type(type)) return isl_type(arg, type); if (cpp_generator::is_isl_bool(type)) return isl_bool(); if (cpp_generator::is_isl_stat(type)) return isl_stat(); if (cpp_generator::is_isl_size(type)) return isl_size(); if (type->isIntegerType()) return type.getAsString(); if (cpp_generator::is_string(type)) return "std::string"; if (cpp_generator::is_callback(type)) return generate_callback_type(arg, type); generator::die("Cannot convert type to C++ type"); } /* Check if "subclass_type" is a subclass of "class_type". */ bool cpp_generator::is_subclass(QualType subclass_type, const isl_class &class_type) { std::string type_str = subclass_type->getPointeeType().getAsString(); std::vector superclasses; std::vector parents; std::vector::iterator ci; superclasses = generator::find_superclasses(classes[type_str].type); for (ci = superclasses.begin(); ci < superclasses.end(); ci++) parents.push_back(&classes[*ci]); while (!parents.empty()) { const isl_class *candidate = parents.back(); parents.pop_back(); if (&class_type == candidate) return true; superclasses = generator::find_superclasses(candidate->type); for (ci = superclasses.begin(); ci < superclasses.end(); ci++) parents.push_back(&classes[*ci]); } return false; } /* Check if "cons" is an implicit conversion constructor of class "clazz". * * An implicit conversion constructor is generated in case "cons" has a single * parameter, where the parameter type is a subclass of the class that is * currently being generated. */ bool cpp_generator::is_implicit_conversion(const Method &cons) { const auto &clazz = cons.clazz; ParmVarDecl *param = cons.fd->getParamDecl(0); QualType type = param->getOriginalType(); int num_params = cons.fd->getNumParams(); if (num_params != 1) return false; if (is_isl_type(type) && !is_isl_ctx(type) && is_subclass(type, clazz)) return true; return false; } /* Construct a list combiner for printing a list. */ Method::list_combiner Method::print_combiner(std::ostream &os) { return { [&] () { os << "("; }, [&] () { os << ", "; }, [&] () { os << ")"; } }; } /* Construct a list combiner for simply iterating over a list. */ Method::list_combiner Method::empty_combiner() { return { [&] () { }, [&] () { }, [&] () { } }; } /* Get kind of "method" in "clazz". * * Given the declaration of a static or member method, returns its kind. */ static Method::Kind get_kind(const isl_class &clazz, FunctionDecl *method) { if (generator::is_constructor(method)) return Method::Kind::constructor; else if (generator::is_static(clazz, method)) return Method::Kind::static_method; else return Method::Kind::member_method; } /* Return the callback arguments of "fd". */ static std::vector find_callback_args(FunctionDecl *fd) { std::vector callbacks; int num_params = fd->getNumParams(); for (int i = 0; i < num_params; ++i) { ParmVarDecl *param = fd->getParamDecl(i); if (generator::is_callback(param->getType())) callbacks.emplace_back(param); } return callbacks; } /* Construct a C++ method object from the class to which is belongs, * the isl function from which it is derived and the method name. * * Perform any renaming of the method that may be required and * determine the type of the method. */ Method::Method(const isl_class &clazz, FunctionDecl *fd, const std::string &name) : clazz(clazz), fd(fd), name(rename_method(name)), kind(get_kind(clazz, fd)), callbacks(find_callback_args(fd)) { } /* Construct a C++ method object from the class to which is belongs and * the isl function from which it is derived. * * Obtain the default method name and continue * with the generic constructor. */ Method::Method(const isl_class &clazz, FunctionDecl *fd) : Method(clazz, fd, clazz.method_name(fd)) { } /* Return the number of parameters of the corresponding C function. * * This number includes any possible user pointers that follow callback * arguments. These are skipped by Method::print_fd_arg_list * during the actual argument printing. */ int Method::c_num_params() const { return fd->getNumParams(); } /* Return the number of parameters of the method * (including the implicit "this"). * * By default, it is the same as the number of parameters * of the corresponding C function. */ int Method::num_params() const { return c_num_params(); } /* Call "on_arg_skip_next" on the arguments from "start" (inclusive) * to "end" (exclusive), calling the methods of "combiner" * before, between and after the arguments. * If "on_arg_skip_next" returns true then the next argument is skipped. */ void Method::on_arg_list(int start, int end, const Method::list_combiner &combiner, const std::function &on_arg_skip_next) { combiner.before(); for (int i = start; i < end; ++i) { if (i != start) combiner.between(); if (on_arg_skip_next(i)) ++i; } combiner.after(); } /* Print the arguments from "start" (inclusive) to "end" (exclusive) * as arguments to a method of C function call, using "print_arg_skip_next" * to print each individual argument. If this callback return true * then the next argument is skipped. */ void Method::print_arg_list(std::ostream &os, int start, int end, const std::function &print_arg_skip_next) { on_arg_list(start, end, print_combiner(os), [&] (int i) { return print_arg_skip_next(i); }); } /* Call "on_arg" on the arguments from "start" (inclusive) to "end" (exclusive), * calling the methods of "combiner" before, between and after the arguments. * The first argument to "on_arg" is the position of the argument * in this->fd. * The second argument is the (first) position in the list of arguments * with all callback arguments spliced in. * * Call on_arg_list to do the actual iteration over the arguments, skipping * the user argument that comes after every callback argument. * On the C++ side no user pointer is needed, as arguments can be forwarded * as part of the std::function argument which specifies the callback function. * The user pointer is also removed from the number of parameters * of the C function because the pair of callback and user pointer * is considered as a single argument that is printed as a whole * by Method::print_param_use. * * In case of a callback argument, the second argument to "print_arg" * is also adjusted to account for the spliced-in arguments of the callback. * The return value takes the place of the callback itself, * while the arguments (excluding the final user pointer) * take the following positions. */ void Method::on_fd_arg_list(int start, int end, const Method::list_combiner &combiner, const std::function &on_arg) const { int arg = start; on_arg_list(start, end, combiner, [this, &on_arg, &arg] (int i) { auto type = fd->getParamDecl(i)->getType(); on_arg(i, arg++); if (!generator::is_callback(type)) return false; arg += generator::prototype_n_args(type) - 1; return true; }); } /* Print the arguments from "start" (inclusive) to "end" (exclusive) * as arguments to a method of C function call, using "print_arg" * to print each individual argument. * The first argument to this callback is the position of the argument * in this->fd. * The second argument is the (first) position in the list of arguments * with all callback arguments spliced in. */ void Method::print_fd_arg_list(std::ostream &os, int start, int end, const std::function &print_arg) const { on_fd_arg_list(start, end, print_combiner(os), print_arg); } /* Call "on_arg" on the arguments to the method call, * calling the methods of "combiner" before, between and after the arguments. * The first argument to "on_arg" is the position of the argument * in this->fd. * The second argument is the (first) position in the list of arguments * with all callback arguments spliced in. */ void Method::on_cpp_arg_list(const Method::list_combiner &combiner, const std::function &on_arg) const { int first_param = kind == member_method ? 1 : 0; on_fd_arg_list(first_param, num_params(), combiner, on_arg); } /* Call "on_arg" on the arguments to the method call. * The first argument to "on_arg" is the position of the argument * in this->fd. * The second argument is the (first) position in the list of arguments * with all callback arguments spliced in. */ void Method::on_cpp_arg_list( const std::function &on_arg) const { on_cpp_arg_list(empty_combiner(), on_arg); } /* Print the arguments to the method call, using "print_arg" * to print each individual argument. * The first argument to this callback is the position of the argument * in this->fd. * The second argument is the (first) position in the list of arguments * with all callback arguments spliced in. */ void Method::print_cpp_arg_list(std::ostream &os, const std::function &print_arg) const { on_cpp_arg_list(print_combiner(os), print_arg); } /* Should the parameter at position "pos" be a copy (rather than * a const reference)? * * Strictly speaking, a copy is only needed on isl types that are * not marked __isl_keep, since those will be release()'d * by code printed by Method::print_param_use. * * However, there may be other arguments such as integer types * that are more naturally passed as a copy. * The default is therefore to require a copy, except for * arguments marked __isl_keep, string arguments or callback arguments. */ bool Method::param_needs_copy(int pos) const { ParmVarDecl *param = get_param(pos); QualType type = param->getOriginalType(); if (generator::keeps(param)) return false; if (generator::is_string(type) || generator::is_callback(type)) return false; return true; } /* Return the method argument at position "pos". */ clang::ParmVarDecl *Method::get_param(int pos) const { return fd->getParamDecl(pos); } /* Construct a method that performs one or more conversions * from the original Method (without conversions), * the name of the type to which "this" should be converted and * a function for determining the arguments of the constructed method. */ ConversionMethod::ConversionMethod(const Method &method, const std::string &this_type, const std::function &get_param) : NoCopyMethod(method), this_type(this_type), get_param_fn(get_param) { } /* Construct a method that only performs a conversion on "this" * from the original Method (without conversions) and * the name of the type to which "this" should be converted. * * Call the generic constructor with * a function for determining the arguments of the constructed method * that performs no conversion. */ ConversionMethod::ConversionMethod(const Method &method, const std::string &this_type) : ConversionMethod(method, this_type, [this] (int pos) { return Method::get_param(pos); }) { } /* Construct a method that performs one or more argument conversions * from the original Method (without conversions) and * a function for determining the arguments of the constructed method. * * Call the generic constructor with method.clazz.name as "this" type, * indicating that "this" should not be converted. */ ConversionMethod::ConversionMethod(const Method &method, const std::function &get_param) : ConversionMethod(method, method.clazz.name, get_param) { } /* Should the parameter at position "pos" be a copy (rather than * a const reference)? * * Parameters of isl type do not need to be a copy. * For other types, use the same defaults as Method. */ bool NoCopyMethod::param_needs_copy(int pos) const { ParmVarDecl *param = get_param(pos); QualType type = param->getOriginalType(); if (generator::is_isl_type(type)) return false; return Method::param_needs_copy(pos); } /* Return the method argument at position "pos". * * Call get_param_fn to determine this argument. */ clang::ParmVarDecl *ConversionMethod::get_param(int pos) const { return get_param_fn(pos); } /* Print a call to the method (without the arguments), * with "ns" the namespace of the generated C++ bindings. * * If "this_type" is different from the name of the class of the method, * then "this" needs to be converted to that type before * the call is performed. */ void ConversionMethod::print_call(std::ostream &os, const std::string &ns) const { if (clazz.name == this_type) { os << "this->"; } else { auto cpp_type = ns + cpp_generator::type2cpp(this_type); os << cpp_type << "(*this)."; } os << name; } /* Construct an object representing a C++ method for setting an enum * from the class to which is belongs, * the isl function from which it is derived and the method and enum names. */ EnumMethod::EnumMethod(const isl_class &clazz, FunctionDecl *fd, const std::string &method_name, const std::string &enum_name) : Method(clazz, fd, method_name), enum_name(enum_name) { } /* Print the use of the argument at position "pos" to "os". * * If the position is beyond the number of method arguments, * then it corresponds to the enum value corresponding to this EnumMethod. * Otherwise, delegate to Method::print_param_use. */ void EnumMethod::print_param_use(ostream &os, int pos) const { if (pos == num_params()) os << enum_name; else Method::print_param_use(os, pos); } /* Return the number of parameters of the method * (including the implicit "this"). * * The last argument of the C function does not appear in the method call, * because it is replaced by a break-up into several methods. */ int EnumMethod::num_params() const { return Method::num_params() - 1; } /* Initialize a class method printer from the stream onto which the methods * are printed, the class method description and the C++ interface generator. */ cpp_generator::class_printer::class_printer(std::ostream &os, const isl_class &clazz, cpp_generator &generator, bool declarations) : os(os), clazz(clazz), cppstring(type2cpp(clazz)), generator(generator), declarations(declarations) { }