From ee195ec16d2cf0cc219b8f75fa097dad63a49d5b Mon Sep 17 00:00:00 2001 From: Oleksandr Zinenko Date: Fri, 1 Sep 2017 13:04:04 +0200 Subject: [PATCH] cpp: export id Introduce special behavior for the id class with two features: disallow unnamed ids by construction, ensure that ids remain value-comparable. Generally, isl_id behaves like a reference-counted smart pointer to the name string and the user pointer. Additionally, it guarantees that ids with identical names and user pointers are pointer-comparable. An id object can have a "user_free" callback that is called when the reference counter reaches zero. Existing mechanism for callbacks does not apply to "user_free" callbacks as it modifies the user object passed to the callback. In particular, it creates a new object of a custom type in each call of the function that takes a callback and passes it instead of the original user pointer. Therefore, two ids constructed independently from the same user pointer would no longer be pointer-comparable. Therefore, one must pass the user pointer directly. The "user_free" callback must in turn remain a C function pointer. An alternative solution that supports std::function would require maintaining a map between user pointers and custom objects that were passed when constructing isl_ids; however, it would break direct comparability between isl_ids constructed using C and C++ interface. Support void and void * as return and argument types in the generator. Modify the generator to inject custom method declarations and definitions in the class based on the class name. Inject custom constructors, utility methods and comparison operators for isl::id. Custom constructors take either a name or a user pointer, or both. The "user_free" callback can be optionally provided in constructors or set up separately. This callback must be a C function pointer because it will be called from the C code. The user pointer is passed as void *, which can be replaced by template methods in the future, except in the "user_free" callback. The "set_user_free" function is injected so as to avoid handling a special case in callback generation. Signed-off-by: Oleksandr Zinenko --- include/isl/id.h | 4 ++- include/isl/set.h | 3 ++ interface/cpp.cc | 74 +++++++++++++++++++++++++++++++++++++- interface/cpp.h | 2 ++ interface/isl_test_cpp.cpp | 69 +++++++++++++++++++++++++++++++++++ 5 files changed, 150 insertions(+), 2 deletions(-) diff --git a/include/isl/id.h b/include/isl/id.h index c025d513..f6a31e7d 100644 --- a/include/isl/id.h +++ b/include/isl/id.h @@ -10,7 +10,7 @@ extern "C" { #endif -struct isl_id; +struct __isl_export isl_id; typedef struct isl_id isl_id; ISL_DECLARE_LIST(id) @@ -23,7 +23,9 @@ __isl_give isl_id *isl_id_alloc(isl_ctx *ctx, __isl_give isl_id *isl_id_copy(isl_id *id); __isl_null isl_id *isl_id_free(__isl_take isl_id *id); +__isl_export void *isl_id_get_user(__isl_keep isl_id *id); +__isl_export __isl_keep const char *isl_id_get_name(__isl_keep isl_id *id); __isl_give isl_id *isl_id_set_free_user(__isl_take isl_id *id, diff --git a/include/isl/set.h b/include/isl/set.h index 793195b0..9041774c 100644 --- a/include/isl/set.h +++ b/include/isl/set.h @@ -75,10 +75,13 @@ isl_bool isl_set_has_dim_id(__isl_keep isl_set *set, enum isl_dim_type type, unsigned pos); __isl_give isl_id *isl_set_get_dim_id(__isl_keep isl_set *set, enum isl_dim_type type, unsigned pos); +__isl_export __isl_give isl_set *isl_set_set_tuple_id(__isl_take isl_set *set, __isl_take isl_id *id); __isl_give isl_set *isl_set_reset_tuple_id(__isl_take isl_set *set); +__isl_export isl_bool isl_set_has_tuple_id(__isl_keep isl_set *set); +__isl_export __isl_give isl_id *isl_set_get_tuple_id(__isl_keep isl_set *set); __isl_give isl_set *isl_set_reset_user(__isl_take isl_set *set); diff --git a/interface/cpp.cc b/interface/cpp.cc index 5668088f..0cf9420e 100644 --- a/interface/cpp.cc +++ b/interface/cpp.cc @@ -171,6 +171,7 @@ void cpp_generator::print_class(ostream &os, const isl_class &clazz) print_ptr_decl(os, clazz); osprintf(os, "\n"); print_methods_decl(os, clazz); + print_custom_public_decl(os, clazz); osprintf(os, "};\n"); } @@ -371,6 +372,31 @@ void cpp_generator::print_methods_decl(ostream &os, const isl_class &clazz) print_method_group_decl(os, clazz, it->first, it->second); } +/* Print declarations for custom members of a class "clazz" to "os", based on + * the class name. + */ +void cpp_generator::print_custom_public_decl(ostream &os, + const isl_class &clazz) +{ + string cppname = type2cpp(clazz); + + if ("id" == cppname) { + const char *declarations = + " inline id(isl::ctx ctx, const std::string &name);\n" + " inline id(isl::ctx ctx, const std::string &name,\n" + " void *usr,\n" + " void (*deleter)(void *) = nullptr);\n" + " inline id(isl::ctx ctx, void *usr,\n" + " void (*deleter)(void *) = nullptr);\n" + " inline bool has_name() const;\n" + " inline id set_free_user(" + "void (*deleter)(void *)) const;\n" + " inline bool operator==(const id &other) const;\n" + " inline bool operator!=(const id &other) const;\n"; + osprintf(os, "%s", declarations); + } +} + /* Print declarations for methods "methods" of name "fullname" in class "clazz" * to "os". * @@ -427,6 +453,8 @@ void cpp_generator::print_class_impl(ostream &os, const isl_class &clazz) print_ptr_impl(os, clazz); osprintf(os, "\n"); print_methods_impl(os, clazz); + osprintf(os, "\n"); + print_custom_methods_impl(os, clazz); } /* Print implementation of global factory function to "os". @@ -563,6 +591,49 @@ void cpp_generator::print_methods_impl(ostream &os, const isl_class &clazz) } } +/* Print definitions for custom methods of class "clazz" to "os", based on the + * class name. + */ +void cpp_generator::print_custom_methods_impl(ostream &os, + const isl_class &clazz) +{ + string name = type2cpp(clazz); + if ("id" == name) { + const char *definitions = + "id::id(isl::ctx ctx, const std::string &name) {\n" + " ptr = isl_id_alloc(ctx.get(), name.c_str(),\n" + " nullptr);\n" + "}\n\n" + "id::id(isl::ctx ctx, const std::string &name,\n" + " void *user, void (*deleter)(void *)) {\n" + " ptr = isl_id_alloc(ctx.get(), name.c_str(), user);\n" + " if (deleter)\n" + " ptr = isl_id_set_free_user(ptr, deleter);\n" + "}\n\n" + "id::id(isl::ctx ctx, void *user,\n" + " void (*deleter)(void *)) {\n" + " ptr = isl_id_alloc(ctx.get(), nullptr, user);\n" + " if (deleter)\n" + " ptr = isl_id_set_free_user(ptr, deleter);\n" + "}\n\n" + "bool id::has_name() const {\n" + " return isl_id_get_name(ptr) != nullptr;\n" + "}\n\n" + "id id::set_free_user(" + "void (*deleter)(void *)) const {\n" + " auto res = isl_id_set_free_user(copy(), deleter);\n" + " return manage(res);\n" + "}\n\n" + "bool id::operator==(const isl::id &other) const {\n" + " return ptr == other.ptr;\n" + "}\n\n" + "bool id::operator!=(const isl::id &other) const {\n" + " return !operator==(other);\n" + "}\n\n"; + osprintf(os, "%s", definitions); + } +} + /* Print definitions for methods "methods" of name "fullname" in class "clazz" * to "os". * @@ -1023,7 +1094,8 @@ string cpp_generator::type2cpp(QualType type) if (is_isl_stat(type)) return "isl::stat"; - if (type->isIntegerType()) + if (type->isIntegerType() || type->isVoidType() || + type->isVoidPointerType()) return type.getAsString(); if (is_string(type)) diff --git a/interface/cpp.h b/interface/cpp.h index 1978e261..e495034d 100644 --- a/interface/cpp.h +++ b/interface/cpp.h @@ -34,6 +34,7 @@ class cpp_generator : public generator { void print_destructor_decl(ostream &os, const isl_class &clazz); void print_ptr_decl(ostream &os, const isl_class &clazz); void print_methods_decl(ostream &os, const isl_class &clazz); + void print_custom_public_decl(ostream &os, const isl_class &clazz); void print_method_group_decl(ostream &os, const isl_class &clazz, const string &fullname, const set &methods); void print_method_decl(ostream &os, const isl_class &clazz, @@ -51,6 +52,7 @@ class cpp_generator : public generator { void print_destructor_impl(ostream &os, const isl_class &clazz); void print_ptr_impl(ostream &os, const isl_class &clazz); void print_methods_impl(ostream &os, const isl_class &clazz); + void print_custom_methods_impl(ostream &os, const isl_class &clazz); void print_method_group_impl(ostream &os, const isl_class &clazz, const string &fullname, const set &methods); void print_method_impl(ostream &os, const isl_class &clazz, diff --git a/interface/isl_test_cpp.cpp b/interface/isl_test_cpp.cpp index a6b1e631..c509f950 100644 --- a/interface/isl_test_cpp.cpp +++ b/interface/isl_test_cpp.cpp @@ -314,6 +314,71 @@ void test_foreach(isl::ctx ctx) assert(ret2 == isl::stat::error); } +/* Test that ids are handled correctly and remain unique. + * + * Verify that id names are stored and returned correctly. Check that ids with + * identical names are equal and those with different names are different. + * Check that ids with identical names and different user pointers are + * different. Verify that equality holds across ids constructed from C and C++ + * interfaces. + */ +void test_id(isl::ctx ctx) +{ + isl::id id_whatever(ctx, std::string("whatever")); + assert(id_whatever.has_name()); + assert(std::string("whatever") == id_whatever.get_name()); + + isl::id id_other(ctx, std::string("whatever")); + assert(id_whatever == id_other); + + int fourtytwo = 42; + isl::id id_whatever_42(ctx, std::string("whatever"), &fourtytwo); + assert(id_whatever != id_whatever_42); + + isl::id id_whatever_42_copy(id_whatever_42); + assert(id_whatever_42 == id_whatever_42_copy); + + isl::id id_whatever_42_other(ctx, std::string("whatever"), &fourtytwo); + assert(id_whatever_42 == id_whatever_42_other); + + isl_id *cid = isl_id_alloc(ctx.get(), "whatever", &fourtytwo); + assert(cid == id_whatever_42.get()); + isl_id_free(cid); +} + +static void reset_flag(void *user) { + *static_cast(user) = 0; +} + +/* Test that user pointers of the ids are not freed as long as the exists at + * least one id pointing to them, either in C or C++. + * + * In a scope, create an id with a flag as a user object and a user_free that + * resets the flag. Use the id in a set object that lives outside the given + * scope. Check that flag is still set after the id object went out of the + * scope. Check that flag is reset after the set object went of of scope. + */ +void test_id_lifetime(isl::ctx ctx) +{ + int *flag = new int(1); + + { + isl::set set(ctx, "{:}"); + { + isl::id id(ctx, std::string("whatever"), flag, + &reset_flag); + set = set.set_tuple_id(id); + } + assert(1 == *flag); + assert(set.has_tuple_id()); + + isl::id same_id(ctx, std::string("whatever"), flag); + assert(set.get_tuple_id() == same_id); + } + assert(0 == *flag); + delete flag; +} + /* Test the isl C++ interface * * This includes: @@ -322,6 +387,8 @@ void test_foreach(isl::ctx ctx) * - Different parameter types * - Different return types * - Foreach functions + * - isl::id uniqueness + * - isl::id lifetime */ int main() { @@ -332,6 +399,8 @@ int main() test_parameters(ctx); test_return(ctx); test_foreach(ctx); + test_id(ctx); + test_id_lifetime(ctx); isl_ctx_free(ctx); }