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