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 TOBIAS GROSSER 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 <memory>
39 #include <sstream>
40 #include <string>
41 #include <vector>
42
43 #include "plain_cpp.h"
44 #include "isl_config.h"
45
46 /* Print string formatted according to "fmt" to ostream "os".
47 *
48 * This osprintf method allows us to use printf style formatting constructs when
49 * writing to an ostream.
50 */
osprintf(ostream & os,const char * format,va_list arguments)51 static void osprintf(ostream &os, const char *format, va_list arguments)
52 {
53 va_list copy;
54 char *string_pointer;
55 size_t size;
56
57 va_copy(copy, arguments);
58 size = vsnprintf(NULL, 0, format, copy);
59 string_pointer = new char[size + 1];
60 va_end(copy);
61 vsnprintf(string_pointer, size + 1, format, arguments);
62 os << string_pointer;
63 delete[] string_pointer;
64 }
65
66 /* Print string formatted according to "fmt" to ostream "os".
67 *
68 * This osprintf method allows us to use printf style formatting constructs when
69 * writing to an ostream.
70 */
osprintf(ostream & os,const char * format,...)71 static void osprintf(ostream &os, const char *format, ...)
72 {
73 va_list arguments;
74
75 va_start(arguments, format);
76 osprintf(os, format, arguments);
77 va_end(arguments);
78 }
79
80 /* Print string formatted according to "fmt" to ostream "os"
81 * with the given indentation.
82 *
83 * This osprintf method allows us to use printf style formatting constructs when
84 * writing to an ostream.
85 */
osprintf(ostream & os,int indent,const char * format,...)86 static void osprintf(ostream &os, int indent, const char *format, ...)
87 {
88 va_list arguments;
89
90 osprintf(os, "%*s", indent, " ");
91 va_start(arguments, format);
92 osprintf(os, format, arguments);
93 va_end(arguments);
94 }
95
96 /* Convert "l" to a string.
97 */
to_string(long l)98 static std::string to_string(long l)
99 {
100 std::ostringstream strm;
101 strm << l;
102 return strm.str();
103 }
104
105 /* Construct a generator for plain C++ bindings.
106 *
107 * "checked" is set if C++ bindings should be generated
108 * that rely on the user to check for error conditions.
109 */
plain_cpp_generator(SourceManager & SM,set<RecordDecl * > & exported_types,set<FunctionDecl * > exported_functions,set<FunctionDecl * > functions,bool checked)110 plain_cpp_generator::plain_cpp_generator(SourceManager &SM,
111 set<RecordDecl *> &exported_types,
112 set<FunctionDecl *> exported_functions, set<FunctionDecl *> functions,
113 bool checked) :
114 cpp_generator(SM, exported_types, exported_functions,
115 functions),
116 checked(checked)
117 {
118 }
119
120 /* Generate a cpp interface based on the extracted types and functions.
121 *
122 * Print first a set of forward declarations for all isl wrapper
123 * classes, then the declarations of the classes, and at the end all
124 * implementations.
125 *
126 * If checked C++ bindings are being generated,
127 * then wrap them in a namespace to avoid conflicts
128 * with the default C++ bindings (with automatic checks using exceptions).
129 */
generate()130 void plain_cpp_generator::generate()
131 {
132 ostream &os = cout;
133
134 osprintf(os, "\n");
135 osprintf(os, "namespace isl {\n\n");
136 if (checked)
137 osprintf(os, "namespace checked {\n\n");
138
139 print_forward_declarations(os);
140 osprintf(os, "\n");
141 print_declarations(os);
142 osprintf(os, "\n");
143 print_implementations(os);
144
145 if (checked)
146 osprintf(os, "} // namespace checked\n");
147 osprintf(os, "} // namespace isl\n");
148 }
149
150 /* Print forward declarations for all classes to "os".
151 */
print_forward_declarations(ostream & os)152 void plain_cpp_generator::print_forward_declarations(ostream &os)
153 {
154 map<string, isl_class>::iterator ci;
155
156 osprintf(os, "// forward declarations\n");
157
158 for (ci = classes.begin(); ci != classes.end(); ++ci)
159 print_class_forward_decl(os, ci->second);
160 }
161
162 /* Print all declarations to "os".
163 */
print_declarations(ostream & os)164 void plain_cpp_generator::print_declarations(ostream &os)
165 {
166 map<string, isl_class>::iterator ci;
167 bool first = true;
168
169 for (ci = classes.begin(); ci != classes.end(); ++ci) {
170 if (first)
171 first = false;
172 else
173 osprintf(os, "\n");
174
175 print_class(os, ci->second);
176 }
177 }
178
179 /* Print all implementations to "os".
180 */
print_implementations(ostream & os)181 void plain_cpp_generator::print_implementations(ostream &os)
182 {
183 map<string, isl_class>::iterator ci;
184 bool first = true;
185
186 for (ci = classes.begin(); ci != classes.end(); ++ci) {
187 if (first)
188 first = false;
189 else
190 osprintf(os, "\n");
191
192 print_class_impl(os, ci->second);
193 }
194 }
195
196 /* If the printed class is a subclass that is based on a type function,
197 * then introduce a "type" field that holds the value of the type
198 * corresponding to the subclass and make the fields of the class
199 * accessible to the "isa" and "as" methods of the (immediate) superclass.
200 * In particular, "isa" needs access to the type field itself,
201 * while "as" needs access to the private constructor.
202 * In case of the "isa" method, all instances are made friends
203 * to avoid access right confusion.
204 */
print_subclass_type()205 void plain_cpp_generator::decl_printer::print_subclass_type()
206 {
207 std::string super;
208 const char *cppname = cppstring.c_str();
209 const char *supername;
210
211 if (!clazz.is_type_subclass())
212 return;
213
214 super = type2cpp(clazz.superclass_name);
215 supername = super.c_str();
216 osprintf(os, " template <class T>\n");
217 osprintf(os, " friend %s %s::isa() const;\n",
218 generator.isl_bool2cpp().c_str(), supername);
219 osprintf(os, " friend %s %s::as<%s>() const;\n",
220 cppname, supername, cppname);
221 osprintf(os, " static const auto type = %s;\n",
222 clazz.subclass_name.c_str());
223 }
224
225 /* Print declarations for class "clazz" to "os".
226 *
227 * If "clazz" is a subclass based on a type function,
228 * then it is made to inherit from the (immediate) superclass and
229 * a "type" attribute is added for use in the "as" and "isa"
230 * methods of the superclass.
231 *
232 * Conversely, if "clazz" is a superclass with a type function,
233 * then declare those "as" and "isa" methods.
234 *
235 * The pointer to the isl object is only added for classes that
236 * are not subclasses, since subclasses refer to the same isl object.
237 */
print_class(ostream & os,const isl_class & clazz)238 void plain_cpp_generator::print_class(ostream &os, const isl_class &clazz)
239 {
240 decl_printer printer(os, clazz, *this);
241 const char *name = clazz.name.c_str();
242 const char *cppname = printer.cppstring.c_str();
243
244 osprintf(os, "// declarations for isl::%s\n", cppname);
245
246 printer.print_class_factory();
247 osprintf(os, "\n");
248 osprintf(os, "class %s ", cppname);
249 if (clazz.is_type_subclass())
250 osprintf(os, ": public %s ",
251 type2cpp(clazz.superclass_name).c_str());
252 osprintf(os, "{\n");
253 printer.print_subclass_type();
254 printer.print_class_factory(" friend ");
255 osprintf(os, "\n");
256 osprintf(os, "protected:\n");
257 if (!clazz.is_type_subclass()) {
258 osprintf(os, " %s *ptr = nullptr;\n", name);
259 osprintf(os, "\n");
260 }
261 printer.print_protected_constructors();
262 osprintf(os, "\n");
263 osprintf(os, "public:\n");
264 printer.print_public_methods();
265
266 osprintf(os, "};\n");
267 }
268
269 /* Print forward declaration of class "clazz" to "os".
270 */
print_class_forward_decl(ostream & os,const isl_class & clazz)271 void plain_cpp_generator::print_class_forward_decl(ostream &os,
272 const isl_class &clazz)
273 {
274 std::string cppstring = type2cpp(clazz);
275 const char *cppname = cppstring.c_str();
276
277 osprintf(os, "class %s;\n", cppname);
278 }
279
280 /* Print global factory functions.
281 *
282 * Each class has two global factory functions:
283 *
284 * set manage(__isl_take isl_set *ptr);
285 * set manage_copy(__isl_keep isl_set *ptr);
286 *
287 * A user can construct isl C++ objects from a raw pointer and indicate whether
288 * they intend to take the ownership of the object or not through these global
289 * factory functions. This ensures isl object creation is very explicit and
290 * pointers are not converted by accident. Thanks to overloading, manage() and
291 * manage_copy() can be called on any isl raw pointer and the corresponding
292 * object is automatically created, without the user having to choose the right
293 * isl object type.
294 *
295 * For a subclass based on a type function, no factory functions
296 * are introduced because they share the C object type with
297 * the superclass.
298 */
print_class_factory(const std::string & prefix)299 void plain_cpp_generator::decl_printer::print_class_factory(
300 const std::string &prefix)
301 {
302 const char *name = clazz.name.c_str();
303 const char *cppname = cppstring.c_str();
304
305 if (clazz.is_type_subclass())
306 return;
307
308 os << prefix;
309 osprintf(os, "inline %s manage(__isl_take %s *ptr);\n", cppname, name);
310 os << prefix;
311 osprintf(os, "inline %s manage_copy(__isl_keep %s *ptr);\n",
312 cppname, name);
313 }
314
315 /* Print declarations of protected constructors.
316 *
317 * Each class has currently one protected constructor:
318 *
319 * 1) Constructor from a plain isl_* C pointer
320 *
321 * Example:
322 *
323 * set(__isl_take isl_set *ptr);
324 *
325 * The raw pointer constructor is kept protected. Object creation is only
326 * possible through manage() or manage_copy().
327 */
print_protected_constructors()328 void plain_cpp_generator::decl_printer::print_protected_constructors()
329 {
330 const char *name = clazz.name.c_str();
331 const char *cppname = cppstring.c_str();
332
333 osprintf(os, " inline explicit %s(__isl_take %s *ptr);\n", cppname,
334 name);
335 }
336
337 /* Print declarations of public constructors.
338 *
339 * Each class currently has two public constructors:
340 *
341 * 1) A default constructor
342 * 2) A copy constructor
343 *
344 * Example:
345 *
346 * set();
347 * set(const set &set);
348 */
print_public_constructors()349 void plain_cpp_generator::decl_printer::print_public_constructors()
350 {
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".
359 */
print_method(const ConversionMethod & method)360 void plain_cpp_generator::decl_printer::print_method(
361 const ConversionMethod &method)
362 {
363 print_full_method_header(method);
364 }
365
366 /* Print declarations for "method".
367 */
print_method(const Method & method)368 void plain_cpp_generator::decl_printer::print_method(const Method &method)
369 {
370 print_full_method_header(method);
371 }
372
373 /* Print a declaration for a constructor for the "id" class
374 * that takes a user object.
375 */
print_id_constructor_user()376 void plain_cpp_generator::decl_printer::print_id_constructor_user()
377 {
378 print_id_constructor_user_header();
379 }
380
381 /* Print a declaration for an "id" method
382 * for retrieving the user object associated to the identifier.
383 * If "optional" is set, the method returns a std::optional user object.
384 */
print_id_user(bool optional)385 void plain_cpp_generator::decl_printer::print_id_user(bool optional)
386 {
387 print_id_user_header(optional);
388 }
389
390 /* Print declarations of copy assignment operator.
391 *
392 * Each class has one assignment operator.
393 *
394 * isl:set &set::operator=(set obj)
395 *
396 */
print_copy_assignment()397 void plain_cpp_generator::decl_printer::print_copy_assignment()
398 {
399 const char *cppname = cppstring.c_str();
400
401 osprintf(os, " inline %s &operator=(%s obj);\n", cppname, cppname);
402 }
403
404 /* Print declaration of destructor.
405 *
406 * No explicit destructor is needed for type based subclasses.
407 */
print_destructor()408 void plain_cpp_generator::decl_printer::print_destructor()
409 {
410 const char *cppname = cppstring.c_str();
411
412 if (clazz.is_type_subclass())
413 return;
414
415 osprintf(os, " inline ~%s();\n", cppname);
416 }
417
418 /* Print declaration of pointer functions.
419 * Since type based subclasses share the pointer with their superclass,
420 * they can also reuse these functions from the superclass.
421 *
422 * To obtain a raw pointer three functions are provided:
423 *
424 * 1) __isl_give isl_set *copy()
425 *
426 * Returns a pointer to a _copy_ of the internal object
427 *
428 * 2) __isl_keep isl_set *get()
429 *
430 * Returns a pointer to the internal object
431 *
432 * 3) __isl_give isl_set *release()
433 *
434 * Returns a pointer to the internal object and resets the
435 * internal pointer to nullptr.
436 *
437 * We also provide functionality to explicitly check if a pointer is
438 * currently managed by this object.
439 *
440 * 4) bool is_null()
441 *
442 * Check if the current object is a null pointer.
443 *
444 * The functions get() and release() model the value_ptr proposed in
445 * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3339.pdf.
446 * The copy() function is an extension to allow the user to explicitly
447 * copy the underlying object.
448 *
449 * Also generate a declaration to delete copy() for r-values, for
450 * r-values release() should be used to avoid unnecessary copies.
451 */
print_ptr()452 void plain_cpp_generator::decl_printer::print_ptr()
453 {
454 const char *name = clazz.name.c_str();
455
456 if (clazz.is_type_subclass())
457 return;
458
459 osprintf(os, " inline __isl_give %s *copy() const &;\n", name);
460 osprintf(os, " inline __isl_give %s *copy() && = delete;\n", name);
461 osprintf(os, " inline __isl_keep %s *get() const;\n", name);
462 osprintf(os, " inline __isl_give %s *release();\n", name);
463 osprintf(os, " inline bool is_null() const;\n");
464 }
465
466 /* Print a template declaration with given indentation
467 * for the "isa_type" method that ensures it is only enabled
468 * when called with a template argument
469 * that represents a type that is equal to that
470 * of the return type of the type function of "super".
471 * In particular, "isa_type" gets called from "isa"
472 * with as template argument the type of the "type" field
473 * of the subclass.
474 * The check ensures that this subclass is in fact a direct subclass
475 * of "super".
476 */
print_isa_type_template(int indent,const isl_class & super)477 void plain_cpp_generator::decl_printer::print_isa_type_template(int indent,
478 const isl_class &super)
479 {
480 osprintf(os, indent,
481 "template <typename T,\n");
482 osprintf(os, indent,
483 " typename = typename std::enable_if<std::is_same<\n");
484 osprintf(os, indent,
485 " const decltype(%s(NULL)),\n",
486 super.fn_type->getNameAsString().c_str());
487 osprintf(os, indent,
488 " const T>::value>::type>\n");
489 }
490
491 /* Print declarations for the "as" and "isa" methods, if the printed class
492 * is a superclass with a type function.
493 *
494 * "isa" checks whether an object is of a given subclass type.
495 * "isa_type" does the same, but gets passed the value of the type field
496 * of the subclass as a function argument and the type of this field
497 * as a template argument.
498 * "as" tries to cast an object to a given subclass type, returning
499 * an invalid object if the object is not of the given type.
500 */
print_downcast()501 void plain_cpp_generator::decl_printer::print_downcast()
502 {
503 if (!clazz.fn_type)
504 return;
505
506 osprintf(os, "private:\n");
507 print_isa_type_template(2, clazz);
508 osprintf(os, " inline %s isa_type(T subtype) const;\n",
509 generator.isl_bool2cpp().c_str());
510 osprintf(os, "public:\n");
511 osprintf(os, " template <class T> inline %s isa() const;\n",
512 generator.isl_bool2cpp().c_str());
513 osprintf(os, " template <class T> inline T as() const;\n");
514 }
515
516 /* Print the declaration of the ctx method.
517 */
print_ctx()518 void plain_cpp_generator::decl_printer::print_ctx()
519 {
520 std::string ns = generator.isl_namespace();
521
522 osprintf(os, " inline %sctx ctx() const;\n", ns.c_str());
523 }
524
525 /* Print a separator between groups of method declarations.
526 */
print_method_separator()527 void plain_cpp_generator::decl_printer::print_method_separator()
528 {
529 os << "\n";
530 }
531
532 /* Add a space to the return type "type" if needed,
533 * i.e., if it is not the type of a pointer.
534 */
add_space_to_return_type(const string & type)535 static string add_space_to_return_type(const string &type)
536 {
537 if (type[type.size() - 1] == '*')
538 return type;
539 return type + " ";
540 }
541
542 /* Print the prototype of the static inline method that is used
543 * as the C callback set by "method".
544 */
print_persistent_callback_prototype(FunctionDecl * method)545 void plain_cpp_generator::plain_printer::print_persistent_callback_prototype(
546 FunctionDecl *method)
547 {
548 string callback_name, rettype, c_args;
549 ParmVarDecl *param = persistent_callback_arg(method);
550 const FunctionProtoType *callback;
551 QualType ptype;
552 string classname;
553
554 ptype = param->getType();
555 callback = extract_prototype(ptype);
556
557 rettype = callback->getReturnType().getAsString();
558 rettype = add_space_to_return_type(rettype);
559 callback_name = clazz.persistent_callback_name(method);
560 c_args = generator.generate_callback_args(ptype, false);
561
562 if (!declarations)
563 classname = type2cpp(clazz) + "::";
564
565 osprintf(os, "%s%s%s(%s)",
566 rettype.c_str(), classname.c_str(),
567 callback_name.c_str(), c_args.c_str());
568 }
569
570 /* Print the prototype of the method for setting the callback function
571 * set by "method".
572 */
573 void
print_persistent_callback_setter_prototype(FunctionDecl * method)574 plain_cpp_generator::plain_printer::print_persistent_callback_setter_prototype(
575 FunctionDecl *method)
576 {
577 string classname, callback_name, cpptype;
578 ParmVarDecl *param = persistent_callback_arg(method);
579
580 if (!declarations)
581 classname = type2cpp(clazz) + "::";
582
583 cpptype = generator.param2cpp(param->getOriginalType());
584 callback_name = clazz.persistent_callback_name(method);
585 osprintf(os, "void %sset_%s_data(const %s &%s)",
586 classname.c_str(), callback_name.c_str(), cpptype.c_str(),
587 param->getName().str().c_str());
588 }
589
590 /* Given a method "method" for setting a persistent callback,
591 * print the fields that are needed for marshalling the callback.
592 *
593 * In particular, print
594 * - the declaration of a data structure for storing the C++ callback function
595 * - a shared pointer to such a data structure
596 * - the declaration of a static inline method
597 * for use as the C callback function
598 * - the declaration of a private method for setting the callback function
599 */
print_persistent_callback_data(FunctionDecl * method)600 void plain_cpp_generator::decl_printer::print_persistent_callback_data(
601 FunctionDecl *method)
602 {
603 string callback_name;
604 ParmVarDecl *param = generator.persistent_callback_arg(method);
605
606 callback_name = clazz.persistent_callback_name(method);
607 print_callback_data_decl(param, callback_name);
608 osprintf(os, ";\n");
609 osprintf(os, " std::shared_ptr<%s_data> %s_data;\n",
610 callback_name.c_str(), callback_name.c_str());
611 osprintf(os, " static inline ");
612 print_persistent_callback_prototype(method);
613 osprintf(os, ";\n");
614 osprintf(os, " inline ");
615 print_persistent_callback_setter_prototype(method);
616 osprintf(os, ";\n");
617 }
618
619 /* Print declarations needed for the persistent callbacks of the class.
620 *
621 * In particular, if there are any persistent callbacks, then
622 * print a private method for copying callback data from
623 * one object to another,
624 * private data for keeping track of the persistent callbacks and
625 * public methods for setting the persistent callbacks.
626 */
print_persistent_callbacks()627 void plain_cpp_generator::decl_printer::print_persistent_callbacks()
628 {
629 const char *cppname = cppstring.c_str();
630
631 if (!clazz.has_persistent_callbacks())
632 return;
633
634 osprintf(os, "private:\n");
635 osprintf(os, " inline %s ©_callbacks(const %s &obj);\n",
636 cppname, cppname);
637 for (const auto &callback : clazz.persistent_callbacks)
638 print_persistent_callback_data(callback);
639
640 osprintf(os, "public:\n");
641 for (const auto &callback : clazz.persistent_callbacks)
642 print_method(Method(clazz, callback));
643 }
644
645 /* Print a declaration for the "get" method "fd",
646 * using a name that includes the "get_" prefix.
647 */
print_get_method(FunctionDecl * fd)648 void plain_cpp_generator::decl_printer::print_get_method(FunctionDecl *fd)
649 {
650 string base = clazz.base_method_name(fd);
651
652 print_method(Method(clazz, fd, base));
653 }
654
655 /* Print implementations for class "clazz" to "os".
656 */
print_class_impl(ostream & os,const isl_class & clazz)657 void plain_cpp_generator::print_class_impl(ostream &os, const isl_class &clazz)
658 {
659 impl_printer printer(os, clazz, *this);
660 const char *cppname = printer.cppstring.c_str();
661
662 osprintf(os, "// implementations for isl::%s", cppname);
663
664 printer.print_class_factory();
665 printer.print_protected_constructors();
666 printer.print_public_methods();
667 printer.print_stream_insertion();
668 }
669
670 /* Print code for throwing an exception corresponding to the last error
671 * that occurred on "saved_ctx".
672 * This assumes that a valid isl::ctx is available in the "saved_ctx" variable,
673 * e.g., through a prior call to print_save_ctx.
674 */
print_throw_last_error(ostream & os)675 static void print_throw_last_error(ostream &os)
676 {
677 osprintf(os, " exception::throw_last_error(saved_ctx);\n");
678 }
679
680 /* Print code with the given indentation
681 * for throwing an exception_invalid with the given message.
682 */
print_throw_invalid(ostream & os,int indent,const char * msg)683 static void print_throw_invalid(ostream &os, int indent, const char *msg)
684 {
685 osprintf(os, indent,
686 "exception::throw_invalid(\"%s\", __FILE__, __LINE__);\n", msg);
687 }
688
689 /* Print code for throwing an exception on NULL input.
690 */
print_throw_NULL_input(ostream & os)691 static void print_throw_NULL_input(ostream &os)
692 {
693 print_throw_invalid(os, 4, "NULL input");
694 }
695
696 /* Print code with the given indentation
697 * for acting on an invalid error with message "msg".
698 * In particular, throw an exception_invalid.
699 * In the checked C++ bindings, isl_die is called instead with the code
700 * in "checked_code".
701 */
print_invalid(ostream & os,int indent,const char * msg,const char * checked_code)702 void plain_cpp_generator::print_invalid(ostream &os, int indent,
703 const char *msg, const char *checked_code)
704 {
705 if (checked)
706 osprintf(os, indent,
707 "isl_die(ctx().get(), isl_error_invalid, "
708 "\"%s\", %s);\n", msg, checked_code);
709 else
710 print_throw_invalid(os, indent, msg);
711 }
712
713 /* Print an operator for inserting objects of the class
714 * into an output stream.
715 *
716 * Unless checked C++ bindings are being generated,
717 * the operator requires its argument to be non-NULL.
718 * An exception is thrown if anything went wrong during the printing.
719 * During this printing, isl is made not to print any error message
720 * because the error message is included in the exception.
721 *
722 * If checked C++ bindings are being generated and anything went wrong,
723 * then record this failure in the output stream.
724 */
print_stream_insertion()725 void plain_cpp_generator::impl_printer::print_stream_insertion()
726 {
727 const char *name = clazz.name.c_str();
728 const char *cppname = cppstring.c_str();
729
730 if (!clazz.fn_to_str)
731 return;
732
733 osprintf(os, "\n");
734 osprintf(os, "inline std::ostream &operator<<(std::ostream &os, ");
735 osprintf(os, "const %s &obj)\n", cppname);
736 osprintf(os, "{\n");
737 print_check_ptr_start("obj.get()");
738 osprintf(os, " char *str = %s_to_str(obj.get());\n", name);
739 print_check_ptr_end("str");
740 if (generator.checked) {
741 osprintf(os, " if (!str) {\n");
742 osprintf(os, " os.setstate(std::ios_base::badbit);\n");
743 osprintf(os, " return os;\n");
744 osprintf(os, " }\n");
745 }
746 osprintf(os, " os << str;\n");
747 osprintf(os, " free(str);\n");
748 osprintf(os, " return os;\n");
749 osprintf(os, "}\n");
750 }
751
752 /* Print code that checks that "ptr" is not NULL at input.
753 *
754 * Omit the check if checked C++ bindings are being generated.
755 */
print_check_ptr(const char * ptr)756 void plain_cpp_generator::impl_printer::print_check_ptr(const char *ptr)
757 {
758 if (generator.checked)
759 return;
760
761 osprintf(os, " if (!%s)\n", ptr);
762 print_throw_NULL_input(os);
763 }
764
765 /* Print code that checks that "ptr" is not NULL at input and
766 * that saves a copy of the isl_ctx of "ptr" for a later check.
767 *
768 * Omit the check if checked C++ bindings are being generated.
769 */
print_check_ptr_start(const char * ptr)770 void plain_cpp_generator::impl_printer::print_check_ptr_start(const char *ptr)
771 {
772 if (generator.checked)
773 return;
774
775 print_check_ptr(ptr);
776 print_save_ctx(clazz.name + "_get_ctx(" + ptr + ")");
777 print_on_error_continue();
778 }
779
780 /* Print code that checks that "ptr" is not NULL at the end.
781 * A copy of the isl_ctx is expected to have been saved by
782 * code generated by print_check_ptr_start.
783 *
784 * Omit the check if checked C++ bindings are being generated.
785 */
print_check_ptr_end(const char * ptr)786 void plain_cpp_generator::impl_printer::print_check_ptr_end(const char *ptr)
787 {
788 if (generator.checked)
789 return;
790
791 osprintf(os, " if (!%s)\n", ptr);
792 print_throw_last_error(os);
793 }
794
795 /* Print implementation of global factory functions.
796 *
797 * Each class has two global factory functions:
798 *
799 * set manage(__isl_take isl_set *ptr);
800 * set manage_copy(__isl_keep isl_set *ptr);
801 *
802 * Unless checked C++ bindings are being generated,
803 * both functions require the argument to be non-NULL.
804 * An exception is thrown if anything went wrong during the copying
805 * in manage_copy.
806 * During the copying, isl is made not to print any error message
807 * because the error message is included in the exception.
808 *
809 * For a subclass based on a type function, no factory functions
810 * are introduced because they share the C object type with
811 * the superclass.
812 */
print_class_factory()813 void plain_cpp_generator::impl_printer::print_class_factory()
814 {
815 const char *name = clazz.name.c_str();
816 const char *cppname = cppstring.c_str();
817
818 if (clazz.is_type_subclass())
819 return;
820
821 osprintf(os, "\n");
822 osprintf(os, "%s manage(__isl_take %s *ptr) {\n", cppname, name);
823 print_check_ptr("ptr");
824 osprintf(os, " return %s(ptr);\n", cppname);
825 osprintf(os, "}\n");
826
827 osprintf(os, "%s manage_copy(__isl_keep %s *ptr) {\n", cppname,
828 name);
829 print_check_ptr_start("ptr");
830 osprintf(os, " ptr = %s_copy(ptr);\n", name);
831 print_check_ptr_end("ptr");
832 osprintf(os, " return %s(ptr);\n", cppname);
833 osprintf(os, "}\n");
834 }
835
836 /* Print implementations of protected constructors.
837 *
838 * The pointer to the isl object is either initialized directly or
839 * through the (immediate) superclass.
840 */
print_protected_constructors()841 void plain_cpp_generator::impl_printer::print_protected_constructors()
842 {
843 const char *name = clazz.name.c_str();
844 const char *cppname = cppstring.c_str();
845 bool subclass = clazz.is_type_subclass();
846
847 osprintf(os, "\n");
848 osprintf(os, "%s::%s(__isl_take %s *ptr)\n", cppname, cppname, name);
849 if (subclass)
850 osprintf(os, " : %s(ptr) {}\n",
851 type2cpp(clazz.superclass_name).c_str());
852 else
853 osprintf(os, " : ptr(ptr) {}\n");
854 }
855
856 /* Print implementations of public constructors.
857 *
858 * The pointer to the isl object is either initialized directly or
859 * through the (immediate) superclass.
860 *
861 * If the class has any persistent callbacks, then copy them
862 * from the original object in the copy constructor.
863 * If the class is a subclass, then the persistent callbacks
864 * are assumed to be copied by the copy constructor of the superclass.
865 *
866 * Throw an exception from the copy constructor if anything went wrong
867 * during the copying or if the input is NULL, if any copying is performed.
868 * During the copying, isl is made not to print any error message
869 * because the error message is included in the exception.
870 * No exceptions are thrown if checked C++ bindings
871 * are being generated,
872 */
print_public_constructors()873 void plain_cpp_generator::impl_printer::print_public_constructors()
874 {
875 std::string super;
876 const char *cppname = cppstring.c_str();
877 bool subclass = clazz.is_type_subclass();
878
879 osprintf(os, "\n");
880 if (subclass)
881 super = type2cpp(clazz.superclass_name);
882 osprintf(os, "%s::%s()\n", cppname, cppname);
883 if (subclass)
884 osprintf(os, " : %s() {}\n\n", super.c_str());
885 else
886 osprintf(os, " : ptr(nullptr) {}\n\n");
887 osprintf(os, "%s::%s(const %s &obj)\n", cppname, cppname, cppname);
888 if (subclass)
889 osprintf(os, " : %s(obj)\n", super.c_str());
890 else
891 osprintf(os, " : ptr(nullptr)\n");
892 osprintf(os, "{\n");
893 if (!subclass) {
894 print_check_ptr_start("obj.ptr");
895 osprintf(os, " ptr = obj.copy();\n");
896 if (clazz.has_persistent_callbacks())
897 osprintf(os, " copy_callbacks(obj);\n");
898 print_check_ptr_end("ptr");
899 }
900 osprintf(os, "}\n");
901 }
902
903 /* Print definition for "method",
904 * without any automatic type conversions.
905 *
906 * This method distinguishes three kinds of methods: member methods, static
907 * methods, and constructors.
908 *
909 * Member methods and static methods return a newly managed
910 * isl C++ object.
911 *
912 * Constructors create a new object from a given set of input parameters. They
913 * do not return a value, but instead update the pointer stored inside the
914 * newly created object.
915 *
916 * Unless checked C++ bindings are being generated,
917 * the inputs of the method are first checked for being valid isl objects and
918 * a copy of the associated isl::ctx is saved (if needed).
919 * If any failure occurs, either during the check for the inputs or
920 * during the isl function call, an exception is thrown.
921 * During the function call, isl is made not to print any error message
922 * because the error message is included in the exception.
923 */
print_method(const Method & method)924 void plain_cpp_generator::impl_printer::print_method(const Method &method)
925 {
926 string methodname = method.fd->getName().str();
927 int num_params = method.c_num_params();
928
929 osprintf(os, "\n");
930 print_full_method_header(method);
931 osprintf(os, "{\n");
932 print_argument_validity_check(method);
933 print_save_ctx(method);
934 print_on_error_continue();
935
936 for (const auto &callback : method.callbacks)
937 print_callback_local(callback);
938
939 osprintf(os, " auto res = %s", methodname.c_str());
940
941 method.print_fd_arg_list(os, 0, num_params, [&] (int i, int arg) {
942 method.print_param_use(os, i);
943 });
944 osprintf(os, ";\n");
945
946 print_exceptional_execution_check(method);
947 if (method.kind == Method::Kind::constructor) {
948 osprintf(os, " ptr = res;\n");
949 } else {
950 print_method_return(method);
951 }
952
953 osprintf(os, "}\n");
954 }
955
956 /* Convert argument of type "src" to "dst", with a name specified by "dst".
957 *
958 * If "src" is the same as "dst", then no argument conversion is needed.
959 *
960 * Otherwise, call the conversion function
961 * with as arguments the isl_ctx of the object and the argument name,
962 * or simply the argument name if the source type is an isl type.
963 * This means this isl_ctx should be available.
964 */
print_arg_conversion(ParmVarDecl * dst,ParmVarDecl * src)965 void plain_cpp_generator::impl_printer::print_arg_conversion(ParmVarDecl *dst,
966 ParmVarDecl *src)
967 {
968 std::string name = dst->getName().str();
969 QualType type = dst->getOriginalType();
970 string cpptype = generator.param2cpp(type);
971
972 if (dst == src)
973 os << name;
974 else if (is_isl_type(src->getOriginalType()))
975 os << cpptype << "(" << name << ")";
976 else
977 os << cpptype << "(ctx(), " << name << ")";
978 }
979
980 /* Print a definition for "method",
981 * where "this" or at least one of the argument types needs to be converted.
982 *
983 * "method" is assumed to be a member method.
984 *
985 * The generated method performs the required conversion(s) and
986 * calls the method generated without conversions.
987 *
988 * Perform a conversion from the argument in the method declaration
989 * (as specified by Method::get_param) to the argument of the C function,
990 * if needed.
991 * Such a conversion may require the isl_ctx to be available.
992 * In order to be able to use this isl_ctx, the current object needs
993 * to valid. The validity of other arguments is checked
994 * by the called method.
995 */
print_method(const ConversionMethod & method)996 void plain_cpp_generator::impl_printer::print_method(
997 const ConversionMethod &method)
998 {
999 if (method.kind != Method::Kind::member_method)
1000 die("Automatic conversion currently only supported "
1001 "for object methods");
1002
1003 osprintf(os, "\n");
1004 print_full_method_header(method);
1005 osprintf(os, "{\n");
1006 print_check_ptr("ptr");
1007 osprintf(os, " return ");
1008 method.print_call(os, generator.isl_namespace());
1009 method.print_cpp_arg_list(os, [&] (int i, int arg) {
1010 ParmVarDecl *param = method.fd->getParamDecl(i);
1011
1012 print_arg_conversion(param, method.get_param(i));
1013 });
1014 osprintf(os, ";\n");
1015 osprintf(os, "}\n");
1016 }
1017
1018 /* Print a definition for a constructor for the "id" class
1019 * that takes a user object.
1020 *
1021 * The user object is taken as a std::any and copied into
1022 * a new std::any object on the heap.
1023 * A pointer to this heap object is stored in the isl_id and
1024 * is scheduled to be freed when the reference count of the isl_id
1025 * drops to zero.
1026 * If the allocation of the isl_id fails, then the heap object
1027 * will not be freed automatically, so it needs to be freed manually.
1028 *
1029 * Unless checked C++ bindings are being generated,
1030 * the ctx argument is copied into the save_ctx variable
1031 * for use by print_throw_last_error, which throws an exception
1032 * if the construction fails.
1033 * During the function call, isl is made not to print any error message
1034 * because the error message is included in the exception.
1035 */
print_id_constructor_user()1036 void plain_cpp_generator::impl_printer::print_id_constructor_user()
1037 {
1038 print_id_constructor_user_header();
1039 os << "{\n";
1040 if (!generator.checked) {
1041 print_save_ctx("ctx");
1042 print_on_error_continue();
1043 }
1044 os << " std::any *p = new std::any(any);\n";
1045 os << " auto res = isl_id_alloc(ctx.get(), str.c_str(), p);\n";
1046 os << " res = isl_id_set_free_user(res, &ctx::free_user);\n";
1047 os << " if (!res) {\n";
1048 os << " delete p;\n";
1049 if (!generator.checked)
1050 print_throw_last_error(os);
1051 os << " }\n";
1052 os << " ptr = res;\n";
1053 os << "}\n";
1054 }
1055
1056 /* Print a definition for an "id" method
1057 * for retrieving the user object associated to the identifier.
1058 * If "optional" is set, the method returns a std::optional user object.
1059 * The returned object is of a type specified by template parameter T.
1060 *
1061 * The isl_id needs to have been created by the constructor generated
1062 * by print_id_constructor_user. That is, it needs to have a user pointer and
1063 * it needs to have its free_user callback set to &ctx::free_user.
1064 * The object stored in the std::any also needs to be of the required type.
1065 *
1066 * If "optional" is set, return a std::nullopt if any of the checks fail.
1067 * Otherwise, throw an exception_invalid (or call isl_die and
1068 * return a default T in the checked C++ bindings).
1069 */
print_id_user(bool optional)1070 void plain_cpp_generator::impl_printer::print_id_user(bool optional)
1071 {
1072 auto fail = [&] (const char *msg) {
1073 if (optional)
1074 os << " return std::nullopt;\n";
1075 else
1076 generator.print_invalid(os, 4, msg, "return T()");
1077 };
1078 os << "\n";
1079 print_id_user_header(optional);
1080 os << "{\n";
1081 print_check_ptr("ptr");
1082 os << " std::any *p = (std::any *) isl_id_get_user(ptr);\n";
1083 os << " if (!p)\n";
1084 fail("no user pointer");
1085 os << " if (isl_id_get_free_user(ptr) != &ctx::free_user)\n";
1086 fail("user pointer not attached by C++ interface");
1087 os << " T *res = std::any_cast<T>(p);\n";
1088 os << " if (!res)\n";
1089 fail("user pointer not of given type");
1090 os << " return *res;\n";
1091 os << "}\n";
1092 }
1093
1094 /* Print implementation of copy assignment operator.
1095 *
1096 * If the class has any persistent callbacks, then copy them
1097 * from the original object.
1098 */
print_copy_assignment()1099 void plain_cpp_generator::impl_printer::print_copy_assignment()
1100 {
1101 const char *name = clazz.name.c_str();
1102 const char *cppname = cppstring.c_str();
1103
1104 osprintf(os, "\n");
1105 osprintf(os, "%s &%s::operator=(%s obj) {\n", cppname,
1106 cppname, cppname);
1107 osprintf(os, " std::swap(this->ptr, obj.ptr);\n", name);
1108 if (clazz.has_persistent_callbacks())
1109 osprintf(os, " copy_callbacks(obj);\n");
1110 osprintf(os, " return *this;\n");
1111 osprintf(os, "}\n");
1112 }
1113
1114 /* Print implementation of destructor.
1115 *
1116 * No explicit destructor is needed for type based subclasses.
1117 */
print_destructor()1118 void plain_cpp_generator::impl_printer::print_destructor()
1119 {
1120 const char *name = clazz.name.c_str();
1121 const char *cppname = cppstring.c_str();
1122
1123 if (clazz.is_type_subclass())
1124 return;
1125
1126 osprintf(os, "\n");
1127 osprintf(os, "%s::~%s() {\n", cppname, cppname);
1128 osprintf(os, " if (ptr)\n");
1129 osprintf(os, " %s_free(ptr);\n", name);
1130 osprintf(os, "}\n");
1131 }
1132
1133 /* Print a check that the persistent callback corresponding to "fd"
1134 * is not set, throwing an exception (or printing an error message
1135 * and returning nullptr) if it is set.
1136 */
print_check_no_persistent_callback(ostream & os,const isl_class & clazz,FunctionDecl * fd)1137 void plain_cpp_generator::print_check_no_persistent_callback(ostream &os,
1138 const isl_class &clazz, FunctionDecl *fd)
1139 {
1140 string callback_name = clazz.persistent_callback_name(fd);
1141
1142 osprintf(os, " if (%s_data)\n", callback_name.c_str());
1143 print_invalid(os, 4, "cannot release object with persistent callbacks",
1144 "return nullptr");
1145 }
1146
1147 /* Print implementation of ptr() functions.
1148 * Since type based subclasses share the pointer with their superclass,
1149 * they can also reuse these functions from the superclass.
1150 *
1151 * If an object has persistent callbacks set, then the underlying
1152 * C object pointer cannot be released because it references data
1153 * in the C++ object.
1154 */
print_ptr()1155 void plain_cpp_generator::impl_printer::print_ptr()
1156 {
1157 const char *name = clazz.name.c_str();
1158 const char *cppname = cppstring.c_str();
1159 set<FunctionDecl *>::const_iterator in;
1160 const set<FunctionDecl *> &callbacks = clazz.persistent_callbacks;
1161
1162 if (clazz.is_type_subclass())
1163 return;
1164
1165 osprintf(os, "\n");
1166 osprintf(os, "__isl_give %s *%s::copy() const & {\n", name, cppname);
1167 osprintf(os, " return %s_copy(ptr);\n", name);
1168 osprintf(os, "}\n\n");
1169 osprintf(os, "__isl_keep %s *%s::get() const {\n", name, cppname);
1170 osprintf(os, " return ptr;\n");
1171 osprintf(os, "}\n\n");
1172 osprintf(os, "__isl_give %s *%s::release() {\n", name, cppname);
1173 for (in = callbacks.begin(); in != callbacks.end(); ++in)
1174 generator.print_check_no_persistent_callback(os, clazz, *in);
1175 osprintf(os, " %s *tmp = ptr;\n", name);
1176 osprintf(os, " ptr = nullptr;\n");
1177 osprintf(os, " return tmp;\n");
1178 osprintf(os, "}\n\n");
1179 osprintf(os, "bool %s::is_null() const {\n", cppname);
1180 osprintf(os, " return ptr == nullptr;\n");
1181 osprintf(os, "}\n");
1182 }
1183
1184 /* Print implementations for the "as" and "isa" methods, if the printed class
1185 * is a superclass with a type function.
1186 *
1187 * "isa" checks whether an object is of a given subclass type.
1188 * "isa_type" does the same, but gets passed the value of the type field
1189 * of the subclass as a function argument and the type of this field
1190 * as a template argument.
1191 * "as" casts an object to a given subclass type, erroring out
1192 * if the object is not of the given type.
1193 *
1194 * If the input is an invalid object, then these methods raise
1195 * an exception.
1196 * If checked bindings are being generated,
1197 * then an invalid boolean or object is returned instead.
1198 */
print_downcast()1199 void plain_cpp_generator::impl_printer::print_downcast()
1200 {
1201 const char *cppname = cppstring.c_str();
1202
1203 if (!clazz.fn_type)
1204 return;
1205
1206 osprintf(os, "\n");
1207 osprintf(os, "template <typename T, typename>\n");
1208 osprintf(os, "%s %s::isa_type(T subtype) const\n",
1209 generator.isl_bool2cpp().c_str(), cppname);
1210 osprintf(os, "{\n");
1211 osprintf(os, " if (is_null())\n");
1212 if (generator.checked)
1213 osprintf(os, " return boolean();\n");
1214 else
1215 print_throw_NULL_input(os);
1216 osprintf(os, " return %s(get()) == subtype;\n",
1217 clazz.fn_type->getNameAsString().c_str());
1218 osprintf(os, "}\n");
1219
1220 osprintf(os, "template <class T>\n");
1221 osprintf(os, "%s %s::isa() const\n",
1222 generator.isl_bool2cpp().c_str(), cppname);
1223 osprintf(os, "{\n");
1224 osprintf(os, " return isa_type<decltype(T::type)>(T::type);\n");
1225 osprintf(os, "}\n");
1226
1227 osprintf(os, "template <class T>\n");
1228 osprintf(os, "T %s::as() const\n", cppname);
1229 osprintf(os, "{\n");
1230 if (generator.checked)
1231 osprintf(os, " if (isa<T>().is_false())\n");
1232 else
1233 osprintf(os, " if (!isa<T>())\n");
1234 generator.print_invalid(os, 4, "not an object of the requested subtype",
1235 "return T()");
1236 osprintf(os, " return T(copy());\n");
1237 osprintf(os, "}\n");
1238 }
1239
1240 /* Print the implementation of the ctx method.
1241 */
print_ctx()1242 void plain_cpp_generator::impl_printer::print_ctx()
1243 {
1244 const char *name = clazz.name.c_str();
1245 const char *cppname = cppstring.c_str();
1246 std::string ns = generator.isl_namespace();
1247
1248 osprintf(os, "\n");
1249 osprintf(os, "%sctx %s::ctx() const {\n", ns.c_str(), cppname);
1250 osprintf(os, " return %sctx(%s_get_ctx(ptr));\n", ns.c_str(), name);
1251 osprintf(os, "}\n");
1252 }
1253
1254 /* Print a separator between groups of method definitions.
1255 *
1256 * No additional separator is required between method definitions.
1257 */
print_method_separator()1258 void plain_cpp_generator::impl_printer::print_method_separator()
1259 {
1260 }
1261
1262 /* Print the implementations of the methods needed for the persistent callbacks
1263 * of the class.
1264 */
print_persistent_callbacks()1265 void plain_cpp_generator::impl_printer::print_persistent_callbacks()
1266 {
1267 const char *cppname = cppstring.c_str();
1268 string classname = type2cpp(clazz);
1269
1270 if (!clazz.has_persistent_callbacks())
1271 return;
1272
1273 osprintf(os, "\n");
1274 osprintf(os, "%s &%s::copy_callbacks(const %s &obj)\n",
1275 cppname, classname.c_str(), cppname);
1276 osprintf(os, "{\n");
1277 for (const auto &callback : clazz.persistent_callbacks) {
1278 string callback_name = clazz.persistent_callback_name(callback);
1279
1280 osprintf(os, " %s_data = obj.%s_data;\n",
1281 callback_name.c_str(), callback_name.c_str());
1282 }
1283 osprintf(os, " return *this;\n");
1284 osprintf(os, "}\n");
1285
1286 for (const auto &callback : clazz.persistent_callbacks)
1287 print_set_persistent_callback(Method(clazz, callback));
1288 }
1289
1290 /* Print a definition for the "get" method "fd" in class "clazz",
1291 * using a name that includes the "get_" prefix, to "os".
1292 *
1293 * This definition simply calls the variant without the "get_" prefix and
1294 * returns its result.
1295 * Note that static methods are not considered to be "get" methods.
1296 */
print_get_method(FunctionDecl * fd)1297 void plain_cpp_generator::impl_printer::print_get_method(FunctionDecl *fd)
1298 {
1299 string get_name = clazz.base_method_name(fd);
1300 string name = clazz.method_name(fd);
1301 int num_params = fd->getNumParams();
1302
1303 osprintf(os, "\n");
1304 print_full_method_header(Method(clazz, fd, get_name));
1305 osprintf(os, "{\n");
1306 osprintf(os, " return %s(", name.c_str());
1307 for (int i = 1; i < num_params; ++i) {
1308 ParmVarDecl *param = fd->getParamDecl(i);
1309
1310 if (i != 1)
1311 osprintf(os, ", ");
1312 osprintf(os, "%s", param->getName().str().c_str());
1313 }
1314 osprintf(os, ");\n");
1315 osprintf(os, "}\n");
1316 }
1317
1318 /* Print code that checks that all isl object arguments to "method" are valid
1319 * (not NULL) and throws an exception if they are not.
1320 *
1321 * If checked bindings are being generated,
1322 * then no such check is performed.
1323 */
print_argument_validity_check(const Method & method)1324 void plain_cpp_generator::impl_printer::print_argument_validity_check(
1325 const Method &method)
1326 {
1327 int n;
1328 bool first = true;
1329
1330 if (generator.checked)
1331 return;
1332
1333 n = method.num_params();
1334 for (int i = 0; i < n; ++i) {
1335 bool is_this;
1336 ParmVarDecl *param = method.fd->getParamDecl(i);
1337 string name = param->getName().str();
1338 const char *name_str = name.c_str();
1339 QualType type = param->getOriginalType();
1340
1341 is_this = i == 0 && method.kind == Method::Kind::member_method;
1342 if (!is_this && (is_isl_ctx(type) || !is_isl_type(type)))
1343 continue;
1344
1345 if (first)
1346 osprintf(os, " if (");
1347 else
1348 osprintf(os, " || ");
1349
1350 if (is_this)
1351 osprintf(os, "!ptr");
1352 else
1353 osprintf(os, "%s.is_null()", name_str);
1354
1355 first = false;
1356 }
1357 if (first)
1358 return;
1359 osprintf(os, ")\n");
1360 print_throw_NULL_input(os);
1361 }
1362
1363 /* Print code for saving a copy of "ctx" in a "saved_ctx" variable.
1364 */
print_save_ctx(const std::string & ctx)1365 void plain_cpp_generator::impl_printer::print_save_ctx(const std::string &ctx)
1366 {
1367 os << " auto saved_ctx = " << ctx << ";\n";
1368 }
1369
1370 /* Print code for saving a copy of the isl::ctx available at the start
1371 * of the method "method" in a "saved_ctx" variable,
1372 * for use in exception handling.
1373 *
1374 * If checked bindings are being generated,
1375 * then the "saved_ctx" variable is not needed.
1376 * If "method" is a member function, then obtain the isl_ctx from
1377 * the "this" object.
1378 * If the first argument of the method is an isl::ctx, then use that one.
1379 * Otherwise, save a copy of the isl::ctx associated to the first argument
1380 * of isl object type.
1381 */
print_save_ctx(const Method & method)1382 void plain_cpp_generator::impl_printer::print_save_ctx(const Method &method)
1383 {
1384 int n;
1385 ParmVarDecl *param = method.fd->getParamDecl(0);
1386 QualType type = param->getOriginalType();
1387
1388 if (generator.checked)
1389 return;
1390 if (method.kind == Method::Kind::member_method)
1391 return print_save_ctx("ctx()");
1392 if (is_isl_ctx(type))
1393 return print_save_ctx(param->getName().str());
1394 n = method.num_params();
1395 for (int i = 0; i < n; ++i) {
1396 ParmVarDecl *param = method.fd->getParamDecl(i);
1397 QualType type = param->getOriginalType();
1398
1399 if (!is_isl_type(type))
1400 continue;
1401 print_save_ctx(param->getName().str() + ".ctx()");
1402 return;
1403 }
1404 }
1405
1406 /* Print code to make isl not print an error message when an error occurs
1407 * within the current scope (if exceptions are available),
1408 * since the error message will be included in the exception.
1409 * If exceptions are not available, then exception::on_error
1410 * is set to ISL_ON_ERROR_ABORT and isl is therefore made to abort instead.
1411 *
1412 * If checked bindings are being generated,
1413 * then leave it to the user to decide what isl should do on error.
1414 * Otherwise, assume that a valid isl::ctx is available
1415 * in the "saved_ctx" variable,
1416 * e.g., through a prior call to print_save_ctx.
1417 */
print_on_error_continue()1418 void plain_cpp_generator::impl_printer::print_on_error_continue()
1419 {
1420 if (generator.checked)
1421 return;
1422 osprintf(os, " options_scoped_set_on_error saved_on_error(saved_ctx, "
1423 "exception::on_error);\n");
1424 }
1425
1426 /* Print code to "os" that checks whether any of the persistent callbacks
1427 * of the class of "method" is set and if it failed with an exception.
1428 * If so, the "eptr" in the corresponding data structure contains the exception
1429 * that was caught and that needs to be rethrown.
1430 * This field is cleared because the callback and its data may get reused.
1431 *
1432 * The check only needs to be generated for member methods since
1433 * an object is needed for any of the persistent callbacks to be set.
1434 */
print_persistent_callback_exceptional_execution_check(ostream & os,const Method & method)1435 static void print_persistent_callback_exceptional_execution_check(ostream &os,
1436 const Method &method)
1437 {
1438 if (method.kind != Method::Kind::member_method)
1439 return;
1440
1441 for (const auto &pcb : method.clazz.persistent_callbacks) {
1442 auto callback_name = method.clazz.persistent_callback_name(pcb);
1443
1444 osprintf(os, " if (%s_data && %s_data->eptr) {\n",
1445 callback_name.c_str(), callback_name.c_str());
1446 osprintf(os, " std::exception_ptr eptr = %s_data->eptr;\n",
1447 callback_name.c_str());
1448 osprintf(os, " %s_data->eptr = nullptr;\n",
1449 callback_name.c_str());
1450 osprintf(os, " std::rethrow_exception(eptr);\n");
1451 osprintf(os, " }\n");
1452 }
1453 }
1454
1455 /* Print code that checks whether the execution of the core of "method"
1456 * was successful.
1457 *
1458 * If checked bindings are being generated,
1459 * then no checks are performed.
1460 *
1461 * Otherwise, first check if any of the callbacks failed with
1462 * an exception. If so, the "eptr" in the corresponding data structure
1463 * contains the exception that was caught and that needs to be rethrown.
1464 * Then check if the function call failed in any other way and throw
1465 * the appropriate exception.
1466 * In particular, if the return type is isl_stat, isl_bool or isl_size,
1467 * then a negative value indicates a failure. If the return type
1468 * is an isl type, then a NULL value indicates a failure.
1469 * Assume print_save_ctx has made sure that a valid isl::ctx
1470 * is available in the "ctx" variable.
1471 */
print_exceptional_execution_check(const Method & method)1472 void plain_cpp_generator::impl_printer::print_exceptional_execution_check(
1473 const Method &method)
1474 {
1475 bool check_null, check_neg;
1476 QualType return_type = method.fd->getReturnType();
1477
1478 if (generator.checked)
1479 return;
1480
1481 print_persistent_callback_exceptional_execution_check(os, method);
1482
1483 for (const auto &callback : method.callbacks) {
1484 std::string name;
1485
1486 name = callback->getName().str();
1487 osprintf(os, " if (%s_data.eptr)\n", name.c_str());
1488 osprintf(os, " std::rethrow_exception(%s_data.eptr);\n",
1489 name.c_str());
1490 }
1491
1492 check_neg = is_isl_neg_error(return_type);
1493 check_null = is_isl_type(return_type);
1494 if (!check_null && !check_neg)
1495 return;
1496
1497 if (check_neg)
1498 osprintf(os, " if (res < 0)\n");
1499 else
1500 osprintf(os, " if (!res)\n");
1501 print_throw_last_error(os);
1502 }
1503
1504 /* Return a pointer to the appropriate type printer,
1505 * i.e., the regular type printer or the checked type printer
1506 * depending on the setting of this->checked.
1507 */
type_printer()1508 std::unique_ptr<cpp_type_printer> plain_cpp_generator::type_printer()
1509 {
1510 cpp_type_printer *printer;
1511
1512 if (checked)
1513 printer = new checked_cpp_type_printer();
1514 else
1515 printer = new cpp_type_printer();
1516
1517 return std::unique_ptr<cpp_type_printer>(printer);
1518 }
1519
1520 /* Return the C++ return type of the method "method".
1521 *
1522 * Use the appropriate type printer.
1523 */
get_return_type(const Method & method)1524 std::string plain_cpp_generator::get_return_type(const Method &method)
1525 {
1526 return type_printer()->return_type(method);
1527 }
1528
1529 /* Given a method "method" for setting a persistent callback of its class,
1530 * print the implementations of the methods needed for that callback.
1531 *
1532 * In particular, print
1533 * - the implementation of a static inline method
1534 * for use as the C callback function
1535 * - the definition of a private method for setting the callback function
1536 * - the public method for constructing a new object with the callback set.
1537 */
print_set_persistent_callback(const Method & method)1538 void plain_cpp_generator::impl_printer::print_set_persistent_callback(
1539 const Method &method)
1540 {
1541 string fullname = method.fd->getName().str();
1542 ParmVarDecl *param = persistent_callback_arg(method.fd);
1543 string pname;
1544 string callback_name = clazz.persistent_callback_name(method.fd);
1545
1546 osprintf(os, "\n");
1547 print_persistent_callback_prototype(method.fd);
1548 osprintf(os, "\n");
1549 osprintf(os, "{\n");
1550 print_callback_body(2, param, callback_name);
1551 osprintf(os, "}\n\n");
1552
1553 pname = param->getName().str();
1554 print_persistent_callback_setter_prototype(method.fd);
1555 osprintf(os, "\n");
1556 osprintf(os, "{\n");
1557 print_check_ptr_start("ptr");
1558 osprintf(os, " %s_data = std::make_shared<struct %s_data>();\n",
1559 callback_name.c_str(), callback_name.c_str());
1560 osprintf(os, " %s_data->func = %s;\n",
1561 callback_name.c_str(), pname.c_str());
1562 osprintf(os, " ptr = %s(ptr, &%s, %s_data.get());\n",
1563 fullname.c_str(), callback_name.c_str(), callback_name.c_str());
1564 print_check_ptr_end("ptr");
1565 osprintf(os, "}\n\n");
1566
1567 print_full_method_header(method);
1568 osprintf(os, "{\n");
1569 osprintf(os, " auto copy = *this;\n");
1570 osprintf(os, " copy.set_%s_data(%s);\n",
1571 callback_name.c_str(), pname.c_str());
1572 osprintf(os, " return copy;\n");
1573 osprintf(os, "}\n");
1574 }
1575
1576 /* Print the return statement of the C++ method "method".
1577 *
1578 * The result of the corresponding isl function is returned as a new
1579 * object if the underlying isl function returns an isl_* ptr, as a bool
1580 * if the isl function returns an isl_bool, as void if the isl functions
1581 * returns an isl_stat,
1582 * as std::string if the isl function returns 'const char *', and as
1583 * unmodified return value otherwise.
1584 * If checked C++ bindings are being generated,
1585 * then an isl_bool return type is transformed into a boolean and
1586 * an isl_stat into a stat since no exceptions can be generated
1587 * on negative results from the isl function.
1588 * If the method returns a new instance of the same object type and
1589 * if the class has any persistent callbacks, then the data
1590 * for these callbacks are copied from the original to the new object.
1591 * If "clazz" is a subclass that is based on a type function and
1592 * if the return type corresponds to the superclass data type,
1593 * then it is replaced by the subclass data type.
1594 */
print_method_return(const Method & method)1595 void plain_cpp_generator::impl_printer::print_method_return(
1596 const Method &method)
1597 {
1598 QualType return_type = method.fd->getReturnType();
1599 string rettype_str = generator.get_return_type(method);
1600 bool returns_super = method.is_subclass_mutator();
1601
1602 if (is_isl_type(return_type) ||
1603 (generator.checked && is_isl_neg_error(return_type))) {
1604 osprintf(os, " return manage(res)");
1605 if (is_mutator(clazz, method.fd) &&
1606 clazz.has_persistent_callbacks())
1607 osprintf(os, ".copy_callbacks(*this)");
1608 if (returns_super)
1609 osprintf(os, ".as<%s>()", rettype_str.c_str());
1610 osprintf(os, ";\n");
1611 } else if (is_isl_stat(return_type)) {
1612 osprintf(os, " return;\n");
1613 } else if (is_string(return_type)) {
1614 osprintf(os, " std::string tmp(res);\n");
1615 if (gives(method.fd))
1616 osprintf(os, " free(res);\n");
1617 osprintf(os, " return tmp;\n");
1618 } else {
1619 osprintf(os, " return res;\n");
1620 }
1621 }
1622
1623 /* Print the header for "method", including the terminating semicolon
1624 * in case of a declaration and a newline.
1625 *
1626 * Use the appropriate type printer to print argument and return types.
1627 */
print_full_method_header(const Method & method)1628 void plain_cpp_generator::plain_printer::print_full_method_header(
1629 const Method &method)
1630 {
1631 auto type_printer = generator.type_printer();
1632
1633 print_method_header(method, *type_printer);
1634
1635 if (declarations)
1636 osprintf(os, ";");
1637 osprintf(os, "\n");
1638 }
1639
1640 /* Generate the list of argument types for a callback function of
1641 * type "type". If "cpp" is set, then generate the C++ type list, otherwise
1642 * the C type list.
1643 *
1644 * Use the appropriate type printer.
1645 * For the plain C++ interface, the argument position is irrelevant,
1646 * so simply pass in -1.
1647 */
generate_callback_args(QualType type,bool cpp)1648 string plain_cpp_generator::generate_callback_args(QualType type, bool cpp)
1649 {
1650 return type_printer()->generate_callback_args(-1, type, cpp);
1651 }
1652
1653 /* Generate the full cpp type of a callback function of type "type".
1654 *
1655 * Use the appropriate type printer.
1656 * For the plain C++ interface, the argument position is irrelevant,
1657 * so simply pass in -1.
1658 */
generate_callback_type(QualType type)1659 string plain_cpp_generator::generate_callback_type(QualType type)
1660 {
1661 return type_printer()->generate_callback_type(-1, type);
1662 }
1663
1664 /* Print the call to the C++ callback function "call",
1665 * with the given indentation, wrapped
1666 * for use inside the lambda function that is used as the C callback function,
1667 * in the case where checked C++ bindings are being generated.
1668 *
1669 * In particular, print
1670 *
1671 * auto ret = @call@;
1672 * return ret.release();
1673 */
print_wrapped_call_checked(int indent,const string & call)1674 void plain_cpp_generator::impl_printer::print_wrapped_call_checked(int indent,
1675 const string &call)
1676 {
1677 osprintf(os, indent, "auto ret = %s;\n", call.c_str());
1678 osprintf(os, indent, "return ret.release();\n");
1679 }
1680
1681 /* Print the call to the C++ callback function "call",
1682 * with the given indentation and with return type "rtype", wrapped
1683 * for use inside the lambda function that is used as the C callback function.
1684 *
1685 * In particular, print
1686 *
1687 * ISL_CPP_TRY {
1688 * @call@;
1689 * return isl_stat_ok;
1690 * } ISL_CPP_CATCH_ALL {
1691 * data->eptr = std::current_exception();
1692 * return isl_stat_error;
1693 * }
1694 * or
1695 * ISL_CPP_TRY {
1696 * auto ret = @call@;
1697 * return ret ? isl_bool_true : isl_bool_false;
1698 * } ISL_CPP_CATCH_ALL {
1699 * data->eptr = std::current_exception();
1700 * return isl_bool_error;
1701 * }
1702 * or
1703 * ISL_CPP_TRY {
1704 * auto ret = @call@;
1705 * return ret.release();
1706 * } ISL_CPP_CATCH_ALL {
1707 * data->eptr = std::current_exception();
1708 * return NULL;
1709 * }
1710 *
1711 * depending on the return type.
1712 *
1713 * where ISL_CPP_TRY is defined to "try" and ISL_CPP_CATCH_ALL to "catch (...)"
1714 * (if exceptions are available).
1715 *
1716 * If checked C++ bindings are being generated, then
1717 * the call is wrapped differently.
1718 */
print_wrapped_call(int indent,const string & call,QualType rtype)1719 void plain_cpp_generator::impl_printer::print_wrapped_call(int indent,
1720 const string &call, QualType rtype)
1721 {
1722 if (generator.checked)
1723 return print_wrapped_call_checked(indent, call);
1724
1725 osprintf(os, indent, "ISL_CPP_TRY {\n");
1726 if (is_isl_stat(rtype))
1727 osprintf(os, indent, " %s;\n", call.c_str());
1728 else
1729 osprintf(os, indent, " auto ret = %s;\n", call.c_str());
1730 if (is_isl_stat(rtype))
1731 osprintf(os, indent, " return isl_stat_ok;\n");
1732 else if (is_isl_bool(rtype))
1733 osprintf(os, indent,
1734 " return ret ? isl_bool_true : isl_bool_false;\n");
1735 else
1736 osprintf(os, indent, " return ret.release();\n");
1737 osprintf(os, indent, "} ISL_CPP_CATCH_ALL {\n");
1738 osprintf(os, indent, " data->eptr = std::current_exception();\n");
1739 if (is_isl_stat(rtype))
1740 osprintf(os, indent, " return isl_stat_error;\n");
1741 else if (is_isl_bool(rtype))
1742 osprintf(os, indent, " return isl_bool_error;\n");
1743 else
1744 osprintf(os, indent, " return NULL;\n");
1745 osprintf(os, indent, "}\n");
1746 }
1747
1748 /* Print the declaration for a "prefix"_data data structure
1749 * that can be used for passing to a C callback function
1750 * containing a copy of the C++ callback function "param",
1751 * along with an std::exception_ptr that is used to store any
1752 * exceptions thrown in the C++ callback.
1753 *
1754 * If the C callback is of the form
1755 *
1756 * isl_stat (*fn)(__isl_take isl_map *map, void *user)
1757 *
1758 * then the following declaration is printed:
1759 *
1760 * struct <prefix>_data {
1761 * std::function<stat(map)> func;
1762 * std::exception_ptr eptr;
1763 * }
1764 *
1765 * (without a newline or a semicolon).
1766 *
1767 * The std::exception_ptr object is not added to "prefix"_data
1768 * if checked C++ bindings are being generated.
1769 */
print_callback_data_decl(ParmVarDecl * param,const string & prefix)1770 void plain_cpp_generator::plain_printer::print_callback_data_decl(
1771 ParmVarDecl *param,
1772 const string &prefix)
1773 {
1774 string cpp_args;
1775
1776 cpp_args = generator.generate_callback_type(param->getType());
1777
1778 osprintf(os, " struct %s_data {\n", prefix.c_str());
1779 osprintf(os, " %s func;\n", cpp_args.c_str());
1780 if (!generator.checked)
1781 osprintf(os, " std::exception_ptr eptr;\n");
1782 osprintf(os, " }");
1783 }
1784
1785 /* Given a group of methods with the same name,
1786 * should extra methods be added that take as arguments
1787 * those types that can be converted to the original argument type
1788 * through a unary constructor?
1789 *
1790 * Note that even if this method returns true,
1791 * the extra methods are only printed by the caller
1792 * if exactly one of the methods in the group was originally defined
1793 * in the printed class.
1794 * Signal that they should be printed if the group contains
1795 * both methods originally defined in the printed class and
1796 * methods that have been copied from an ancestor
1797 * by checking whether there are at least two methods in the group.
1798 */
want_descendent_overloads(const function_set & methods)1799 bool plain_cpp_generator::plain_printer::want_descendent_overloads(
1800 const function_set &methods)
1801 {
1802 return methods.size() > 1;
1803 }
1804
1805 /* Print the header of the constructor for the "id" class
1806 * that takes a user object.
1807 *
1808 * The user object is taken as a std::any.
1809 */
print_id_constructor_user_header()1810 void plain_cpp_generator::plain_printer::print_id_constructor_user_header()
1811 {
1812 if (declarations)
1813 os << " inline explicit ";
1814 else
1815 os << "id::";
1816 os << "id(" << generator.isl_namespace() << "ctx ctx, "
1817 << "const std::string &str, const std::any &any)";
1818 if (declarations)
1819 os << ";";
1820 os << "\n";
1821 }
1822
1823 /* Print the header of the "id" method
1824 * for retrieving the user object associated to the identifier.
1825 * If "optional" is set, the method returns a std::optional user object.
1826 * The returned object is of a type specified by template parameter T.
1827 */
print_id_user_header(bool optional)1828 void plain_cpp_generator::plain_printer::print_id_user_header(bool optional)
1829 {
1830 auto indent = declarations ? " " : "";
1831 os << indent << "template <class T>\n";
1832 os << indent << (optional ? "std::optional<T> " : "T ");
1833 if (!declarations)
1834 os << "id::";
1835 os << (optional ? "try_" : "");
1836 os << "user() const";
1837 if (declarations)
1838 os << ";";
1839 os << "\n";
1840 }
1841
1842 /* Perform printing by "fn" in a context that only gets compiled
1843 * by C++17 compilers.
1844 */
on_cplusplus17(ostream & os,const std::function<void (void)> & fn)1845 static void on_cplusplus17(ostream &os, const std::function<void(void)> &fn)
1846 {
1847 os << "#if __cplusplus >= 201703L\n";
1848 fn();
1849 os << "#endif\n";
1850 }
1851
1852 /* Print declarations or definitions of the special methods of the "id" class
1853 * that are not automatically derived from the C interface.
1854 *
1855 * In particular, print a constructor that takes a user pointer
1856 * as well as methods for retrieving this user pointer.
1857 *
1858 * These methods require C++17 features.
1859 */
print_special_id()1860 void plain_cpp_generator::plain_printer::print_special_id()
1861 {
1862 os << "\n";
1863 on_cplusplus17(os, [this] () {
1864 print_id_constructor_user();
1865 print_id_user(true);
1866 print_id_user(false);
1867 });
1868 }
1869
1870 /* Print declarations or definitions of any special methods of this class
1871 * not automatically derived from the C interface.
1872 *
1873 * In particular, print special methods for the "id" class.
1874 */
print_special()1875 void plain_cpp_generator::plain_printer::print_special()
1876 {
1877 if (clazz.name == "isl_id")
1878 print_special_id();
1879 }
1880
1881 /* Print declarations or definitions of the public methods.
1882 */
print_public_methods()1883 void plain_cpp_generator::plain_printer::print_public_methods()
1884 {
1885 print_public_constructors();
1886 print_constructors();
1887 print_copy_assignment();
1888 print_destructor();
1889 print_ptr();
1890 print_downcast();
1891 print_ctx();
1892 print_method_separator();
1893 print_persistent_callbacks();
1894 print_methods();
1895 print_set_enums();
1896 print_special();
1897 }
1898
1899 /* Print the body of C function callback with the given indentation
1900 * that can be use as an argument to "param" for marshalling
1901 * the corresponding C++ callback.
1902 * The data structure that contains the C++ callback is of type
1903 * "prefix"_data.
1904 *
1905 * For a callback of the form
1906 *
1907 * isl_stat (*fn)(__isl_take isl_map *map, void *user)
1908 *
1909 * the following code is generated:
1910 *
1911 * auto *data = static_cast<struct <prefix>_data *>(arg_1);
1912 * ISL_CPP_TRY {
1913 * stat ret = (data->func)(manage(arg_0));
1914 * return isl_stat_ok;
1915 * } ISL_CPP_CATCH_ALL {
1916 * data->eptr = std::current_exception();
1917 * return isl_stat_error;
1918 * }
1919 *
1920 * If checked C++ bindings are being generated, then
1921 * generate the following code:
1922 *
1923 * auto *data = static_cast<struct <prefix>_data *>(arg_1);
1924 * stat ret = (data->func)(manage(arg_0));
1925 * return isl_stat(ret);
1926 */
print_callback_body(int indent,ParmVarDecl * param,const string & prefix)1927 void plain_cpp_generator::impl_printer::print_callback_body(int indent,
1928 ParmVarDecl *param, const string &prefix)
1929 {
1930 QualType ptype, rtype;
1931 string call, last_idx;
1932 const FunctionProtoType *callback;
1933 int num_params;
1934
1935 ptype = param->getType();
1936
1937 callback = extract_prototype(ptype);
1938 rtype = callback->getReturnType();
1939 num_params = callback->getNumArgs();
1940
1941 last_idx = ::to_string(num_params - 1);
1942
1943 call = "(data->func)(";
1944 for (long i = 0; i < num_params - 1; i++) {
1945 if (!generator.callback_takes_argument(param, i))
1946 call += "manage_copy";
1947 else
1948 call += "manage";
1949 call += "(arg_" + ::to_string(i) + ")";
1950 if (i != num_params - 2)
1951 call += ", ";
1952 }
1953 call += ")";
1954
1955 osprintf(os, indent,
1956 "auto *data = static_cast<struct %s_data *>(arg_%s);\n",
1957 prefix.c_str(), last_idx.c_str());
1958 print_wrapped_call(indent, call, rtype);
1959 }
1960
1961 /* Print the local variables that are needed for a callback argument,
1962 * in particular, print a lambda function that wraps the callback and
1963 * a pointer to the actual C++ callback function.
1964 *
1965 * For a callback of the form
1966 *
1967 * isl_stat (*fn)(__isl_take isl_map *map, void *user)
1968 *
1969 * the following lambda function is generated:
1970 *
1971 * auto fn_lambda = [](isl_map *arg_0, void *arg_1) -> isl_stat {
1972 * auto *data = static_cast<struct fn_data *>(arg_1);
1973 * try {
1974 * stat ret = (data->func)(manage(arg_0));
1975 * return isl_stat_ok;
1976 * } catch (...) {
1977 * data->eptr = std::current_exception();
1978 * return isl_stat_error;
1979 * }
1980 * };
1981 *
1982 * A copy of the std::function C++ callback function is stored in
1983 * a fn_data data structure for passing to the C callback function,
1984 * along with an std::exception_ptr that is used to store any
1985 * exceptions thrown in the C++ callback.
1986 *
1987 * struct fn_data {
1988 * std::function<stat(map)> func;
1989 * std::exception_ptr eptr;
1990 * } fn_data = { fn };
1991 *
1992 * This std::function object represents the actual user
1993 * callback function together with the locally captured state at the caller.
1994 *
1995 * The lambda function is expected to be used as a C callback function
1996 * where the lambda itself is provided as the function pointer and
1997 * where the user void pointer is a pointer to fn_data.
1998 * The std::function object is extracted from the pointer to fn_data
1999 * inside the lambda function.
2000 *
2001 * The std::exception_ptr object is not added to fn_data
2002 * if checked C++ bindings are being generated.
2003 * The body of the generated lambda function then is as follows:
2004 *
2005 * stat ret = (data->func)(manage(arg_0));
2006 * return isl_stat(ret);
2007 *
2008 * If the C callback does not take its arguments, then
2009 * manage_copy is used instead of manage.
2010 */
print_callback_local(ParmVarDecl * param)2011 void plain_cpp_generator::impl_printer::print_callback_local(ParmVarDecl *param)
2012 {
2013 string pname;
2014 QualType ptype, rtype;
2015 string c_args, cpp_args, rettype;
2016 const FunctionProtoType *callback;
2017
2018 pname = param->getName().str();
2019 ptype = param->getType();
2020
2021 c_args = generator.generate_callback_args(ptype, false);
2022
2023 callback = extract_prototype(ptype);
2024 rtype = callback->getReturnType();
2025 rettype = rtype.getAsString();
2026
2027 print_callback_data_decl(param, pname);
2028 osprintf(os, " %s_data = { %s };\n", pname.c_str(), pname.c_str());
2029 osprintf(os, " auto %s_lambda = [](%s) -> %s {\n",
2030 pname.c_str(), c_args.c_str(), rettype.c_str());
2031 print_callback_body(4, param, pname);
2032 osprintf(os, " };\n");
2033 }
2034
2035 /* Return the C++ counterpart to the isl_bool type.
2036 *
2037 * For the checked C++ bindings this is "boolean".
2038 */
isl_bool() const2039 std::string checked_cpp_type_printer::isl_bool() const
2040 {
2041 return "boolean";
2042 }
2043
2044 /* Return the C++ counterpart to the isl_bool type.
2045 *
2046 * Use the appropriate type printer.
2047 */
isl_bool2cpp()2048 string plain_cpp_generator::isl_bool2cpp()
2049 {
2050 return type_printer()->isl_bool();
2051 }
2052
2053 /* Return the C++ counterpart to the isl_stat type.
2054 *
2055 * For the checked C++ bindings this is "stat".
2056 */
isl_stat() const2057 string checked_cpp_type_printer::isl_stat() const
2058 {
2059 return "stat";
2060 }
2061
2062 /* Return the C++ counterpart to the isl_size type.
2063 *
2064 * For the checked C++ bindings this is "class size".
2065 */
isl_size() const2066 string checked_cpp_type_printer::isl_size() const
2067 {
2068 return "class size";
2069 }
2070
2071 /* Return the namespace of the generated C++ bindings.
2072 *
2073 * For the checked C++ bindings this is "isl::checked::".
2074 */
isl_namespace() const2075 std::string checked_cpp_type_printer::isl_namespace() const
2076 {
2077 return "isl::checked::";
2078 }
2079
2080 /* Return the namespace of the generated C++ bindings.
2081 *
2082 * Use the appropriate type printer.
2083 */
isl_namespace()2084 string plain_cpp_generator::isl_namespace()
2085 {
2086 return type_printer()->isl_namespace();
2087 }
2088
2089 /* Translate parameter or return type "type" to its C++ name counterpart.
2090 *
2091 * Use the appropriate type printer.
2092 * For the plain C++ interface, the argument position is irrelevant,
2093 * so simply pass in -1.
2094 */
param2cpp(QualType type)2095 string plain_cpp_generator::param2cpp(QualType type)
2096 {
2097 return type_printer()->param(-1, type);
2098 }
2099