| /* |
| * 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 <iostream> |
| #include <string> |
| #include <vector> |
| |
| #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<RecordDecl *> &exported_types, |
| set<FunctionDecl *> exported_functions, set<FunctionDecl *> 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<string> &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<string> 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<bool> &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<bool> 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 <name>_lambda, while |
| * the actual callback function is expected to be stored |
| * in a structure called <name>_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<bool> &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) { |
| std::string name = method.fd->getParamDecl(i)->getName().str(); |
| ParmVarDecl *param = method.get_param(i); |
| QualType type = param->getOriginalType(); |
| string cpptype = type_printer.param(i, 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<stat(map)> |
| */ |
| 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<std::string> superclasses; |
| std::vector<const isl_class *> parents; |
| std::vector<std::string>::iterator ci; |
| |
| superclasses = generator::find_superclasses(classes[type_str].type); |
| |
| for (ci = superclasses.begin(); ci < superclasses.end(); ci++) |
| parents.push_back(&classes[*ci]); |
| |
| while (!parents.empty()) { |
| const isl_class *candidate = parents.back(); |
| |
| parents.pop_back(); |
| |
| if (&class_type == candidate) |
| return true; |
| |
| superclasses = generator::find_superclasses(candidate->type); |
| |
| for (ci = superclasses.begin(); ci < superclasses.end(); ci++) |
| parents.push_back(&classes[*ci]); |
| } |
| |
| return false; |
| } |
| |
| /* Check if "cons" is an implicit conversion constructor of class "clazz". |
| * |
| * An implicit conversion constructor is generated in case "cons" has a single |
| * parameter, where the parameter type is a subclass of the class that is |
| * currently being generated. |
| */ |
| bool cpp_generator::is_implicit_conversion(const 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; |
| } |
| |
| /* 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 argument of "fd", if there is any. |
| * Return NULL otherwise. |
| */ |
| static ParmVarDecl *find_callback_arg(FunctionDecl *fd) |
| { |
| int num_params = fd->getNumParams(); |
| |
| for (int i = 0; i < num_params; ++i) { |
| ParmVarDecl *param = fd->getParamDecl(i); |
| if (generator::is_callback(param->getType())) |
| return param; |
| } |
| |
| return NULL; |
| } |
| |
| /* 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)), |
| callback(find_callback_arg(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. |
| * |
| * If the method has a callback argument, we reduce the number of parameters |
| * that are exposed by one to hide the user pointer from the interface. On |
| * the C++ side no user pointer is needed, as arguments can be forwarded |
| * as part of the std::function argument which specifies the callback function. |
| * |
| * 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. |
| */ |
| int Method::c_num_params() const |
| { |
| return fd->getNumParams() - (callback != NULL); |
| } |
| |
| /* 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(); |
| } |
| |
| /* 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. |
| */ |
| void Method::print_arg_list(std::ostream &os, int start, int end, |
| const std::function<void(int i)> &print_arg) |
| { |
| os << "("; |
| for (int i = start; i < end; ++i) { |
| if (i != start) |
| os << ", "; |
| print_arg(i); |
| } |
| os << ")"; |
| } |
| |
| /* Print the arguments to the method call, using "print_arg" |
| * to print each individual argument. |
| */ |
| void Method::print_cpp_arg_list(std::ostream &os, |
| const std::function<void(int i)> &print_arg) const |
| { |
| int first_param = kind == member_method ? 1 : 0; |
| print_arg_list(os, first_param, num_params(), 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<clang::ParmVarDecl *(int pos)> &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<clang::ParmVarDecl *(int pos)> &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) |
| { |
| } |