1 /*
2 * Copyright 2016, 2017 Tobias Grosser. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following
13 * disclaimer in the documentation and/or other materials provided
14 * with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY TOBIAS GROSSER ''AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SVEN VERDOOLAEGE OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
23 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * The views and conclusions contained in the software and documentation
29 * are those of the authors and should not be interpreted as
30 * representing official policies, either expressed or implied, of
31 * Tobias Grosser.
32 */
33
34 #include <cstdarg>
35 #include <cstdio>
36 #include <iostream>
37 #include <map>
38 #include <sstream>
39 #include <string>
40 #include <vector>
41
42 #include "cpp.h"
43 #include "isl_config.h"
44
45 /* Print string formatted according to "fmt" to ostream "os".
46 *
47 * This osprintf method allows us to use printf style formatting constructs when
48 * writing to an ostream.
49 */
osprintf(ostream & os,const char * format,va_list arguments)50 static void osprintf(ostream &os, const char *format, va_list arguments)
51 {
52 va_list copy;
53 char *string_pointer;
54 size_t size;
55
56 va_copy(copy, arguments);
57 size = vsnprintf(NULL, 0, format, copy);
58 string_pointer = new char[size + 1];
59 va_end(copy);
60 vsnprintf(string_pointer, size + 1, format, arguments);
61 os << string_pointer;
62 delete[] string_pointer;
63 }
64
65 /* Print string formatted according to "fmt" to ostream "os".
66 *
67 * This osprintf method allows us to use printf style formatting constructs when
68 * writing to an ostream.
69 */
osprintf(ostream & os,const char * format,...)70 static void osprintf(ostream &os, const char *format, ...)
71 {
72 va_list arguments;
73
74 va_start(arguments, format);
75 osprintf(os, format, arguments);
76 va_end(arguments);
77 }
78
79 /* Print string formatted according to "fmt" to ostream "os"
80 * with the given indentation.
81 *
82 * This osprintf method allows us to use printf style formatting constructs when
83 * writing to an ostream.
84 */
osprintf(ostream & os,int indent,const char * format,...)85 static void osprintf(ostream &os, int indent, const char *format, ...)
86 {
87 va_list arguments;
88
89 osprintf(os, "%*s", indent, " ");
90 va_start(arguments, format);
91 osprintf(os, format, arguments);
92 va_end(arguments);
93 }
94
95 /* Convert "l" to a string.
96 */
to_string(long l)97 static std::string to_string(long l)
98 {
99 std::ostringstream strm;
100 strm << l;
101 return strm.str();
102 }
103
104 /* Generate a cpp interface based on the extracted types and functions.
105 *
106 * Print first a set of forward declarations for all isl wrapper
107 * classes, then the declarations of the classes, and at the end all
108 * implementations.
109 *
110 * If checked C++ bindings are being generated,
111 * then wrap them in a namespace to avoid conflicts
112 * with the default C++ bindings (with automatic checks using exceptions).
113 */
generate()114 void cpp_generator::generate()
115 {
116 ostream &os = cout;
117
118 osprintf(os, "\n");
119 osprintf(os, "namespace isl {\n\n");
120 if (checked)
121 osprintf(os, "namespace checked {\n\n");
122
123 print_forward_declarations(os);
124 osprintf(os, "\n");
125 print_declarations(os);
126 osprintf(os, "\n");
127 print_implementations(os);
128
129 if (checked)
130 osprintf(os, "} // namespace checked\n");
131 osprintf(os, "} // namespace isl\n");
132 }
133
134 /* Print forward declarations for all classes to "os".
135 */
print_forward_declarations(ostream & os)136 void cpp_generator::print_forward_declarations(ostream &os)
137 {
138 map<string, isl_class>::iterator ci;
139
140 osprintf(os, "// forward declarations\n");
141
142 for (ci = classes.begin(); ci != classes.end(); ++ci)
143 print_class_forward_decl(os, ci->second);
144 }
145
146 /* Print all declarations to "os".
147 */
print_declarations(ostream & os)148 void cpp_generator::print_declarations(ostream &os)
149 {
150 map<string, isl_class>::iterator ci;
151 bool first = true;
152
153 for (ci = classes.begin(); ci != classes.end(); ++ci) {
154 if (first)
155 first = false;
156 else
157 osprintf(os, "\n");
158
159 print_class(os, ci->second);
160 }
161 }
162
163 /* Print all implementations to "os".
164 */
print_implementations(ostream & os)165 void cpp_generator::print_implementations(ostream &os)
166 {
167 map<string, isl_class>::iterator ci;
168 bool first = true;
169
170 for (ci = classes.begin(); ci != classes.end(); ++ci) {
171 if (first)
172 first = false;
173 else
174 osprintf(os, "\n");
175
176 print_class_impl(os, ci->second);
177 }
178 }
179
180 /* If "clazz" is a subclass that is based on a type function,
181 * then introduce a "type" field that holds the value of the type
182 * corresponding to the subclass and make the fields of the class
183 * accessible to the "isa" and "as" methods of the (immediate) superclass.
184 * In particular, "isa" needs access to the type field itself,
185 * while "as" needs access to the private constructor.
186 * In case of the "isa" method, all instances are made friends
187 * to avoid access right confusion.
188 */
print_subclass_type(ostream & os,const isl_class & clazz)189 void cpp_generator::print_subclass_type(ostream &os, const isl_class &clazz)
190 {
191 std::string cppstring = type2cpp(clazz);
192 std::string super;
193 const char *cppname = cppstring.c_str();
194 const char *supername;
195
196 if (!clazz.is_type_subclass())
197 return;
198
199 super = type2cpp(clazz.superclass_name);
200 supername = super.c_str();
201 osprintf(os, " template <class T>\n");
202 osprintf(os, " friend %s %s::isa() const;\n",
203 isl_bool2cpp().c_str(), supername);
204 osprintf(os, " friend %s %s::as<%s>() const;\n",
205 cppname, supername, cppname);
206 osprintf(os, " static const auto type = %s;\n",
207 clazz.subclass_name.c_str());
208 }
209
210 /* Print declarations for class "clazz" to "os".
211 *
212 * If "clazz" is a subclass based on a type function,
213 * then it is made to inherit from the (immediate) superclass and
214 * a "type" attribute is added for use in the "as" and "isa"
215 * methods of the superclass.
216 *
217 * Conversely, if "clazz" is a superclass with a type function,
218 * then declare those "as" and "isa" methods.
219 *
220 * The pointer to the isl object is only added for classes that
221 * are not subclasses, since subclasses refer to the same isl object.
222 */
print_class(ostream & os,const isl_class & clazz)223 void cpp_generator::print_class(ostream &os, const isl_class &clazz)
224 {
225 const char *name = clazz.name.c_str();
226 std::string cppstring = type2cpp(clazz);
227 const char *cppname = cppstring.c_str();
228
229 osprintf(os, "// declarations for isl::%s\n", cppname);
230
231 print_class_factory_decl(os, clazz);
232 osprintf(os, "\n");
233 osprintf(os, "class %s ", cppname);
234 if (clazz.is_type_subclass())
235 osprintf(os, ": public %s ",
236 type2cpp(clazz.superclass_name).c_str());
237 osprintf(os, "{\n");
238 print_subclass_type(os, clazz);
239 print_class_factory_decl(os, clazz, " friend ");
240 osprintf(os, "\n");
241 osprintf(os, "protected:\n");
242 if (!clazz.is_type_subclass()) {
243 osprintf(os, " %s *ptr = nullptr;\n", name);
244 osprintf(os, "\n");
245 }
246 print_protected_constructors_decl(os, clazz);
247 osprintf(os, "\n");
248 osprintf(os, "public:\n");
249 print_public_constructors_decl(os, clazz);
250 print_constructors_decl(os, clazz);
251 print_copy_assignment_decl(os, clazz);
252 print_destructor_decl(os, clazz);
253 print_ptr_decl(os, clazz);
254 print_downcast_decl(os, clazz);
255 print_ctx_decl(os);
256 osprintf(os, "\n");
257 print_persistent_callbacks_decl(os, clazz);
258 print_methods_decl(os, clazz);
259 print_set_enums_decl(os, clazz);
260
261 osprintf(os, "};\n");
262 }
263
264 /* Print forward declaration of class "clazz" to "os".
265 */
print_class_forward_decl(ostream & os,const isl_class & clazz)266 void cpp_generator::print_class_forward_decl(ostream &os,
267 const isl_class &clazz)
268 {
269 std::string cppstring = type2cpp(clazz);
270 const char *cppname = cppstring.c_str();
271
272 osprintf(os, "class %s;\n", cppname);
273 }
274
275 /* Print global factory functions to "os".
276 *
277 * Each class has two global factory functions:
278 *
279 * set manage(__isl_take isl_set *ptr);
280 * set manage_copy(__isl_keep isl_set *ptr);
281 *
282 * A user can construct isl C++ objects from a raw pointer and indicate whether
283 * they intend to take the ownership of the object or not through these global
284 * factory functions. This ensures isl object creation is very explicit and
285 * pointers are not converted by accident. Thanks to overloading, manage() and
286 * manage_copy() can be called on any isl raw pointer and the corresponding
287 * object is automatically created, without the user having to choose the right
288 * isl object type.
289 *
290 * For a subclass based on a type function, no factory functions
291 * are introduced because they share the C object type with
292 * the superclass.
293 */
print_class_factory_decl(ostream & os,const isl_class & clazz,const std::string & prefix)294 void cpp_generator::print_class_factory_decl(ostream &os,
295 const isl_class &clazz, const std::string &prefix)
296 {
297 const char *name = clazz.name.c_str();
298 std::string cppstring = type2cpp(clazz);
299 const char *cppname = cppstring.c_str();
300
301 if (clazz.is_type_subclass())
302 return;
303
304 os << prefix;
305 osprintf(os, "inline %s manage(__isl_take %s *ptr);\n", cppname, name);
306 os << prefix;
307 osprintf(os, "inline %s manage_copy(__isl_keep %s *ptr);\n",
308 cppname, name);
309 }
310
311 /* Print declarations of protected constructors for class "clazz" to "os".
312 *
313 * Each class has currently one protected constructor:
314 *
315 * 1) Constructor from a plain isl_* C pointer
316 *
317 * Example:
318 *
319 * set(__isl_take isl_set *ptr);
320 *
321 * The raw pointer constructor is kept protected. Object creation is only
322 * possible through manage() or manage_copy().
323 */
print_protected_constructors_decl(ostream & os,const isl_class & clazz)324 void cpp_generator::print_protected_constructors_decl(ostream &os,
325 const isl_class &clazz)
326 {
327 const char *name = clazz.name.c_str();
328 std::string cppstring = type2cpp(clazz);
329 const char *cppname = cppstring.c_str();
330
331 osprintf(os, " inline explicit %s(__isl_take %s *ptr);\n", cppname,
332 name);
333 }
334
335 /* Print declarations of public constructors for class "clazz" to "os".
336 *
337 * Each class currently has two public constructors:
338 *
339 * 1) A default constructor
340 * 2) A copy constructor
341 *
342 * Example:
343 *
344 * set();
345 * set(const set &set);
346 */
print_public_constructors_decl(ostream & os,const isl_class & clazz)347 void cpp_generator::print_public_constructors_decl(ostream &os,
348 const isl_class &clazz)
349 {
350 std::string cppstring = type2cpp(clazz);
351 const char *cppname = cppstring.c_str();
352 osprintf(os, " inline /* implicit */ %s();\n", cppname);
353
354 osprintf(os, " inline /* implicit */ %s(const %s &obj);\n",
355 cppname, cppname);
356 }
357
358 /* Print declarations for "method" in class "clazz" to "os".
359 *
360 * "kind" specifies the kind of method that should be generated.
361 *
362 * "convert" specifies which of the method arguments should
363 * be automatically converted.
364 */
365 template <>
print_method(ostream & os,const isl_class & clazz,FunctionDecl * method,function_kind kind,const std::vector<bool> & convert)366 void cpp_generator::print_method<cpp_generator::decl>(ostream &os,
367 const isl_class &clazz, FunctionDecl *method, function_kind kind,
368 const std::vector<bool> &convert)
369 {
370 string name = clazz.method_name(method);
371
372 print_named_method_decl(os, clazz, method, name, kind, convert);
373 }
374
375 /* Print declarations for "method" in class "clazz" to "os",
376 * without any argument conversions.
377 *
378 * "kind" specifies the kind of method that should be generated.
379 */
380 template <>
print_method(ostream & os,const isl_class & clazz,FunctionDecl * method,function_kind kind)381 void cpp_generator::print_method<cpp_generator::decl>(ostream &os,
382 const isl_class &clazz, FunctionDecl *method, function_kind kind)
383 {
384 print_method<decl>(os, clazz,method, kind, {});
385 }
386
387 /* Print declarations for constructors for class "class" to "os".
388 *
389 * For each isl function that is marked as __isl_constructor,
390 * add a corresponding C++ constructor.
391 *
392 * Example:
393 *
394 * inline /\* implicit *\/ union_set(basic_set bset);
395 * inline /\* implicit *\/ union_set(set set);
396 * inline explicit val(ctx ctx, long i);
397 * inline explicit val(ctx ctx, const std::string &str);
398 */
print_constructors_decl(ostream & os,const isl_class & clazz)399 void cpp_generator::print_constructors_decl(ostream &os,
400 const isl_class &clazz)
401 {
402 function_set::const_iterator in;
403 const function_set &constructors = clazz.constructors;
404
405 for (in = constructors.begin(); in != constructors.end(); ++in) {
406 FunctionDecl *cons = *in;
407
408 print_method<decl>(os, clazz, cons, function_kind_constructor);
409 }
410 }
411
412 /* Print declarations of copy assignment operator for class "clazz"
413 * to "os".
414 *
415 * Each class has one assignment operator.
416 *
417 * isl:set &set::operator=(set obj)
418 *
419 */
print_copy_assignment_decl(ostream & os,const isl_class & clazz)420 void cpp_generator::print_copy_assignment_decl(ostream &os,
421 const isl_class &clazz)
422 {
423 std::string cppstring = type2cpp(clazz);
424 const char *cppname = cppstring.c_str();
425
426 osprintf(os, " inline %s &operator=(%s obj);\n", cppname, cppname);
427 }
428
429 /* Print declaration of destructor for class "clazz" to "os".
430 *
431 * No explicit destructor is needed for type based subclasses.
432 */
print_destructor_decl(ostream & os,const isl_class & clazz)433 void cpp_generator::print_destructor_decl(ostream &os, const isl_class &clazz)
434 {
435 std::string cppstring = type2cpp(clazz);
436 const char *cppname = cppstring.c_str();
437
438 if (clazz.is_type_subclass())
439 return;
440
441 osprintf(os, " inline ~%s();\n", cppname);
442 }
443
444 /* Print declaration of pointer functions for class "clazz" to "os".
445 * Since type based subclasses share the pointer with their superclass,
446 * they can also reuse these functions from the superclass.
447 *
448 * To obtain a raw pointer three functions are provided:
449 *
450 * 1) __isl_give isl_set *copy()
451 *
452 * Returns a pointer to a _copy_ of the internal object
453 *
454 * 2) __isl_keep isl_set *get()
455 *
456 * Returns a pointer to the internal object
457 *
458 * 3) __isl_give isl_set *release()
459 *
460 * Returns a pointer to the internal object and resets the
461 * internal pointer to nullptr.
462 *
463 * We also provide functionality to explicitly check if a pointer is
464 * currently managed by this object.
465 *
466 * 4) bool is_null()
467 *
468 * Check if the current object is a null pointer.
469 *
470 * The functions get() and release() model the value_ptr proposed in
471 * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3339.pdf.
472 * The copy() function is an extension to allow the user to explicitly
473 * copy the underlying object.
474 *
475 * Also generate a declaration to delete copy() for r-values, for
476 * r-values release() should be used to avoid unnecessary copies.
477 */
print_ptr_decl(ostream & os,const isl_class & clazz)478 void cpp_generator::print_ptr_decl(ostream &os, const isl_class &clazz)
479 {
480 const char *name = clazz.name.c_str();
481
482 if (clazz.is_type_subclass())
483 return;
484
485 osprintf(os, " inline __isl_give %s *copy() const &;\n", name);
486 osprintf(os, " inline __isl_give %s *copy() && = delete;\n", name);
487 osprintf(os, " inline __isl_keep %s *get() const;\n", name);
488 osprintf(os, " inline __isl_give %s *release();\n", name);
489 osprintf(os, " inline bool is_null() const;\n");
490 }
491
492 /* Print a template declaration with given indentation
493 * for the "isa_type" method that ensures it is only enabled
494 * when called with a template argument
495 * that represents a type that is equal to that
496 * of the return type of the type function of "super".
497 * In particular, "isa_type" gets called from "isa"
498 * with as template argument the type of the "type" field
499 * of the subclass.
500 * The check ensures that this subclass is in fact a direct subclass
501 * of "super".
502 */
print_isa_type_template(ostream & os,int indent,const isl_class & super)503 void cpp_generator::print_isa_type_template(ostream &os, int indent,
504 const isl_class &super)
505 {
506 osprintf(os, indent,
507 "template <typename T,\n");
508 osprintf(os, indent,
509 " typename = typename std::enable_if<std::is_same<\n");
510 osprintf(os, indent,
511 " const decltype(%s(NULL)),\n",
512 super.fn_type->getNameAsString().c_str());
513 osprintf(os, indent,
514 " const T>::value>::type>\n");
515 }
516
517 /* Print declarations for the "as" and "isa" methods, if "clazz"
518 * is a superclass with a type function.
519 *
520 * "isa" checks whether an object is of a given subclass type.
521 * "isa_type" does the same, but gets passed the value of the type field
522 * of the subclass as a function argument and the type of this field
523 * as a template argument.
524 * "as" tries to cast an object to a given subclass type, returning
525 * an invalid object if the object is not of the given type.
526 */
print_downcast_decl(ostream & os,const isl_class & clazz)527 void cpp_generator::print_downcast_decl(ostream &os, const isl_class &clazz)
528 {
529 if (!clazz.fn_type)
530 return;
531
532 osprintf(os, "private:\n");
533 print_isa_type_template(os, 2, clazz);
534 osprintf(os, " inline %s isa_type(T subtype) const;\n",
535 isl_bool2cpp().c_str());
536 osprintf(os, "public:\n");
537 osprintf(os, " template <class T> inline %s isa() const;\n",
538 isl_bool2cpp().c_str());
539 osprintf(os, " template <class T> inline T as() const;\n");
540 }
541
542 /* Print the declaration of the ctx method.
543 */
print_ctx_decl(ostream & os)544 void cpp_generator::print_ctx_decl(ostream &os)
545 {
546 std::string ns = isl_namespace();
547
548 osprintf(os, " inline %sctx ctx() const;\n", ns.c_str());
549 }
550
551 /* Add a space to the return type "type" if needed,
552 * i.e., if it is not the type of a pointer.
553 */
add_space_to_return_type(const string & type)554 static string add_space_to_return_type(const string &type)
555 {
556 if (type[type.size() - 1] == '*')
557 return type;
558 return type + " ";
559 }
560
561 /* Print the prototype of the static inline method that is used
562 * as the C callback of "clazz" set by "method" to "os".
563 */
print_persistent_callback_prototype(ostream & os,const isl_class & clazz,FunctionDecl * method,bool is_declaration)564 void cpp_generator::print_persistent_callback_prototype(ostream &os,
565 const isl_class &clazz, FunctionDecl *method, bool is_declaration)
566 {
567 string callback_name, rettype, c_args;
568 ParmVarDecl *param = persistent_callback_arg(method);
569 const FunctionProtoType *callback;
570 QualType ptype;
571 string classname;
572
573 ptype = param->getType();
574 callback = extract_prototype(ptype);
575
576 rettype = callback->getReturnType().getAsString();
577 rettype = add_space_to_return_type(rettype);
578 callback_name = clazz.persistent_callback_name(method);
579 c_args = generate_callback_args(ptype, false);
580
581 if (!is_declaration)
582 classname = type2cpp(clazz) + "::";
583
584 osprintf(os, "%s%s%s(%s)",
585 rettype.c_str(), classname.c_str(),
586 callback_name.c_str(), c_args.c_str());
587 }
588
589 /* Print the prototype of the method for setting the callback function
590 * of "clazz" set by "method" to "os".
591 */
print_persistent_callback_setter_prototype(ostream & os,const isl_class & clazz,FunctionDecl * method,bool is_declaration)592 void cpp_generator::print_persistent_callback_setter_prototype(ostream &os,
593 const isl_class &clazz, FunctionDecl *method, bool is_declaration)
594 {
595 string classname, callback_name, cpptype;
596 ParmVarDecl *param = persistent_callback_arg(method);
597
598 if (!is_declaration)
599 classname = type2cpp(clazz) + "::";
600
601 cpptype = type2cpp(param->getOriginalType());
602 callback_name = clazz.persistent_callback_name(method);
603 osprintf(os, "void %sset_%s_data(const %s &%s)",
604 classname.c_str(), callback_name.c_str(), cpptype.c_str(),
605 param->getName().str().c_str());
606 }
607
608 /* Given a function "method" for setting a "clazz" persistent callback,
609 * print the fields that are needed for marshalling the callback to "os".
610 *
611 * In particular, print
612 * - the declaration of a data structure for storing the C++ callback function
613 * - a shared pointer to such a data structure
614 * - the declaration of a static inline method
615 * for use as the C callback function
616 * - the declaration of a private method for setting the callback function
617 */
print_persistent_callback_data(ostream & os,const isl_class & clazz,FunctionDecl * method)618 void cpp_generator::print_persistent_callback_data(ostream &os,
619 const isl_class &clazz, FunctionDecl *method)
620 {
621 string callback_name;
622 ParmVarDecl *param = persistent_callback_arg(method);
623
624 callback_name = clazz.persistent_callback_name(method);
625 print_callback_data_decl(os, param, callback_name);
626 osprintf(os, ";\n");
627 osprintf(os, " std::shared_ptr<%s_data> %s_data;\n",
628 callback_name.c_str(), callback_name.c_str());
629 osprintf(os, " static inline ");
630 print_persistent_callback_prototype(os, clazz, method, true);
631 osprintf(os, ";\n");
632 osprintf(os, " inline ");
633 print_persistent_callback_setter_prototype(os, clazz, method, true);
634 osprintf(os, ";\n");
635 }
636
637 /* Print declarations needed for the persistent callbacks of "clazz".
638 *
639 * In particular, if there are any persistent callbacks, then
640 * print a private method for copying callback data from
641 * one object to another,
642 * private data for keeping track of the persistent callbacks and
643 * public methods for setting the persistent callbacks.
644 */
print_persistent_callbacks_decl(ostream & os,const isl_class & clazz)645 void cpp_generator::print_persistent_callbacks_decl(ostream &os,
646 const isl_class &clazz)
647 {
648 std::string cppstring = type2cpp(clazz);
649 const char *cppname = cppstring.c_str();
650 set<FunctionDecl *>::const_iterator in;
651 const set<FunctionDecl *> &callbacks = clazz.persistent_callbacks;
652
653 if (!clazz.has_persistent_callbacks())
654 return;
655
656 osprintf(os, "private:\n");
657 osprintf(os, " inline %s ©_callbacks(const %s &obj);\n",
658 cppname, cppname);
659 for (in = callbacks.begin(); in != callbacks.end(); ++in)
660 print_persistent_callback_data(os, clazz, *in);
661
662 osprintf(os, "public:\n");
663 for (in = callbacks.begin(); in != callbacks.end(); ++in)
664 print_method<decl>(os, clazz, *in, function_kind_member_method);
665 }
666
667 /* Print declarations for methods in class "clazz" to "os".
668 */
print_methods_decl(ostream & os,const isl_class & clazz)669 void cpp_generator::print_methods_decl(ostream &os, const isl_class &clazz)
670 {
671 map<string, function_set >::const_iterator it;
672
673 for (it = clazz.methods.begin(); it != clazz.methods.end(); ++it)
674 print_method_group_decl(os, clazz, it->second);
675 }
676
677 /* Print a declaration for a method "name" in "clazz" derived
678 * from "fd", which sets an enum, to "os".
679 *
680 * The last argument is removed because it is replaced by
681 * a break-up into several methods.
682 */
print_set_enum_decl(ostream & os,const isl_class & clazz,FunctionDecl * fd,const string & name)683 void cpp_generator::print_set_enum_decl(ostream &os, const isl_class &clazz,
684 FunctionDecl *fd, const string &name)
685 {
686 int n = fd->getNumParams();
687
688 print_method_header(os, clazz, fd, name, n - 1, true,
689 function_kind_member_method);
690 }
691
692 /* Print declarations for the methods in "clazz" derived from "fd",
693 * which sets an enum, to "os".
694 *
695 * A method is generated for each value in the enum, setting
696 * the enum to that value.
697 */
print_set_enums_decl(ostream & os,const isl_class & clazz,FunctionDecl * fd)698 void cpp_generator::print_set_enums_decl(ostream &os, const isl_class &clazz,
699 FunctionDecl *fd)
700 {
701 vector<set_enum>::const_iterator it;
702 const vector<set_enum> &set_enums = clazz.set_enums.at(fd);
703
704 for (it = set_enums.begin(); it != set_enums.end(); ++it)
705 print_set_enum_decl(os, clazz, fd, it->method_name);
706 }
707
708 /* Print declarations for methods in "clazz" derived from functions
709 * that set an enum, to "os".
710 */
print_set_enums_decl(ostream & os,const isl_class & clazz)711 void cpp_generator::print_set_enums_decl(ostream &os, const isl_class &clazz)
712 {
713 map<FunctionDecl *, vector<set_enum> >::const_iterator it;
714
715 for (it = clazz.set_enums.begin(); it != clazz.set_enums.end(); ++it)
716 print_set_enums_decl(os, clazz, it->first);
717 }
718
719 /* Print a declaration for the "get" method "fd" in class "clazz",
720 * using a name that includes the "get_" prefix, to "os".
721 */
722 template<>
print_get_method(ostream & os,const isl_class & clazz,FunctionDecl * fd)723 void cpp_generator::print_get_method<cpp_generator::decl>(ostream &os,
724 const isl_class &clazz, FunctionDecl *fd)
725 {
726 function_kind kind = function_kind_member_method;
727 string base = clazz.base_method_name(fd);
728
729 print_named_method_decl(os, clazz, fd, base, kind);
730 }
731
732 /* Update "convert" to reflect the next combination of automatic conversions
733 * for the arguments of "fd",
734 * returning false if there are no more combinations.
735 *
736 * In particular, find the last argument for which an automatic
737 * conversion function is available mapping to the type of this argument and
738 * that is not already marked for conversion.
739 * Mark this argument, if any, for conversion and clear the markings
740 * of all subsequent arguments.
741 * Repeated calls to this method therefore run through
742 * all possible combinations.
743 *
744 * Note that the first function argument is never considered
745 * for automatic conversion since this is the argument
746 * from which the isl_ctx used in the conversion is extracted.
747 */
next_variant(FunctionDecl * fd,std::vector<bool> & convert)748 bool cpp_generator::next_variant(FunctionDecl *fd, std::vector<bool> &convert)
749 {
750 size_t n = convert.size();
751
752 for (int i = n - 1; i >= 1; --i) {
753 ParmVarDecl *param = fd->getParamDecl(i);
754 const Type *type = param->getOriginalType().getTypePtr();
755
756 if (conversions.count(type) == 0)
757 continue;
758 if (convert[i])
759 continue;
760 convert[i] = true;
761 for (size_t j = i + 1; j < n; ++j)
762 convert[j] = false;
763 return true;
764 }
765
766 return false;
767 }
768
769 /* Print a declaration or definition for method "fd" in class "clazz"
770 * to "os".
771 *
772 * For methods that are identified as "get" methods, also
773 * print a declaration or definition for the method
774 * using a name that includes the "get_" prefix.
775 *
776 * If the generated method is an object method, then check
777 * whether any of its arguments can be automatically converted
778 * from something else, and, if so, generate a method
779 * for each combination of converted arguments.
780 */
781 template <enum cpp_generator::method_part part>
print_method_variants(ostream & os,const isl_class & clazz,FunctionDecl * fd)782 void cpp_generator::print_method_variants(ostream &os, const isl_class &clazz,
783 FunctionDecl *fd)
784 {
785 function_kind kind = get_method_kind(clazz, fd);
786 std::vector<bool> convert(fd->getNumParams());
787
788 print_method<part>(os, clazz, fd, kind);
789 if (clazz.is_get_method(fd))
790 print_get_method<part>(os, clazz, fd);
791 if (kind == function_kind_member_method)
792 while (next_variant(fd, convert))
793 print_method<part>(os, clazz, fd, kind, convert);
794 }
795
796 /* Print declarations for methods "methods" in class "clazz" to "os".
797 */
print_method_group_decl(ostream & os,const isl_class & clazz,const function_set & methods)798 void cpp_generator::print_method_group_decl(ostream &os, const isl_class &clazz,
799 const function_set &methods)
800 {
801 function_set::const_iterator it;
802
803 for (it = methods.begin(); it != methods.end(); ++it)
804 print_method_variants<decl>(os, clazz, *it);
805 }
806
807 /* Print a declaration for a method called "name" in class "clazz"
808 * derived from "fd" to "os".
809 *
810 * "kind" specifies the kind of method that should be generated.
811 *
812 * "convert" specifies which of the method arguments should
813 * be automatically converted.
814 */
print_named_method_decl(ostream & os,const isl_class & clazz,FunctionDecl * fd,const string & name,function_kind kind,const std::vector<bool> & convert)815 void cpp_generator::print_named_method_decl(ostream &os, const isl_class &clazz,
816 FunctionDecl *fd, const string &name, function_kind kind,
817 const std::vector<bool> &convert)
818 {
819 print_named_method_header(os, clazz, fd, name, true, kind, convert);
820 }
821
822 /* Print implementations for class "clazz" to "os".
823 */
print_class_impl(ostream & os,const isl_class & clazz)824 void cpp_generator::print_class_impl(ostream &os, const isl_class &clazz)
825 {
826 std::string cppstring = type2cpp(clazz);
827 const char *cppname = cppstring.c_str();
828
829 osprintf(os, "// implementations for isl::%s", cppname);
830
831 print_class_factory_impl(os, clazz);
832 print_public_constructors_impl(os, clazz);
833 print_protected_constructors_impl(os, clazz);
834 print_constructors_impl(os, clazz);
835 print_copy_assignment_impl(os, clazz);
836 print_destructor_impl(os, clazz);
837 print_ptr_impl(os, clazz);
838 print_downcast_impl(os, clazz);
839 print_ctx_impl(os, clazz);
840 print_persistent_callbacks_impl(os, clazz);
841 print_methods_impl(os, clazz);
842 print_set_enums_impl(os, clazz);
843 print_stream_insertion(os, clazz);
844 }
845
846 /* Print code for throwing an exception corresponding to the last error
847 * that occurred on "saved_ctx".
848 * This assumes that a valid isl::ctx is available in the "saved_ctx" variable,
849 * e.g., through a prior call to print_save_ctx.
850 */
print_throw_last_error(ostream & os)851 static void print_throw_last_error(ostream &os)
852 {
853 osprintf(os, " exception::throw_last_error(saved_ctx);\n");
854 }
855
856 /* Print code with the given indentation
857 * for throwing an exception_invalid with the given message.
858 */
print_throw_invalid(ostream & os,int indent,const char * msg)859 static void print_throw_invalid(ostream &os, int indent, const char *msg)
860 {
861 osprintf(os, indent,
862 "exception::throw_invalid(\"%s\", __FILE__, __LINE__);\n", msg);
863 }
864
865 /* Print code for throwing an exception on NULL input.
866 */
print_throw_NULL_input(ostream & os)867 static void print_throw_NULL_input(ostream &os)
868 {
869 print_throw_invalid(os, 4, "NULL input");
870 }
871
872 /* Print code with the given indentation
873 * for acting on an invalid error with message "msg".
874 * In particular, throw an exception_invalid.
875 * In the checked C++ bindings, isl_die is called instead with the code
876 * in "checked_code".
877 */
print_invalid(ostream & os,int indent,const char * msg,const char * checked_code)878 void cpp_generator::print_invalid(ostream &os, int indent, const char *msg,
879 const char *checked_code)
880 {
881 if (checked)
882 osprintf(os, indent,
883 "isl_die(ctx().get(), isl_error_invalid, "
884 "\"%s\", %s);\n", msg, checked_code);
885 else
886 print_throw_invalid(os, indent, msg);
887 }
888
889 /* Print an operator for inserting objects of "class"
890 * into an output stream.
891 *
892 * Unless checked C++ bindings are being generated,
893 * the operator requires its argument to be non-NULL.
894 * An exception is thrown if anything went wrong during the printing.
895 * During this printing, isl is made not to print any error message
896 * because the error message is included in the exception.
897 *
898 * If checked C++ bindings are being generated and anything went wrong,
899 * then record this failure in the output stream.
900 */
print_stream_insertion(ostream & os,const isl_class & clazz)901 void cpp_generator::print_stream_insertion(ostream &os, const isl_class &clazz)
902 {
903 const char *name = clazz.name.c_str();
904 std::string cppstring = type2cpp(clazz);
905 const char *cppname = cppstring.c_str();
906
907 if (!clazz.fn_to_str)
908 return;
909
910 osprintf(os, "\n");
911 osprintf(os, "inline std::ostream &operator<<(std::ostream &os, ");
912 osprintf(os, "const %s &obj)\n", cppname);
913 osprintf(os, "{\n");
914 print_check_ptr_start(os, clazz, "obj.get()");
915 osprintf(os, " char *str = %s_to_str(obj.get());\n", name);
916 print_check_ptr_end(os, "str");
917 if (checked) {
918 osprintf(os, " if (!str) {\n");
919 osprintf(os, " os.setstate(std::ios_base::badbit);\n");
920 osprintf(os, " return os;\n");
921 osprintf(os, " }\n");
922 }
923 osprintf(os, " os << str;\n");
924 osprintf(os, " free(str);\n");
925 osprintf(os, " return os;\n");
926 osprintf(os, "}\n");
927 }
928
929 /* Print code that checks that "ptr" is not NULL at input.
930 *
931 * Omit the check if checked C++ bindings are being generated.
932 */
print_check_ptr(ostream & os,const char * ptr)933 void cpp_generator::print_check_ptr(ostream &os, const char *ptr)
934 {
935 if (checked)
936 return;
937
938 osprintf(os, " if (!%s)\n", ptr);
939 print_throw_NULL_input(os);
940 }
941
942 /* Print code that checks that "ptr" is not NULL at input and
943 * that saves a copy of the isl_ctx of "ptr" for a later check.
944 *
945 * Omit the check if checked C++ bindings are being generated.
946 */
print_check_ptr_start(ostream & os,const isl_class & clazz,const char * ptr)947 void cpp_generator::print_check_ptr_start(ostream &os, const isl_class &clazz,
948 const char *ptr)
949 {
950 if (checked)
951 return;
952
953 print_check_ptr(os, ptr);
954 osprintf(os, " auto saved_ctx = %s_get_ctx(%s);\n",
955 clazz.name.c_str(), ptr);
956 print_on_error_continue(os);
957 }
958
959 /* Print code that checks that "ptr" is not NULL at the end.
960 * A copy of the isl_ctx is expected to have been saved by
961 * code generated by print_check_ptr_start.
962 *
963 * Omit the check if checked C++ bindings are being generated.
964 */
print_check_ptr_end(ostream & os,const char * ptr)965 void cpp_generator::print_check_ptr_end(ostream &os, const char *ptr)
966 {
967 if (checked)
968 return;
969
970 osprintf(os, " if (!%s)\n", ptr);
971 print_throw_last_error(os);
972 }
973
974 /* Print implementation of global factory functions to "os".
975 *
976 * Each class has two global factory functions:
977 *
978 * set manage(__isl_take isl_set *ptr);
979 * set manage_copy(__isl_keep isl_set *ptr);
980 *
981 * Unless checked C++ bindings are being generated,
982 * both functions require the argument to be non-NULL.
983 * An exception is thrown if anything went wrong during the copying
984 * in manage_copy.
985 * During the copying, isl is made not to print any error message
986 * because the error message is included in the exception.
987 *
988 * For a subclass based on a type function, no factory functions
989 * are introduced because they share the C object type with
990 * the superclass.
991 */
print_class_factory_impl(ostream & os,const isl_class & clazz)992 void cpp_generator::print_class_factory_impl(ostream &os,
993 const isl_class &clazz)
994 {
995 const char *name = clazz.name.c_str();
996 std::string cppstring = type2cpp(clazz);
997 const char *cppname = cppstring.c_str();
998
999 if (clazz.is_type_subclass())
1000 return;
1001
1002 osprintf(os, "\n");
1003 osprintf(os, "%s manage(__isl_take %s *ptr) {\n", cppname, name);
1004 print_check_ptr(os, "ptr");
1005 osprintf(os, " return %s(ptr);\n", cppname);
1006 osprintf(os, "}\n");
1007
1008 osprintf(os, "%s manage_copy(__isl_keep %s *ptr) {\n", cppname,
1009 name);
1010 print_check_ptr_start(os, clazz, "ptr");
1011 osprintf(os, " ptr = %s_copy(ptr);\n", name);
1012 print_check_ptr_end(os, "ptr");
1013 osprintf(os, " return %s(ptr);\n", cppname);
1014 osprintf(os, "}\n");
1015 }
1016
1017 /* Print implementations of protected constructors for class "clazz" to "os".
1018 *
1019 * The pointer to the isl object is either initialized directly or
1020 * through the (immediate) superclass.
1021 */
print_protected_constructors_impl(ostream & os,const isl_class & clazz)1022 void cpp_generator::print_protected_constructors_impl(ostream &os,
1023 const isl_class &clazz)
1024 {
1025 const char *name = clazz.name.c_str();
1026 std::string cppstring = type2cpp(clazz);
1027 const char *cppname = cppstring.c_str();
1028 bool subclass = clazz.is_type_subclass();
1029
1030 osprintf(os, "\n");
1031 osprintf(os, "%s::%s(__isl_take %s *ptr)\n", cppname, cppname, name);
1032 if (subclass)
1033 osprintf(os, " : %s(ptr) {}\n",
1034 type2cpp(clazz.superclass_name).c_str());
1035 else
1036 osprintf(os, " : ptr(ptr) {}\n");
1037 }
1038
1039 /* Print implementations of public constructors for class "clazz" to "os".
1040 *
1041 * The pointer to the isl object is either initialized directly or
1042 * through the (immediate) superclass.
1043 *
1044 * If the class has any persistent callbacks, then copy them
1045 * from the original object in the copy constructor.
1046 * If the class is a subclass, then the persistent callbacks
1047 * are assumed to be copied by the copy constructor of the superclass.
1048 *
1049 * Throw an exception from the copy constructor if anything went wrong
1050 * during the copying or if the input is NULL, if any copying is performed.
1051 * During the copying, isl is made not to print any error message
1052 * because the error message is included in the exception.
1053 * No exceptions are thrown if checked C++ bindings
1054 * are being generated,
1055 */
print_public_constructors_impl(ostream & os,const isl_class & clazz)1056 void cpp_generator::print_public_constructors_impl(ostream &os,
1057 const isl_class &clazz)
1058 {
1059 std::string cppstring = type2cpp(clazz);
1060 std::string super;
1061 const char *cppname = cppstring.c_str();
1062 bool subclass = clazz.is_type_subclass();
1063
1064 osprintf(os, "\n");
1065 if (subclass)
1066 super = type2cpp(clazz.superclass_name);
1067 osprintf(os, "%s::%s()\n", cppname, cppname);
1068 if (subclass)
1069 osprintf(os, " : %s() {}\n\n", super.c_str());
1070 else
1071 osprintf(os, " : ptr(nullptr) {}\n\n");
1072 osprintf(os, "%s::%s(const %s &obj)\n", cppname, cppname, cppname);
1073 if (subclass)
1074 osprintf(os, " : %s(obj)\n", super.c_str());
1075 else
1076 osprintf(os, " : ptr(nullptr)\n");
1077 osprintf(os, "{\n");
1078 if (!subclass) {
1079 print_check_ptr_start(os, clazz, "obj.ptr");
1080 osprintf(os, " ptr = obj.copy();\n");
1081 if (clazz.has_persistent_callbacks())
1082 osprintf(os, " copy_callbacks(obj);\n");
1083 print_check_ptr_end(os, "ptr");
1084 }
1085 osprintf(os, "}\n");
1086 }
1087
1088 /* Print definition for "method" in class "clazz" to "os",
1089 * without any automatic type conversions.
1090 *
1091 * "kind" specifies the kind of method that should be generated.
1092 *
1093 * This method distinguishes three kinds of methods: member methods, static
1094 * methods, and constructors.
1095 *
1096 * Member methods call "method" by passing to the underlying isl function the
1097 * isl object belonging to "this" as first argument and the remaining arguments
1098 * as subsequent arguments.
1099 *
1100 * Static methods call "method" by passing all arguments to the underlying isl
1101 * function, as no this-pointer is available. The result is a newly managed
1102 * isl C++ object.
1103 *
1104 * Constructors create a new object from a given set of input parameters. They
1105 * do not return a value, but instead update the pointer stored inside the
1106 * newly created object.
1107 *
1108 * If the method has a callback argument, we reduce the number of parameters
1109 * that are exposed by one to hide the user pointer from the interface. On
1110 * the C++ side no user pointer is needed, as arguments can be forwarded
1111 * as part of the std::function argument which specifies the callback function.
1112 *
1113 * Unless checked C++ bindings are being generated,
1114 * the inputs of the method are first checked for being valid isl objects and
1115 * a copy of the associated isl::ctx is saved (if needed).
1116 * If any failure occurs, either during the check for the inputs or
1117 * during the isl function call, an exception is thrown.
1118 * During the function call, isl is made not to print any error message
1119 * because the error message is included in the exception.
1120 */
1121 template<>
print_method(ostream & os,const isl_class & clazz,FunctionDecl * method,function_kind kind)1122 void cpp_generator::print_method<cpp_generator::impl>(ostream &os,
1123 const isl_class &clazz, FunctionDecl *method, function_kind kind)
1124 {
1125 string methodname = method->getName().str();
1126 int num_params = method->getNumParams();
1127
1128 osprintf(os, "\n");
1129 print_method_header(os, clazz, method, false, kind);
1130 osprintf(os, "{\n");
1131 print_argument_validity_check(os, method, kind);
1132 print_save_ctx(os, method, kind);
1133 print_on_error_continue(os);
1134
1135 for (int i = 0; i < num_params; ++i) {
1136 ParmVarDecl *param = method->getParamDecl(i);
1137 if (is_callback(param->getType())) {
1138 num_params -= 1;
1139 print_callback_local(os, param);
1140 }
1141 }
1142
1143 osprintf(os, " auto res = %s(", methodname.c_str());
1144
1145 for (int i = 0; i < num_params; ++i) {
1146 ParmVarDecl *param = method->getParamDecl(i);
1147 bool load_from_this_ptr = false;
1148
1149 if (i == 0 && kind == function_kind_member_method)
1150 load_from_this_ptr = true;
1151
1152 print_method_param_use(os, param, load_from_this_ptr);
1153
1154 if (i != num_params - 1)
1155 osprintf(os, ", ");
1156 }
1157 osprintf(os, ");\n");
1158
1159 print_exceptional_execution_check(os, clazz, method, kind);
1160 if (kind == function_kind_constructor) {
1161 osprintf(os, " ptr = res;\n");
1162 } else {
1163 print_method_return(os, clazz, method);
1164 }
1165
1166 osprintf(os, "}\n");
1167 }
1168
1169 /* Print a definition for "method" in class "clazz" to "os",
1170 * where at least one of the argument types needs to be converted,
1171 * as specified by "convert".
1172 *
1173 * "kind" specifies the kind of method that should be generated and
1174 * is assumed to be set to function_kind_member_method.
1175 *
1176 * The generated method performs the required conversion(s) and
1177 * calls the method generated without conversions.
1178 *
1179 * Each conversion is performed by calling the conversion function
1180 * with as arguments the isl_ctx of the object and the argument
1181 * to the generated method.
1182 * In order to be able to use this isl_ctx, the current object needs
1183 * to valid. The validity of other arguments is checked
1184 * by the called method.
1185 */
1186 template<>
print_method(ostream & os,const isl_class & clazz,FunctionDecl * method,function_kind kind,const std::vector<bool> & convert)1187 void cpp_generator::print_method<cpp_generator::impl>(ostream &os,
1188 const isl_class &clazz, FunctionDecl *method, function_kind kind,
1189 const std::vector<bool> &convert)
1190 {
1191 string name = clazz.method_name(method);
1192 int num_params = method->getNumParams();
1193
1194 if (kind != function_kind_member_method)
1195 die("Automatic conversion currently only supported "
1196 "for object methods");
1197
1198 osprintf(os, "\n");
1199 print_named_method_header(os, clazz, method, name, false,
1200 kind, convert);
1201 osprintf(os, "{\n");
1202 print_check_ptr(os, "ptr");
1203 osprintf(os, " return this->%s(", name.c_str());
1204 for (int i = 1; i < num_params; ++i) {
1205 ParmVarDecl *param = method->getParamDecl(i);
1206 std::string name = param->getName().str();
1207
1208 if (i != 1)
1209 osprintf(os, ", ");
1210 if (convert[i]) {
1211 QualType type = param->getOriginalType();
1212 string cpptype = type2cpp(type);
1213 osprintf(os, "%s(ctx(), %s)",
1214 cpptype.c_str(), name.c_str());
1215 } else {
1216 osprintf(os, "%s", name.c_str());
1217 }
1218 }
1219 osprintf(os, ");\n");
1220 osprintf(os, "}\n");
1221 }
1222
1223 /* Print implementations of constructors for class "clazz" to "os".
1224 */
print_constructors_impl(ostream & os,const isl_class & clazz)1225 void cpp_generator::print_constructors_impl(ostream &os,
1226 const isl_class &clazz)
1227 {
1228 function_set::const_iterator in;
1229 const function_set constructors = clazz.constructors;
1230
1231 for (in = constructors.begin(); in != constructors.end(); ++in) {
1232 FunctionDecl *cons = *in;
1233
1234 print_method<impl>(os, clazz, cons, function_kind_constructor);
1235 }
1236 }
1237
1238 /* Print implementation of copy assignment operator for class "clazz" to "os".
1239 *
1240 * If the class has any persistent callbacks, then copy them
1241 * from the original object.
1242 */
print_copy_assignment_impl(ostream & os,const isl_class & clazz)1243 void cpp_generator::print_copy_assignment_impl(ostream &os,
1244 const isl_class &clazz)
1245 {
1246 const char *name = clazz.name.c_str();
1247 std::string cppstring = type2cpp(clazz);
1248 const char *cppname = cppstring.c_str();
1249
1250 osprintf(os, "\n");
1251 osprintf(os, "%s &%s::operator=(%s obj) {\n", cppname,
1252 cppname, cppname);
1253 osprintf(os, " std::swap(this->ptr, obj.ptr);\n", name);
1254 if (clazz.has_persistent_callbacks())
1255 osprintf(os, " copy_callbacks(obj);\n");
1256 osprintf(os, " return *this;\n");
1257 osprintf(os, "}\n");
1258 }
1259
1260 /* Print implementation of destructor for class "clazz" to "os".
1261 *
1262 * No explicit destructor is needed for type based subclasses.
1263 */
print_destructor_impl(ostream & os,const isl_class & clazz)1264 void cpp_generator::print_destructor_impl(ostream &os,
1265 const isl_class &clazz)
1266 {
1267 const char *name = clazz.name.c_str();
1268 std::string cppstring = type2cpp(clazz);
1269 const char *cppname = cppstring.c_str();
1270
1271 if (clazz.is_type_subclass())
1272 return;
1273
1274 osprintf(os, "\n");
1275 osprintf(os, "%s::~%s() {\n", cppname, cppname);
1276 osprintf(os, " if (ptr)\n");
1277 osprintf(os, " %s_free(ptr);\n", name);
1278 osprintf(os, "}\n");
1279 }
1280
1281 /* Print a check that the persistent callback corresponding to "fd"
1282 * is not set, throwing an exception (or printing an error message
1283 * and returning nullptr) if it is set.
1284 */
print_check_no_persistent_callback(ostream & os,const isl_class & clazz,FunctionDecl * fd)1285 void cpp_generator::print_check_no_persistent_callback(ostream &os,
1286 const isl_class &clazz, FunctionDecl *fd)
1287 {
1288 string callback_name = clazz.persistent_callback_name(fd);
1289
1290 osprintf(os, " if (%s_data)\n", callback_name.c_str());
1291 print_invalid(os, 4, "cannot release object with persistent callbacks",
1292 "return nullptr");
1293 }
1294
1295 /* Print implementation of ptr() functions for class "clazz" to "os".
1296 * Since type based subclasses share the pointer with their superclass,
1297 * they can also reuse these functions from the superclass.
1298 *
1299 * If an object has persistent callbacks set, then the underlying
1300 * C object pointer cannot be released because it references data
1301 * in the C++ object.
1302 */
print_ptr_impl(ostream & os,const isl_class & clazz)1303 void cpp_generator::print_ptr_impl(ostream &os, const isl_class &clazz)
1304 {
1305 const char *name = clazz.name.c_str();
1306 std::string cppstring = type2cpp(clazz);
1307 const char *cppname = cppstring.c_str();
1308 set<FunctionDecl *>::const_iterator in;
1309 const set<FunctionDecl *> &callbacks = clazz.persistent_callbacks;
1310
1311 if (clazz.is_type_subclass())
1312 return;
1313
1314 osprintf(os, "\n");
1315 osprintf(os, "__isl_give %s *%s::copy() const & {\n", name, cppname);
1316 osprintf(os, " return %s_copy(ptr);\n", name);
1317 osprintf(os, "}\n\n");
1318 osprintf(os, "__isl_keep %s *%s::get() const {\n", name, cppname);
1319 osprintf(os, " return ptr;\n");
1320 osprintf(os, "}\n\n");
1321 osprintf(os, "__isl_give %s *%s::release() {\n", name, cppname);
1322 for (in = callbacks.begin(); in != callbacks.end(); ++in)
1323 print_check_no_persistent_callback(os, clazz, *in);
1324 osprintf(os, " %s *tmp = ptr;\n", name);
1325 osprintf(os, " ptr = nullptr;\n");
1326 osprintf(os, " return tmp;\n");
1327 osprintf(os, "}\n\n");
1328 osprintf(os, "bool %s::is_null() const {\n", cppname);
1329 osprintf(os, " return ptr == nullptr;\n");
1330 osprintf(os, "}\n");
1331 }
1332
1333 /* Print implementations for the "as" and "isa" methods, if "clazz"
1334 * is a superclass with a type function.
1335 *
1336 * "isa" checks whether an object is of a given subclass type.
1337 * "isa_type" does the same, but gets passed the value of the type field
1338 * of the subclass as a function argument and the type of this field
1339 * as a template argument.
1340 * "as" casts an object to a given subclass type, erroring out
1341 * if the object is not of the given type.
1342 *
1343 * If the input is an invalid object, then these methods raise
1344 * an exception.
1345 * If checked bindings are being generated,
1346 * then an invalid boolean or object is returned instead.
1347 */
print_downcast_impl(ostream & os,const isl_class & clazz)1348 void cpp_generator::print_downcast_impl(ostream &os, const isl_class &clazz)
1349 {
1350 std::string cppstring = type2cpp(clazz);
1351 const char *cppname = cppstring.c_str();
1352
1353 if (!clazz.fn_type)
1354 return;
1355
1356 osprintf(os, "\n");
1357 osprintf(os, "template <typename T, typename>\n");
1358 osprintf(os, "%s %s::isa_type(T subtype) const\n",
1359 isl_bool2cpp().c_str(), cppname);
1360 osprintf(os, "{\n");
1361 osprintf(os, " if (is_null())\n");
1362 if (checked)
1363 osprintf(os, " return boolean();\n");
1364 else
1365 print_throw_NULL_input(os);
1366 osprintf(os, " return %s(get()) == subtype;\n",
1367 clazz.fn_type->getNameAsString().c_str());
1368 osprintf(os, "}\n");
1369
1370 osprintf(os, "template <class T>\n");
1371 osprintf(os, "%s %s::isa() const\n", isl_bool2cpp().c_str(), cppname);
1372 osprintf(os, "{\n");
1373 osprintf(os, " return isa_type<decltype(T::type)>(T::type);\n");
1374 osprintf(os, "}\n");
1375
1376 osprintf(os, "template <class T>\n");
1377 osprintf(os, "T %s::as() const\n", cppname);
1378 osprintf(os, "{\n");
1379 if (checked)
1380 osprintf(os, " if (isa<T>().is_false())\n");
1381 else
1382 osprintf(os, " if (!isa<T>())\n");
1383 print_invalid(os, 4, "not an object of the requested subtype",
1384 "return T()");
1385 osprintf(os, " return T(copy());\n");
1386 osprintf(os, "}\n");
1387 }
1388
1389 /* Print the implementation of the ctx method.
1390 */
print_ctx_impl(ostream & os,const isl_class & clazz)1391 void cpp_generator::print_ctx_impl(ostream &os, const isl_class &clazz)
1392 {
1393 const char *name = clazz.name.c_str();
1394 std::string cppstring = type2cpp(clazz);
1395 const char *cppname = cppstring.c_str();
1396 std::string ns = isl_namespace();
1397
1398 osprintf(os, "\n");
1399 osprintf(os, "%sctx %s::ctx() const {\n", ns.c_str(), cppname);
1400 osprintf(os, " return %sctx(%s_get_ctx(ptr));\n", ns.c_str(), name);
1401 osprintf(os, "}\n");
1402 }
1403
1404 /* Print the implementations of the methods needed for the persistent callbacks
1405 * of "clazz".
1406 */
print_persistent_callbacks_impl(ostream & os,const isl_class & clazz)1407 void cpp_generator::print_persistent_callbacks_impl(ostream &os,
1408 const isl_class &clazz)
1409 {
1410 std::string cppstring = type2cpp(clazz);
1411 const char *cppname = cppstring.c_str();
1412 string classname = type2cpp(clazz);
1413 set<FunctionDecl *>::const_iterator in;
1414 const set<FunctionDecl *> &callbacks = clazz.persistent_callbacks;
1415
1416 if (!clazz.has_persistent_callbacks())
1417 return;
1418
1419 osprintf(os, "\n");
1420 osprintf(os, "%s &%s::copy_callbacks(const %s &obj)\n",
1421 cppname, classname.c_str(), cppname);
1422 osprintf(os, "{\n");
1423 for (in = callbacks.begin(); in != callbacks.end(); ++in) {
1424 string callback_name = clazz.persistent_callback_name(*in);
1425
1426 osprintf(os, " %s_data = obj.%s_data;\n",
1427 callback_name.c_str(), callback_name.c_str());
1428 }
1429 osprintf(os, " return *this;\n");
1430 osprintf(os, "}\n");
1431
1432 for (in = callbacks.begin(); in != callbacks.end(); ++in) {
1433 function_kind kind = function_kind_member_method;
1434
1435 print_set_persistent_callback(os, clazz, *in, kind);
1436 }
1437 }
1438
1439 /* Print definitions for methods of class "clazz" to "os".
1440 */
print_methods_impl(ostream & os,const isl_class & clazz)1441 void cpp_generator::print_methods_impl(ostream &os, const isl_class &clazz)
1442 {
1443 map<string, function_set>::const_iterator it;
1444
1445 for (it = clazz.methods.begin(); it != clazz.methods.end(); ++it)
1446 print_method_group_impl(os, clazz, it->second);
1447 }
1448
1449 /* Print the definition for a method "method_name" in "clazz" derived
1450 * from "fd", which sets an enum, to "os".
1451 * In particular, the method "method_name" sets the enum to "enum_name".
1452 *
1453 * The last argument of the C function does not appear in the method call,
1454 * but is fixed to "enum_name" instead.
1455 * Other than that, the method printed here is similar to one
1456 * printed by cpp_generator::print_method_impl, except that
1457 * some of the special cases do not occur.
1458 */
print_set_enum_impl(ostream & os,const isl_class & clazz,FunctionDecl * fd,const string & enum_name,const string & method_name)1459 void cpp_generator::print_set_enum_impl(ostream &os, const isl_class &clazz,
1460 FunctionDecl *fd, const string &enum_name, const string &method_name)
1461 {
1462 string c_name = fd->getName().str();
1463 int n = fd->getNumParams();
1464 function_kind kind = function_kind_member_method;
1465
1466 osprintf(os, "\n");
1467 print_method_header(os, clazz, fd, method_name, n - 1, false, kind);
1468 osprintf(os, "{\n");
1469
1470 print_argument_validity_check(os, fd, kind);
1471 print_save_ctx(os, fd, kind);
1472 print_on_error_continue(os);
1473
1474 osprintf(os, " auto res = %s(", c_name.c_str());
1475
1476 for (int i = 0; i < n - 1; ++i) {
1477 ParmVarDecl *param = fd->getParamDecl(i);
1478
1479 if (i > 0)
1480 osprintf(os, ", ");
1481 print_method_param_use(os, param, i == 0);
1482 }
1483 osprintf(os, ", %s", enum_name.c_str());
1484 osprintf(os, ");\n");
1485
1486 print_exceptional_execution_check(os, clazz, fd, kind);
1487 print_method_return(os, clazz, fd);
1488
1489 osprintf(os, "}\n");
1490 }
1491
1492 /* Print definitions for the methods in "clazz" derived from "fd",
1493 * which sets an enum, to "os".
1494 *
1495 * A method is generated for each value in the enum, setting
1496 * the enum to that value.
1497 */
print_set_enums_impl(ostream & os,const isl_class & clazz,FunctionDecl * fd)1498 void cpp_generator::print_set_enums_impl(ostream &os, const isl_class &clazz,
1499 FunctionDecl *fd)
1500 {
1501 vector<set_enum>::const_iterator it;
1502 const vector<set_enum> &set_enums = clazz.set_enums.at(fd);
1503
1504 for (it = set_enums.begin(); it != set_enums.end(); ++it) {
1505 print_set_enum_impl(os, clazz, fd, it->name, it->method_name);
1506 }
1507 }
1508
1509 /* Print definitions for methods in "clazz" derived from functions
1510 * that set an enum, to "os".
1511 */
print_set_enums_impl(ostream & os,const isl_class & clazz)1512 void cpp_generator::print_set_enums_impl(ostream &os, const isl_class &clazz)
1513 {
1514 map<FunctionDecl *, vector<set_enum> >::const_iterator it;
1515
1516 for (it = clazz.set_enums.begin(); it != clazz.set_enums.end(); ++it)
1517 print_set_enums_impl(os, clazz, it->first);
1518 }
1519
1520 /* Print a definition for the "get" method "fd" in class "clazz",
1521 * using a name that includes the "get_" prefix, to "os".
1522 *
1523 * This definition simply calls the variant without the "get_" prefix and
1524 * returns its result.
1525 * Note that static methods are not considered to be "get" methods.
1526 */
1527 template<>
print_get_method(ostream & os,const isl_class & clazz,FunctionDecl * fd)1528 void cpp_generator::print_get_method<cpp_generator::impl>(ostream &os,
1529 const isl_class &clazz, FunctionDecl *fd)
1530 {
1531 string get_name = clazz.base_method_name(fd);
1532 string name = clazz.method_name(fd);
1533 function_kind kind = function_kind_member_method;
1534 int num_params = fd->getNumParams();
1535
1536 osprintf(os, "\n");
1537 print_named_method_header(os, clazz, fd, get_name, false, kind);
1538 osprintf(os, "{\n");
1539 osprintf(os, " return %s(", name.c_str());
1540 for (int i = 1; i < num_params; ++i) {
1541 ParmVarDecl *param = fd->getParamDecl(i);
1542
1543 if (i != 1)
1544 osprintf(os, ", ");
1545 osprintf(os, "%s", param->getName().str().c_str());
1546 }
1547 osprintf(os, ");\n");
1548 osprintf(os, "}\n");
1549 }
1550
1551 /* Print definitions for methods "methods" in class "clazz" to "os".
1552 */
print_method_group_impl(ostream & os,const isl_class & clazz,const function_set & methods)1553 void cpp_generator::print_method_group_impl(ostream &os, const isl_class &clazz,
1554 const function_set &methods)
1555 {
1556 function_set::const_iterator it;
1557
1558 for (it = methods.begin(); it != methods.end(); ++it)
1559 print_method_variants<impl>(os, clazz, *it);
1560 }
1561
1562 /* Print the use of "param" to "os".
1563 *
1564 * "load_from_this_ptr" specifies whether the parameter should be loaded from
1565 * the this-ptr. In case a value is loaded from a this pointer, the original
1566 * value must be preserved and must consequently be copied. Values that are
1567 * loaded from parameters do not need to be preserved, as such values will
1568 * already be copies of the actual parameters. It is consequently possible
1569 * to directly take the pointer from these values, which saves
1570 * an unnecessary copy.
1571 *
1572 * In case the parameter is a callback function, two parameters get printed,
1573 * a wrapper for the callback function and a pointer to the actual
1574 * callback function. The wrapper is expected to be available
1575 * in a previously declared variable <name>_lambda, while
1576 * the actual callback function is expected to be stored
1577 * in a structure called <name>_data.
1578 * The caller of this function must ensure that these variables exist.
1579 */
print_method_param_use(ostream & os,ParmVarDecl * param,bool load_from_this_ptr)1580 void cpp_generator::print_method_param_use(ostream &os, ParmVarDecl *param,
1581 bool load_from_this_ptr)
1582 {
1583 string name = param->getName().str();
1584 const char *name_str = name.c_str();
1585 QualType type = param->getOriginalType();
1586
1587 if (type->isIntegerType()) {
1588 osprintf(os, "%s", name_str);
1589 return;
1590 }
1591
1592 if (is_string(type)) {
1593 osprintf(os, "%s.c_str()", name_str);
1594 return;
1595 }
1596
1597 if (is_callback(type)) {
1598 osprintf(os, "%s_lambda, ", name_str);
1599 osprintf(os, "&%s_data", name_str);
1600 return;
1601 }
1602
1603 if (!load_from_this_ptr)
1604 osprintf(os, "%s.", name_str);
1605
1606 if (keeps(param)) {
1607 osprintf(os, "get()");
1608 } else {
1609 if (load_from_this_ptr)
1610 osprintf(os, "copy()");
1611 else
1612 osprintf(os, "release()");
1613 }
1614 }
1615
1616 /* Print code that checks that all isl object arguments to "method" are valid
1617 * (not NULL) and throws an exception if they are not.
1618 * "kind" specifies the kind of method that is being generated.
1619 *
1620 * If checked bindings are being generated,
1621 * then no such check is performed.
1622 */
print_argument_validity_check(ostream & os,FunctionDecl * method,function_kind kind)1623 void cpp_generator::print_argument_validity_check(ostream &os,
1624 FunctionDecl *method, function_kind kind)
1625 {
1626 int n;
1627 bool first = true;
1628
1629 if (checked)
1630 return;
1631
1632 n = method->getNumParams();
1633 for (int i = 0; i < n; ++i) {
1634 bool is_this;
1635 ParmVarDecl *param = method->getParamDecl(i);
1636 string name = param->getName().str();
1637 const char *name_str = name.c_str();
1638 QualType type = param->getOriginalType();
1639
1640 is_this = i == 0 && kind == function_kind_member_method;
1641 if (!is_this && (is_isl_ctx(type) || !is_isl_type(type)))
1642 continue;
1643
1644 if (first)
1645 osprintf(os, " if (");
1646 else
1647 osprintf(os, " || ");
1648
1649 if (is_this)
1650 osprintf(os, "!ptr");
1651 else
1652 osprintf(os, "%s.is_null()", name_str);
1653
1654 first = false;
1655 }
1656 if (first)
1657 return;
1658 osprintf(os, ")\n");
1659 print_throw_NULL_input(os);
1660 }
1661
1662 /* Print code for saving a copy of the isl::ctx available at the start
1663 * of the method "method" in a "saved_ctx" variable,
1664 * for use in exception handling.
1665 * "kind" specifies what kind of method "method" is.
1666 *
1667 * If checked bindings are being generated,
1668 * then the "saved_ctx" variable is not needed.
1669 * If "method" is a member function, then obtain the isl_ctx from
1670 * the "this" object.
1671 * If the first argument of the method is an isl::ctx, then use that one.
1672 * Otherwise, save a copy of the isl::ctx associated to the first argument
1673 * of isl object type.
1674 */
print_save_ctx(ostream & os,FunctionDecl * method,function_kind kind)1675 void cpp_generator::print_save_ctx(ostream &os, FunctionDecl *method,
1676 function_kind kind)
1677 {
1678 int n;
1679 ParmVarDecl *param = method->getParamDecl(0);
1680 QualType type = param->getOriginalType();
1681
1682 if (checked)
1683 return;
1684 if (kind == function_kind_member_method) {
1685 osprintf(os, " auto saved_ctx = ctx();\n");
1686 return;
1687 }
1688 if (is_isl_ctx(type)) {
1689 const char *name;
1690
1691 name = param->getName().str().c_str();
1692 osprintf(os, " auto saved_ctx = %s;\n", name);
1693 return;
1694 }
1695 n = method->getNumParams();
1696 for (int i = 0; i < n; ++i) {
1697 ParmVarDecl *param = method->getParamDecl(i);
1698 QualType type = param->getOriginalType();
1699
1700 if (!is_isl_type(type))
1701 continue;
1702 osprintf(os, " auto saved_ctx = %s.ctx();\n",
1703 param->getName().str().c_str());
1704 return;
1705 }
1706 }
1707
1708 /* Print code to make isl not print an error message when an error occurs
1709 * within the current scope (if exceptions are available),
1710 * since the error message will be included in the exception.
1711 * If exceptions are not available, then exception::on_error
1712 * is set to ISL_ON_ERROR_ABORT and isl is therefore made to abort instead.
1713 *
1714 * If checked bindings are being generated,
1715 * then leave it to the user to decide what isl should do on error.
1716 * Otherwise, assume that a valid isl::ctx is available
1717 * in the "saved_ctx" variable,
1718 * e.g., through a prior call to print_save_ctx.
1719 */
print_on_error_continue(ostream & os)1720 void cpp_generator::print_on_error_continue(ostream &os)
1721 {
1722 if (checked)
1723 return;
1724 osprintf(os, " options_scoped_set_on_error saved_on_error(saved_ctx, "
1725 "exception::on_error);\n");
1726 }
1727
1728 /* Print code to "os" that checks whether any of the persistent callbacks
1729 * of "clazz" is set and if it failed with an exception. If so, the "eptr"
1730 * in the corresponding data structure contains the exception
1731 * that was caught and that needs to be rethrown.
1732 * This field is cleared because the callback and its data may get reused.
1733 *
1734 * The check only needs to be generated for member methods since
1735 * an object is needed for any of the persistent callbacks to be set.
1736 */
print_persistent_callback_exceptional_execution_check(ostream & os,const isl_class & clazz,cpp_generator::function_kind kind)1737 static void print_persistent_callback_exceptional_execution_check(ostream &os,
1738 const isl_class &clazz, cpp_generator::function_kind kind)
1739 {
1740 const set<FunctionDecl *> &callbacks = clazz.persistent_callbacks;
1741 set<FunctionDecl *>::const_iterator in;
1742
1743 if (kind != cpp_generator::function_kind_member_method)
1744 return;
1745
1746 for (in = callbacks.begin(); in != callbacks.end(); ++in) {
1747 string callback_name = clazz.persistent_callback_name(*in);
1748
1749 osprintf(os, " if (%s_data && %s_data->eptr) {\n",
1750 callback_name.c_str(), callback_name.c_str());
1751 osprintf(os, " std::exception_ptr eptr = %s_data->eptr;\n",
1752 callback_name.c_str());
1753 osprintf(os, " %s_data->eptr = nullptr;\n",
1754 callback_name.c_str());
1755 osprintf(os, " std::rethrow_exception(eptr);\n");
1756 osprintf(os, " }\n");
1757 }
1758 }
1759
1760 /* Print code that checks whether the execution of the core of "method"
1761 * of class "clazz" was successful.
1762 * "kind" specifies what kind of method "method" is.
1763 *
1764 * If checked bindings are being generated,
1765 * then no checks are performed.
1766 *
1767 * Otherwise, first check if any of the callbacks failed with
1768 * an exception. If so, the "eptr" in the corresponding data structure
1769 * contains the exception that was caught and that needs to be rethrown.
1770 * Then check if the function call failed in any other way and throw
1771 * the appropriate exception.
1772 * In particular, if the return type is isl_stat, isl_bool or isl_size,
1773 * then a negative value indicates a failure. If the return type
1774 * is an isl type, then a NULL value indicates a failure.
1775 * Assume print_save_ctx has made sure that a valid isl::ctx
1776 * is available in the "ctx" variable.
1777 */
print_exceptional_execution_check(ostream & os,const isl_class & clazz,FunctionDecl * method,function_kind kind)1778 void cpp_generator::print_exceptional_execution_check(ostream &os,
1779 const isl_class &clazz, FunctionDecl *method, function_kind kind)
1780 {
1781 int n;
1782 bool check_null, check_neg;
1783 QualType return_type = method->getReturnType();
1784
1785 if (checked)
1786 return;
1787
1788 print_persistent_callback_exceptional_execution_check(os, clazz, kind);
1789
1790 n = method->getNumParams();
1791 for (int i = 0; i < n; ++i) {
1792 ParmVarDecl *param = method->getParamDecl(i);
1793 const char *name;
1794
1795 if (!is_callback(param->getOriginalType()))
1796 continue;
1797 name = param->getName().str().c_str();
1798 osprintf(os, " if (%s_data.eptr)\n", name);
1799 osprintf(os, " std::rethrow_exception(%s_data.eptr);\n",
1800 name);
1801 }
1802
1803 check_neg = is_isl_neg_error(return_type);
1804 check_null = is_isl_type(return_type);
1805 if (!check_null && !check_neg)
1806 return;
1807
1808 if (check_neg)
1809 osprintf(os, " if (res < 0)\n");
1810 else
1811 osprintf(os, " if (!res)\n");
1812 print_throw_last_error(os);
1813 }
1814
1815 /* Does "fd" modify an object of a subclass based on a type function?
1816 */
is_subclass_mutator(const isl_class & clazz,FunctionDecl * fd)1817 static bool is_subclass_mutator(const isl_class &clazz, FunctionDecl *fd)
1818 {
1819 return clazz.is_type_subclass() && generator::is_mutator(clazz, fd);
1820 }
1821
1822 /* Return the C++ return type of the method corresponding to "fd" in "clazz".
1823 *
1824 * If "fd" modifies an object of a subclass, then return
1825 * the type of this subclass.
1826 * Otherwise, return the C++ counterpart of the actual return type.
1827 */
get_return_type(const isl_class & clazz,FunctionDecl * fd)1828 std::string cpp_generator::get_return_type(const isl_class &clazz,
1829 FunctionDecl *fd)
1830 {
1831 if (is_subclass_mutator(clazz, fd))
1832 return type2cpp(clazz);
1833 else
1834 return type2cpp(fd->getReturnType());
1835 }
1836
1837 /* Given a function "method" for setting a "clazz" persistent callback,
1838 * print the implementations of the methods needed for that callback.
1839 *
1840 * In particular, print
1841 * - the implementation of a static inline method
1842 * for use as the C callback function
1843 * - the definition of a private method for setting the callback function
1844 * - the public method for constructing a new object with the callback set.
1845 */
print_set_persistent_callback(ostream & os,const isl_class & clazz,FunctionDecl * method,function_kind kind)1846 void cpp_generator::print_set_persistent_callback(ostream &os,
1847 const isl_class &clazz, FunctionDecl *method,
1848 function_kind kind)
1849 {
1850 string fullname = method->getName().str();
1851 ParmVarDecl *param = persistent_callback_arg(method);
1852 string classname = type2cpp(clazz);
1853 string pname;
1854 string callback_name = clazz.persistent_callback_name(method);
1855
1856 osprintf(os, "\n");
1857 print_persistent_callback_prototype(os, clazz, method, false);
1858 osprintf(os, "\n");
1859 osprintf(os, "{\n");
1860 print_callback_body(os, 2, param, callback_name);
1861 osprintf(os, "}\n\n");
1862
1863 pname = param->getName().str();
1864 print_persistent_callback_setter_prototype(os, clazz, method, false);
1865 osprintf(os, "\n");
1866 osprintf(os, "{\n");
1867 print_check_ptr_start(os, clazz, "ptr");
1868 osprintf(os, " %s_data = std::make_shared<struct %s_data>();\n",
1869 callback_name.c_str(), callback_name.c_str());
1870 osprintf(os, " %s_data->func = %s;\n",
1871 callback_name.c_str(), pname.c_str());
1872 osprintf(os, " ptr = %s(ptr, &%s, %s_data.get());\n",
1873 fullname.c_str(), callback_name.c_str(), callback_name.c_str());
1874 print_check_ptr_end(os, "ptr");
1875 osprintf(os, "}\n\n");
1876
1877 print_method_header(os, clazz, method, false, kind);
1878 osprintf(os, "{\n");
1879 osprintf(os, " auto copy = *this;\n");
1880 osprintf(os, " copy.set_%s_data(%s);\n",
1881 callback_name.c_str(), pname.c_str());
1882 osprintf(os, " return copy;\n");
1883 osprintf(os, "}\n");
1884 }
1885
1886 /* Print the return statement of the C++ method corresponding
1887 * to the C function "method" in class "clazz" to "os".
1888 *
1889 * The result of the isl function is returned as a new
1890 * object if the underlying isl function returns an isl_* ptr, as a bool
1891 * if the isl function returns an isl_bool, as void if the isl functions
1892 * returns an isl_stat,
1893 * as std::string if the isl function returns 'const char *', and as
1894 * unmodified return value otherwise.
1895 * If checked C++ bindings are being generated,
1896 * then an isl_bool return type is transformed into a boolean and
1897 * an isl_stat into a stat since no exceptions can be generated
1898 * on negative results from the isl function.
1899 * If the method returns a new instance of the same object type and
1900 * if the class has any persistent callbacks, then the data
1901 * for these callbacks are copied from the original to the new object.
1902 * If "clazz" is a subclass that is based on a type function and
1903 * if the return type corresponds to the superclass data type,
1904 * then it is replaced by the subclass data type.
1905 */
print_method_return(ostream & os,const isl_class & clazz,FunctionDecl * method)1906 void cpp_generator::print_method_return(ostream &os, const isl_class &clazz,
1907 FunctionDecl *method)
1908 {
1909 QualType return_type = method->getReturnType();
1910 string rettype_str = get_return_type(clazz, method);
1911 bool returns_super = is_subclass_mutator(clazz, method);
1912
1913 if (is_isl_type(return_type) ||
1914 (checked && is_isl_neg_error(return_type))) {
1915 osprintf(os, " return manage(res)");
1916 if (is_mutator(clazz, method) &&
1917 clazz.has_persistent_callbacks())
1918 osprintf(os, ".copy_callbacks(*this)");
1919 if (returns_super)
1920 osprintf(os, ".as<%s>()", rettype_str.c_str());
1921 osprintf(os, ";\n");
1922 } else if (is_isl_stat(return_type)) {
1923 osprintf(os, " return;\n");
1924 } else if (is_string(return_type)) {
1925 osprintf(os, " std::string tmp(res);\n");
1926 if (gives(method))
1927 osprintf(os, " free(res);\n");
1928 osprintf(os, " return tmp;\n");
1929 } else {
1930 osprintf(os, " return res;\n");
1931 }
1932 }
1933
1934 /* Return the formal parameter at position "pos" of "fd".
1935 * However, if this parameter should be converted, as indicated
1936 * by "convert", then return the second formal parameter
1937 * of the conversion function instead.
1938 *
1939 * If "convert" is empty, then it is assumed that
1940 * none of the arguments should be converted.
1941 */
get_param(FunctionDecl * fd,int pos,const std::vector<bool> & convert)1942 ParmVarDecl *cpp_generator::get_param(FunctionDecl *fd, int pos,
1943 const std::vector<bool> &convert)
1944 {
1945 ParmVarDecl *param = fd->getParamDecl(pos);
1946
1947 if (convert.size() == 0)
1948 return param;
1949 if (!convert[pos])
1950 return param;
1951 return conversions[param->getOriginalType().getTypePtr()];
1952 }
1953
1954 /* Print the header for "method" in class "clazz", with name "cname" and
1955 * "num_params" number of arguments, to "os".
1956 *
1957 * Print the header of a declaration if "is_declaration" is set, otherwise print
1958 * the header of a method definition.
1959 *
1960 * "kind" specifies the kind of method that should be generated.
1961 *
1962 * "convert" specifies which of the method arguments should
1963 * be automatically converted.
1964 *
1965 * This function prints headers for member methods, static methods, and
1966 * constructors, either for their declaration or definition.
1967 *
1968 * Member functions are declared as "const", as they do not change the current
1969 * object, but instead create a new object. They always retrieve the first
1970 * parameter of the original isl function from the this-pointer of the object,
1971 * such that only starting at the second parameter the parameters of the
1972 * original function become part of the method's interface.
1973 *
1974 * A function
1975 *
1976 * __isl_give isl_set *isl_set_intersect(__isl_take isl_set *s1,
1977 * __isl_take isl_set *s2);
1978 *
1979 * is translated into:
1980 *
1981 * inline set intersect(set set2) const;
1982 *
1983 * For static functions and constructors all parameters of the original isl
1984 * function are exposed.
1985 *
1986 * Parameters that are defined as __isl_keep, are of type string or
1987 * are callbacks, are passed
1988 * as const reference, which allows the compiler to optimize the parameter
1989 * transfer.
1990 *
1991 * Constructors are marked as explicit using the C++ keyword 'explicit' or as
1992 * implicit using a comment in place of the explicit keyword. By annotating
1993 * implicit constructors with a comment, users of the interface are made
1994 * aware of the potential danger that implicit construction is possible
1995 * for these constructors, whereas without a comment not every user would
1996 * know that implicit construction is allowed in absence of an explicit keyword.
1997 *
1998 * If any of the arguments needs to be converted, then the argument
1999 * of the method is changed to that of the source of the conversion.
2000 * The name of the argument is, however, derived from the original
2001 * function argument.
2002 */
print_method_header(ostream & os,const isl_class & clazz,FunctionDecl * method,const string & cname,int num_params,bool is_declaration,function_kind kind,const std::vector<bool> & convert)2003 void cpp_generator::print_method_header(ostream &os, const isl_class &clazz,
2004 FunctionDecl *method, const string &cname, int num_params,
2005 bool is_declaration, function_kind kind,
2006 const std::vector<bool> &convert)
2007 {
2008 string rettype_str = get_return_type(clazz, method);
2009 string classname = type2cpp(clazz);
2010 int first_param = 0;
2011
2012 if (kind == function_kind_member_method)
2013 first_param = 1;
2014
2015 if (is_declaration) {
2016 osprintf(os, " ");
2017
2018 if (kind == function_kind_static_method)
2019 osprintf(os, "static ");
2020
2021 osprintf(os, "inline ");
2022
2023 if (kind == function_kind_constructor) {
2024 if (is_implicit_conversion(clazz, method))
2025 osprintf(os, "/* implicit */ ");
2026 else
2027 osprintf(os, "explicit ");
2028 }
2029 }
2030
2031 if (kind != function_kind_constructor)
2032 osprintf(os, "%s ", rettype_str.c_str());
2033
2034 if (!is_declaration)
2035 osprintf(os, "%s::", classname.c_str());
2036
2037 if (kind != function_kind_constructor)
2038 osprintf(os, "%s", cname.c_str());
2039 else
2040 osprintf(os, "%s", classname.c_str());
2041
2042 osprintf(os, "(");
2043
2044 for (int i = first_param; i < num_params; ++i) {
2045 std::string name = method->getParamDecl(i)->getName().str();
2046 ParmVarDecl *param = get_param(method, i, convert);
2047 QualType type = param->getOriginalType();
2048 string cpptype = type2cpp(type);
2049
2050 if (is_callback(type))
2051 num_params--;
2052
2053 if (keeps(param) || is_string(type) || is_callback(type))
2054 osprintf(os, "const %s &%s", cpptype.c_str(),
2055 name.c_str());
2056 else
2057 osprintf(os, "%s %s", cpptype.c_str(), name.c_str());
2058
2059 if (i != num_params - 1)
2060 osprintf(os, ", ");
2061 }
2062
2063 osprintf(os, ")");
2064
2065 if (kind == function_kind_member_method)
2066 osprintf(os, " const");
2067
2068 if (is_declaration)
2069 osprintf(os, ";");
2070 osprintf(os, "\n");
2071 }
2072
2073 /* Print the header for a method called "name" in class "clazz"
2074 * derived from "method" to "os".
2075 *
2076 * Print the header of a declaration if "is_declaration" is set, otherwise print
2077 * the header of a method definition.
2078 *
2079 * "kind" specifies the kind of method that should be generated.
2080 *
2081 * "convert" specifies which of the method arguments should
2082 * be automatically converted.
2083 */
print_named_method_header(ostream & os,const isl_class & clazz,FunctionDecl * method,string name,bool is_declaration,function_kind kind,const std::vector<bool> & convert)2084 void cpp_generator::print_named_method_header(ostream &os,
2085 const isl_class &clazz, FunctionDecl *method, string name,
2086 bool is_declaration, function_kind kind,
2087 const std::vector<bool> &convert)
2088 {
2089 int num_params = method->getNumParams();
2090
2091 name = rename_method(name);
2092 print_method_header(os, clazz, method, name, num_params,
2093 is_declaration, kind, convert);
2094 }
2095
2096 /* Print the header for "method" in class "clazz" to "os"
2097 * using its default name.
2098 *
2099 * Print the header of a declaration if "is_declaration" is set, otherwise print
2100 * the header of a method definition.
2101 *
2102 * "kind" specifies the kind of method that should be generated.
2103 */
print_method_header(ostream & os,const isl_class & clazz,FunctionDecl * method,bool is_declaration,function_kind kind)2104 void cpp_generator::print_method_header(ostream &os, const isl_class &clazz,
2105 FunctionDecl *method, bool is_declaration, function_kind kind)
2106 {
2107 string name = clazz.method_name(method);
2108
2109 print_named_method_header(os, clazz, method, name, is_declaration,
2110 kind);
2111 }
2112
2113 /* Generate the list of argument types for a callback function of
2114 * type "type". If "cpp" is set, then generate the C++ type list, otherwise
2115 * the C type list.
2116 *
2117 * For a callback of type
2118 *
2119 * isl_stat (*)(__isl_take isl_map *map, void *user)
2120 *
2121 * the following C++ argument list is generated:
2122 *
2123 * map
2124 */
generate_callback_args(QualType type,bool cpp)2125 string cpp_generator::generate_callback_args(QualType type, bool cpp)
2126 {
2127 std::string type_str;
2128 const FunctionProtoType *callback;
2129 int num_params;
2130
2131 callback = extract_prototype(type);
2132 num_params = callback->getNumArgs();
2133 if (cpp)
2134 num_params--;
2135
2136 for (long i = 0; i < num_params; i++) {
2137 QualType type = callback->getArgType(i);
2138
2139 if (cpp)
2140 type_str += type2cpp(type);
2141 else
2142 type_str += type.getAsString();
2143
2144 if (!cpp)
2145 type_str += "arg_" + ::to_string(i);
2146
2147 if (i != num_params - 1)
2148 type_str += ", ";
2149 }
2150
2151 return type_str;
2152 }
2153
2154 /* Generate the full cpp type of a callback function of type "type".
2155 *
2156 * For a callback of type
2157 *
2158 * isl_stat (*)(__isl_take isl_map *map, void *user)
2159 *
2160 * the following type is generated:
2161 *
2162 * std::function<stat(map)>
2163 */
generate_callback_type(QualType type)2164 string cpp_generator::generate_callback_type(QualType type)
2165 {
2166 std::string type_str;
2167 const FunctionProtoType *callback = extract_prototype(type);
2168 QualType return_type = callback->getReturnType();
2169 string rettype_str = type2cpp(return_type);
2170
2171 type_str = "std::function<";
2172 type_str += rettype_str;
2173 type_str += "(";
2174 type_str += generate_callback_args(type, true);
2175 type_str += ")>";
2176
2177 return type_str;
2178 }
2179
2180 /* Print the call to the C++ callback function "call",
2181 * with the given indentation, wrapped
2182 * for use inside the lambda function that is used as the C callback function,
2183 * in the case where checked C++ bindings are being generated.
2184 *
2185 * In particular, print
2186 *
2187 * auto ret = @call@;
2188 * return ret.release();
2189 */
print_wrapped_call_checked(ostream & os,int indent,const string & call)2190 void cpp_generator::print_wrapped_call_checked(ostream &os, int indent,
2191 const string &call)
2192 {
2193 osprintf(os, indent, "auto ret = %s;\n", call.c_str());
2194 osprintf(os, indent, "return ret.release();\n");
2195 }
2196
2197 /* Print the call to the C++ callback function "call",
2198 * with the given indentation and with return type "rtype", wrapped
2199 * for use inside the lambda function that is used as the C callback function.
2200 *
2201 * In particular, print
2202 *
2203 * ISL_CPP_TRY {
2204 * @call@;
2205 * return isl_stat_ok;
2206 * } ISL_CPP_CATCH_ALL {
2207 * data->eptr = std::current_exception();
2208 * return isl_stat_error;
2209 * }
2210 * or
2211 * ISL_CPP_TRY {
2212 * auto ret = @call@;
2213 * return ret ? isl_bool_true : isl_bool_false;
2214 * } ISL_CPP_CATCH_ALL {
2215 * data->eptr = std::current_exception();
2216 * return isl_bool_error;
2217 * }
2218 * or
2219 * ISL_CPP_TRY {
2220 * auto ret = @call@;
2221 * return ret.release();
2222 * } ISL_CPP_CATCH_ALL {
2223 * data->eptr = std::current_exception();
2224 * return NULL;
2225 * }
2226 *
2227 * depending on the return type.
2228 *
2229 * where ISL_CPP_TRY is defined to "try" and ISL_CPP_CATCH_ALL to "catch (...)"
2230 * (if exceptions are available).
2231 *
2232 * If checked C++ bindings are being generated, then
2233 * the call is wrapped differently.
2234 */
print_wrapped_call(ostream & os,int indent,const string & call,QualType rtype)2235 void cpp_generator::print_wrapped_call(ostream &os, int indent,
2236 const string &call, QualType rtype)
2237 {
2238 if (checked)
2239 return print_wrapped_call_checked(os, indent, call);
2240
2241 osprintf(os, indent, "ISL_CPP_TRY {\n");
2242 if (is_isl_stat(rtype))
2243 osprintf(os, indent, " %s;\n", call.c_str());
2244 else
2245 osprintf(os, indent, " auto ret = %s;\n", call.c_str());
2246 if (is_isl_stat(rtype))
2247 osprintf(os, indent, " return isl_stat_ok;\n");
2248 else if (is_isl_bool(rtype))
2249 osprintf(os, indent,
2250 " return ret ? isl_bool_true : isl_bool_false;\n");
2251 else
2252 osprintf(os, indent, " return ret.release();\n");
2253 osprintf(os, indent, "} ISL_CPP_CATCH_ALL {\n");
2254 osprintf(os, indent, " data->eptr = std::current_exception();\n");
2255 if (is_isl_stat(rtype))
2256 osprintf(os, indent, " return isl_stat_error;\n");
2257 else if (is_isl_bool(rtype))
2258 osprintf(os, indent, " return isl_bool_error;\n");
2259 else
2260 osprintf(os, indent, " return NULL;\n");
2261 osprintf(os, indent, "}\n");
2262 }
2263
2264 /* Print the declaration for a "prefix"_data data structure
2265 * that can be used for passing to a C callback function
2266 * containing a copy of the C++ callback function "param",
2267 * along with an std::exception_ptr that is used to store any
2268 * exceptions thrown in the C++ callback.
2269 *
2270 * If the C callback is of the form
2271 *
2272 * isl_stat (*fn)(__isl_take isl_map *map, void *user)
2273 *
2274 * then the following declaration is printed:
2275 *
2276 * struct <prefix>_data {
2277 * std::function<stat(map)> func;
2278 * std::exception_ptr eptr;
2279 * }
2280 *
2281 * (without a newline or a semicolon).
2282 *
2283 * The std::exception_ptr object is not added to "prefix"_data
2284 * if checked C++ bindings are being generated.
2285 */
print_callback_data_decl(ostream & os,ParmVarDecl * param,const string & prefix)2286 void cpp_generator::print_callback_data_decl(ostream &os, ParmVarDecl *param,
2287 const string &prefix)
2288 {
2289 string cpp_args;
2290
2291 cpp_args = generate_callback_type(param->getType());
2292
2293 osprintf(os, " struct %s_data {\n", prefix.c_str());
2294 osprintf(os, " %s func;\n", cpp_args.c_str());
2295 if (!checked)
2296 osprintf(os, " std::exception_ptr eptr;\n");
2297 osprintf(os, " }");
2298 }
2299
2300 /* Print the body of C function callback with the given indentation
2301 * that can be use as an argument to "param" for marshalling
2302 * the corresponding C++ callback.
2303 * The data structure that contains the C++ callback is of type
2304 * "prefix"_data.
2305 *
2306 * For a callback of the form
2307 *
2308 * isl_stat (*fn)(__isl_take isl_map *map, void *user)
2309 *
2310 * the following code is generated:
2311 *
2312 * auto *data = static_cast<struct <prefix>_data *>(arg_1);
2313 * ISL_CPP_TRY {
2314 * stat ret = (data->func)(manage(arg_0));
2315 * return isl_stat_ok;
2316 * } ISL_CPP_CATCH_ALL {
2317 * data->eptr = std::current_exception();
2318 * return isl_stat_error;
2319 * }
2320 *
2321 * If checked C++ bindings are being generated, then
2322 * generate the following code:
2323 *
2324 * auto *data = static_cast<struct <prefix>_data *>(arg_1);
2325 * stat ret = (data->func)(manage(arg_0));
2326 * return isl_stat(ret);
2327 */
print_callback_body(ostream & os,int indent,ParmVarDecl * param,const string & prefix)2328 void cpp_generator::print_callback_body(ostream &os, int indent,
2329 ParmVarDecl *param, const string &prefix)
2330 {
2331 QualType ptype, rtype;
2332 string call, last_idx;
2333 const FunctionProtoType *callback;
2334 int num_params;
2335
2336 ptype = param->getType();
2337
2338 callback = extract_prototype(ptype);
2339 rtype = callback->getReturnType();
2340 num_params = callback->getNumArgs();
2341
2342 last_idx = ::to_string(num_params - 1);
2343
2344 call = "(data->func)(";
2345 for (long i = 0; i < num_params - 1; i++) {
2346 if (!callback_takes_argument(param, i))
2347 call += "manage_copy";
2348 else
2349 call += "manage";
2350 call += "(arg_" + ::to_string(i) + ")";
2351 if (i != num_params - 2)
2352 call += ", ";
2353 }
2354 call += ")";
2355
2356 osprintf(os, indent,
2357 "auto *data = static_cast<struct %s_data *>(arg_%s);\n",
2358 prefix.c_str(), last_idx.c_str());
2359 print_wrapped_call(os, indent, call, rtype);
2360 }
2361
2362 /* Print the local variables that are needed for a callback argument,
2363 * in particular, print a lambda function that wraps the callback and
2364 * a pointer to the actual C++ callback function.
2365 *
2366 * For a callback of the form
2367 *
2368 * isl_stat (*fn)(__isl_take isl_map *map, void *user)
2369 *
2370 * the following lambda function is generated:
2371 *
2372 * auto fn_lambda = [](isl_map *arg_0, void *arg_1) -> isl_stat {
2373 * auto *data = static_cast<struct fn_data *>(arg_1);
2374 * try {
2375 * stat ret = (data->func)(manage(arg_0));
2376 * return isl_stat_ok;
2377 * } catch (...) {
2378 * data->eptr = std::current_exception();
2379 * return isl_stat_error;
2380 * }
2381 * };
2382 *
2383 * A copy of the std::function C++ callback function is stored in
2384 * a fn_data data structure for passing to the C callback function,
2385 * along with an std::exception_ptr that is used to store any
2386 * exceptions thrown in the C++ callback.
2387 *
2388 * struct fn_data {
2389 * std::function<stat(map)> func;
2390 * std::exception_ptr eptr;
2391 * } fn_data = { fn };
2392 *
2393 * This std::function object represents the actual user
2394 * callback function together with the locally captured state at the caller.
2395 *
2396 * The lambda function is expected to be used as a C callback function
2397 * where the lambda itself is provided as the function pointer and
2398 * where the user void pointer is a pointer to fn_data.
2399 * The std::function object is extracted from the pointer to fn_data
2400 * inside the lambda function.
2401 *
2402 * The std::exception_ptr object is not added to fn_data
2403 * if checked C++ bindings are being generated.
2404 * The body of the generated lambda function then is as follows:
2405 *
2406 * stat ret = (data->func)(manage(arg_0));
2407 * return isl_stat(ret);
2408 *
2409 * If the C callback does not take its arguments, then
2410 * manage_copy is used instead of manage.
2411 */
print_callback_local(ostream & os,ParmVarDecl * param)2412 void cpp_generator::print_callback_local(ostream &os, ParmVarDecl *param)
2413 {
2414 string pname;
2415 QualType ptype, rtype;
2416 string c_args, cpp_args, rettype;
2417 const FunctionProtoType *callback;
2418
2419 pname = param->getName().str();
2420 ptype = param->getType();
2421
2422 c_args = generate_callback_args(ptype, false);
2423
2424 callback = extract_prototype(ptype);
2425 rtype = callback->getReturnType();
2426 rettype = rtype.getAsString();
2427
2428 print_callback_data_decl(os, param, pname);
2429 osprintf(os, " %s_data = { %s };\n", pname.c_str(), pname.c_str());
2430 osprintf(os, " auto %s_lambda = [](%s) -> %s {\n",
2431 pname.c_str(), c_args.c_str(), rettype.c_str());
2432 print_callback_body(os, 4, param, pname);
2433 osprintf(os, " };\n");
2434 }
2435
2436 /* An array listing functions that must be renamed and the function name they
2437 * should be renamed to. We currently rename functions in case their name would
2438 * match a reserved C++ keyword, which is not allowed in C++.
2439 */
2440 static const char *rename_map[][2] = {
2441 { "union", "unite" },
2442 };
2443
2444 /* Rename method "name" in case the method name in the C++ bindings should not
2445 * match the name in the C bindings. We do this for example to avoid
2446 * C++ keywords.
2447 */
rename_method(std::string name)2448 std::string cpp_generator::rename_method(std::string name)
2449 {
2450 for (size_t i = 0; i < sizeof(rename_map) / sizeof(rename_map[0]); i++)
2451 if (name.compare(rename_map[i][0]) == 0)
2452 return rename_map[i][1];
2453
2454 return name;
2455 }
2456
2457 /* Translate isl class "clazz" to its corresponding C++ type.
2458 * Use the name of the type based subclass, if any.
2459 */
type2cpp(const isl_class & clazz)2460 string cpp_generator::type2cpp(const isl_class &clazz)
2461 {
2462 return type2cpp(clazz.subclass_name);
2463 }
2464
2465 /* Translate type string "type_str" to its C++ name counterpart.
2466 */
type2cpp(string type_str)2467 string cpp_generator::type2cpp(string type_str)
2468 {
2469 return type_str.substr(4);
2470 }
2471
2472 /* Return the C++ counterpart to the isl_bool type.
2473 * If checked C++ bindings are being generated,
2474 * then this is "boolean". Otherwise, it is simply "bool".
2475 */
isl_bool2cpp()2476 string cpp_generator::isl_bool2cpp()
2477 {
2478 return checked ? "boolean" : "bool";
2479 }
2480
2481 /* Return the namespace of the generated C++ bindings.
2482 */
isl_namespace()2483 string cpp_generator::isl_namespace()
2484 {
2485 return checked ? "isl::checked::" : "isl::";
2486 }
2487
2488 /* Translate QualType "type" to its C++ name counterpart.
2489 *
2490 * An isl_bool return type is translated into "bool",
2491 * while an isl_stat is translated into "void" and
2492 * an isl_size is translated to "unsigned".
2493 * The exceptional cases are handled through exceptions.
2494 * If checked C++ bindings are being generated, then
2495 * C++ counterparts of isl_bool, isl_stat and isl_size need to be used instead.
2496 */
type2cpp(QualType type)2497 string cpp_generator::type2cpp(QualType type)
2498 {
2499 if (is_isl_type(type))
2500 return isl_namespace() +
2501 type2cpp(type->getPointeeType().getAsString());
2502
2503 if (is_isl_bool(type))
2504 return isl_bool2cpp();
2505
2506 if (is_isl_stat(type))
2507 return checked ? "stat" : "void";
2508
2509 if (is_isl_size(type))
2510 return checked ? "class size" : "unsigned";
2511
2512 if (type->isIntegerType())
2513 return type.getAsString();
2514
2515 if (is_string(type))
2516 return "std::string";
2517
2518 if (is_callback(type))
2519 return generate_callback_type(type);
2520
2521 die("Cannot convert type to C++ type");
2522 }
2523
2524 /* Check if "subclass_type" is a subclass of "class_type".
2525 */
is_subclass(QualType subclass_type,const isl_class & class_type)2526 bool cpp_generator::is_subclass(QualType subclass_type,
2527 const isl_class &class_type)
2528 {
2529 std::string type_str = subclass_type->getPointeeType().getAsString();
2530 std::vector<std::string> superclasses;
2531 std::vector<const isl_class *> parents;
2532 std::vector<std::string>::iterator ci;
2533
2534 superclasses = generator::find_superclasses(classes[type_str].type);
2535
2536 for (ci = superclasses.begin(); ci < superclasses.end(); ci++)
2537 parents.push_back(&classes[*ci]);
2538
2539 while (!parents.empty()) {
2540 const isl_class *candidate = parents.back();
2541
2542 parents.pop_back();
2543
2544 if (&class_type == candidate)
2545 return true;
2546
2547 superclasses = generator::find_superclasses(candidate->type);
2548
2549 for (ci = superclasses.begin(); ci < superclasses.end(); ci++)
2550 parents.push_back(&classes[*ci]);
2551 }
2552
2553 return false;
2554 }
2555
2556 /* Check if "cons" is an implicit conversion constructor of class "clazz".
2557 *
2558 * An implicit conversion constructor is generated in case "cons" has a single
2559 * parameter, where the parameter type is a subclass of the class that is
2560 * currently being generated.
2561 */
is_implicit_conversion(const isl_class & clazz,FunctionDecl * cons)2562 bool cpp_generator::is_implicit_conversion(const isl_class &clazz,
2563 FunctionDecl *cons)
2564 {
2565 ParmVarDecl *param = cons->getParamDecl(0);
2566 QualType type = param->getOriginalType();
2567
2568 int num_params = cons->getNumParams();
2569 if (num_params != 1)
2570 return false;
2571
2572 if (is_isl_type(type) && !is_isl_ctx(type) && is_subclass(type, clazz))
2573 return true;
2574
2575 return false;
2576 }
2577
2578 /* Get kind of "method" in "clazz".
2579 *
2580 * Given the declaration of a static or member method, returns its kind.
2581 */
get_method_kind(const isl_class & clazz,FunctionDecl * method)2582 cpp_generator::function_kind cpp_generator::get_method_kind(
2583 const isl_class &clazz, FunctionDecl *method)
2584 {
2585 if (is_static(clazz, method))
2586 return function_kind_static_method;
2587 else
2588 return function_kind_member_method;
2589 }
2590