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