xref: /netbsd-src/external/mit/isl/dist/interface/plain_cpp.cc (revision 5971e316fdea024efff6be8f03536623db06833e)
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 &copy_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