brintos

brintos / llvm-project-archived public Read only

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