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