Skip to content

Commit

Permalink
cpp: export id
Browse files Browse the repository at this point in the history
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 <git@ozinenko.com>
  • Loading branch information
ftynse committed Sep 1, 2017
1 parent c5c351b commit ee195ec
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 2 deletions.
4 changes: 3 additions & 1 deletion include/isl/id.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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,
Expand Down
3 changes: 3 additions & 0 deletions include/isl/set.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
74 changes: 73 additions & 1 deletion interface/cpp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down Expand Up @@ -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".
*
Expand Down Expand Up @@ -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".
Expand Down Expand Up @@ -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".
*
Expand Down Expand Up @@ -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))
Expand Down
2 changes: 2 additions & 0 deletions interface/cpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<FunctionDecl *> &methods);
void print_method_decl(ostream &os, const isl_class &clazz,
Expand All @@ -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<FunctionDecl *> &methods);
void print_method_impl(ostream &os, const isl_class &clazz,
Expand Down
69 changes: 69 additions & 0 deletions interface/isl_test_cpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int *>(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:
Expand All @@ -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()
{
Expand All @@ -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);
}

0 comments on commit ee195ec

Please sign in to comment.