1 /// Written in the D programming language. 2 /// Date: 2015, Joakim Brännström 3 /// License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 4 /// Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 module dsrcgen.cpp; 6 7 import std.typecons : Flag, Yes, No; 8 9 public import dsrcgen.base; 10 public import dsrcgen.c; 11 12 @safe: 13 14 /// Mixin of methods for semantic representation of C++ in D. 15 mixin template CppModuleX(T) { 16 /** Access to self. 17 * 18 * Useful in with-statements. 19 */ 20 T _() { 21 return this; 22 } 23 24 // Statements 25 auto friend(string expr) { 26 return stmt("friend " ~ expr); 27 } 28 29 auto new_(string expr) { 30 return stmt("new " ~ expr); 31 } 32 33 auto delete_(string expr) { 34 return stmt("delete " ~ expr); 35 } 36 37 auto delete_array(string expr) { 38 return stmt("delete [] " ~ expr); 39 } 40 41 auto template_(string value) { 42 return stmt("template<" ~ value ~ ">")[$.end = ""]; 43 } 44 45 // Suites 46 /** Suites for C++ definitions for a class. 47 * Useful for implementiong ctor, dtor and member methods for a class. 48 * Params: 49 * class_name = name of the class. 50 * headline = whatever to append after class_name. 51 * Example: 52 * ---- 53 * class_suite("Simple", "Simple()"); 54 * ---- 55 * Generated code: 56 * ---- 57 * Simple::Simple() { 58 * } 59 * ---- 60 */ 61 auto class_suite(string class_name, string headline) { 62 import std.format : format; 63 64 auto tmp = format("%s::%s", class_name, headline); 65 auto e = suite(tmp, Yes.addSep); 66 return e; 67 } 68 69 auto class_suite(string rval, string class_name, string headline) { 70 import std.format : format; 71 72 auto tmp = format("%s %s::%s", rval, class_name, headline); 73 auto e = suite(tmp, Yes.addSep); 74 return e; 75 } 76 77 auto ctor(T...)(string class_name, auto ref T args) { 78 import std.format : format; 79 80 string params = paramsToString(args); 81 82 auto e = stmt(format("%s(%s)", class_name, params)); 83 return e; 84 } 85 86 auto ctor(string class_name) { 87 auto e = stmt(class_name ~ "()"); 88 return e; 89 } 90 91 auto ctor_body(T...)(string class_name, auto ref T args) { 92 import std.format : format; 93 94 string params = paramsToString(args); 95 96 auto e = class_suite(class_name, format("%s(%s)", class_name, params)); 97 return e; 98 } 99 100 auto ctor_body(string class_name) { 101 import std.format : format; 102 103 auto e = class_suite(class_name, format("%s()", class_name)); 104 return e; 105 } 106 107 auto ctor_initlist_body(T...)(string class_name, string[] init_list, auto ref T args) { 108 import std.algorithm : joiner; 109 import std.format : format; 110 111 string params = paramsToString(args); 112 113 auto e = class_suite(class_name, format("%s(%s) : %s", class_name, 114 params, init_list.joiner(", "))); 115 return e; 116 } 117 118 /** Virtual d'tor. 119 * Params: 120 * isVirtual = if evaluated to true prepend with virtual. 121 * class_name = name of the class to create a d'tor for. 122 * Example: 123 * ---- 124 * dtor(Yes.isVirtual, "Foo"); 125 * ---- 126 */ 127 auto dtor(Flag!"isVirtual" isVirtual, string class_name) { 128 import std.format : format; 129 130 auto e = stmt(format("%s%s%s()", isVirtual ? "virtual " : "", 131 class_name[0] == '~' ? "" : "~", class_name)); 132 return e; 133 } 134 135 auto dtor(string class_name) { 136 import std.format : format; 137 138 auto e = stmt(format("%s%s()", class_name[0] == '~' ? "" : "~", class_name)); 139 return e; 140 } 141 142 auto enum_class(string identifier) { 143 return suite("enum class " ~ identifier)[$.end = "};"]; 144 } 145 146 /// Definition for a dtor. 147 auto dtor_body(string class_name) { 148 import std.format : format; 149 150 string s = class_name; 151 if (s[0] == '~') { 152 s = s[1 .. $]; 153 } 154 auto e = class_suite(class_name, format("~%s()", s)); 155 return e; 156 } 157 158 auto namespace(string n) { 159 auto e = suite("namespace " ~ n)[$.end = "} //NS:" ~ n]; 160 return e; 161 } 162 163 auto class_(string n) { 164 auto e = suite("class " ~ n)[$.end = "};"]; 165 return e; 166 } 167 168 auto class_(string name, string inherit) { 169 import std.format : format; 170 171 if (inherit.length == 0) { 172 return class_(name); 173 } else { 174 auto e = suite(format("class %s : %s", name, inherit))[$.end = "};"]; 175 return e; 176 } 177 } 178 179 auto public_() { 180 auto e = suite("public:", No.addSep)[$.begin = "", $.end = ""]; 181 e.suppressThisIndent(1); 182 e.sep; 183 return e; 184 } 185 186 auto protected_() { 187 auto e = suite("protected:", No.addSep)[$.begin = "", $.end = ""]; 188 e.suppressThisIndent(1); 189 e.sep; 190 return e; 191 } 192 193 auto private_() { 194 auto e = suite("private:", No.addSep)[$.begin = "", $.end = ""]; 195 e.suppressThisIndent(1); 196 e.sep; 197 return e; 198 } 199 200 auto method(Flag!"isVirtual" isVirtual, string return_type, string name, 201 Flag!"isConst" isConst) { 202 import std.format : format; 203 204 auto e = stmt(format("%s%s %s()%s", isVirtual ? "virtual " : "", 205 return_type, name, isConst ? " const" : "")); 206 return e; 207 } 208 209 auto method(T...)(Flag!"isVirtual" isVirtual, string return_type, 210 string name, Flag!"isConst" isConst, auto ref T args) { 211 import std.format : format; 212 213 string params = paramsToString(args); 214 215 auto e = stmt(format("%s%s %s(%s)%s", isVirtual ? "virtual " : "", 216 return_type, name, params, isConst ? " const" : "")); 217 return e; 218 } 219 220 auto method_body(string return_type, string class_name, string name, Flag!"isConst" isConst) { 221 import std.format : format; 222 223 auto e = class_suite(return_type, class_name, format("%s()%s", name, 224 isConst ? " const" : "")); 225 return e; 226 } 227 228 auto method_body(T...)(string return_type, string class_name, string name, 229 Flag!"isConst" isConst, auto ref T args) { 230 import std.format : format; 231 232 string params = paramsToString(args); 233 234 auto e = class_suite(return_type, class_name, format("%s(%s)%s", name, 235 params, isConst ? " const" : "")); 236 return e; 237 } 238 239 auto method_inline(Flag!"isVirtual" isVirtual, string return_type, 240 string name, Flag!"isConst" isConst) { 241 import std.format : format; 242 243 auto e = suite(format("%s%s %s()%s", isVirtual ? "virtual " : "", 244 return_type, name, isConst ? " const" : "")); 245 return e; 246 } 247 248 auto method_inline(T...)(Flag!"isVirtual" isVirtual, string return_type, 249 string name, Flag!"isConst" isConst, auto ref T args) { 250 import std.format : format; 251 252 string params = paramsToString(args); 253 254 auto e = suite(format("%s%s %s(%s)%s", isVirtual ? "virtual " : "", 255 return_type, name, params, isConst ? " const" : "")); 256 return e; 257 } 258 } 259 260 /// Represent a semantic item in C++ source. 261 class CppModule : BaseModule { 262 mixin CModuleX!(CppModule); 263 mixin CppModuleX!(CppModule); 264 } 265 266 /** Code structure for generation of a C++ header. 267 * 268 * The content is structed as: 269 * doc 270 * header 271 * ifdef_guardbegin 272 * content 273 * ifdef_guard end 274 * 275 * Note that the indent is suppressed. 276 */ 277 struct CppHModule { 278 /// Document root. 279 CppModule doc; 280 /// Usually a copyright header. 281 CppModule header; 282 /// Main code content. 283 CppModule content; 284 285 /** 286 * Params: 287 * ifdef_guard = guard statement. 288 */ 289 this(string ifdef_guard) { 290 // Must suppress indentation to generate what is expected by the user. 291 doc = new CppModule; 292 with (doc) { 293 // doc is a container of the modules so should not affect indent. 294 // header, content and footer is containers so should not affect indent. 295 // ifndef guard usually never affect indent. 296 suppressIndent(1); 297 header = base; 298 header.suppressIndent(1); 299 with (IFNDEF(ifdef_guard)) { 300 define(ifdef_guard); 301 content = base; 302 content.suppressIndent(1); 303 } 304 } 305 } 306 307 /// Render the content as a string. 308 auto render() { 309 return doc.render(); 310 } 311 } 312 313 /** Template expressions in C++. 314 * 315 * Convenient when using templates. 316 * 317 * a = Et("static_cast")("char*"); 318 * b = a("foo"); // static_cast<char*>(foo); 319 * c = a("bar"); // static_cast<char*>(bar); 320 * 321 * v = Et("vector")("int"); 322 * v0 = v ~ E("foo"); // vector<int> foo; 323 * v1 = v("bar"); // vector<int>(bar); 324 */ 325 struct Et { 326 import dsrcgen.c : E; 327 import std.conv : to; 328 import std.string : format; 329 import std.traits : isSomeString; 330 331 pure: 332 private string tmpl; 333 334 /** Template with parameters parameters. 335 * Example: 336 * 'static_cast'<int>' 337 * | ----------|----- 338 * |template |params 339 */ 340 static struct Ett { 341 private string tmpl_; 342 private string params; 343 344 /// Represent a template. 345 this(string tmpl_, string params) pure nothrow { 346 this.tmpl_ = tmpl_; 347 this.params = params; 348 } 349 350 /// Represent a template with multiple parameters. 351 this(T...)(string tmpl_, auto ref T args) pure nothrow if (args.length > 1) { 352 this.tmpl_ = tmpl_; 353 this.params = paramsToString(args); 354 } 355 356 /// Represent the semantic meaning of Identifier(..) as text. 357 auto opCall(T...)(auto ref T args) pure const nothrow { 358 return E(this.toString)(paramsToString(args)); 359 } 360 361 /** String representation. 362 * Implicit. 363 */ 364 @property string toString() pure const nothrow { 365 return tmpl_ ~ "<" ~ params ~ ">"; 366 } 367 368 alias toString this; 369 370 /** String representation. 371 * Explicit. 372 */ 373 T opCast(T : string)() pure const nothrow { 374 return tmpl_ ~ "<" ~ params ~ ">"; 375 } 376 377 /// Only handles the concatenation operator "~". 378 auto opBinary(string op, T)(in T rhs) pure const nothrow { 379 static if (op == "~" && is(T == E)) { 380 return E(toString() ~ " " ~ rhs.toString); 381 } else static if (op == "~") { 382 return E(toString() ~ " " ~ to!string(rhs)); 383 } else { 384 static assert(0, "Operator " ~ op ~ " not implemented"); 385 } 386 } 387 } 388 389 /// Straight copy of parameter tmpl. 390 this(T)(T tmpl) pure nothrow if (isSomeString!T) { 391 this.tmpl = tmpl; 392 } 393 394 /// Convert parameter tmpl to string representation. 395 this(T)(T tmpl) pure nothrow if (!isSomeString!T) { 396 this.tmpl = to!string(tmpl); 397 } 398 399 /// Represent the semantic meaning of "template name"("params"). 400 auto opCall(T...)(auto ref T params) pure const nothrow { 401 return Ett(tmpl, params); 402 } 403 } 404 405 @("Test of C++ suits") 406 unittest { 407 string expect = " 408 namespace foo { 409 } //NS:foo 410 class Foo { 411 public: 412 Foo(); 413 Foo(int y); 414 ~Foo(); 415 virtual ~Foo(); 416 }; 417 class Foo : Bar { 418 }; 419 public: 420 return 5; 421 protected: 422 return 7; 423 private: 424 return 8; 425 "; 426 auto x = new CppModule(); 427 with (x) { 428 sep; 429 namespace("foo"); 430 with (class_("Foo")) { 431 public_; 432 auto ctor0 = ctor("Foo"); 433 auto ctor1 = ctor("Foo", "int y"); 434 auto dtor0 = dtor("Foo"); 435 auto dtor1 = dtor(Yes.isVirtual, "Foo"); 436 } 437 class_("Foo", "Bar"); 438 with (public_) { 439 return_(E(5)); 440 } 441 with (protected_) { 442 return_(E(7)); 443 } 444 with (private_) { 445 return_(E(8)); 446 } 447 } 448 449 auto rval = x.render(); 450 assert(rval == expect, rval); 451 } 452 453 @("Test new and delete") 454 unittest { 455 auto expect = " new foo; 456 delete bar; 457 delete [] wart; 458 "; 459 460 auto x = new CppModule; 461 with (x) { 462 new_("foo"); 463 delete_("bar"); 464 delete_array("wart"); 465 } 466 467 auto r = x.render; 468 assert(expect == r, r); 469 } 470 471 @("Test Et composition") 472 unittest { 473 auto m = new CppModule; 474 m.suppressIndent(1); 475 476 auto expect = "static_cast<char*>(foo); 477 static_cast<char*>(bar); 478 pair<int, bool>(3, true); 479 "; 480 auto a = Et("static_cast")("char*"); 481 m.stmt(a("foo")); 482 m.stmt(a("bar")); 483 m.stmt(Et("pair")("int", "bool")(3, true)); 484 assert(expect == m.render, m.render); 485 486 m = new CppModule; 487 m.suppressIndent(1); 488 expect = "vector<int> foo; 489 vector<int>(bar); 490 "; 491 auto v = Et("vector")("int"); 492 m.stmt(v ~ E("foo")); 493 m.stmt(v("bar")); 494 assert(expect == m.render, m.render); 495 } 496 497 @("should generate an inlined class method") 498 unittest { 499 auto expect = " void foo() { 500 } 501 void bar(int foo) { 502 } 503 "; 504 505 auto m = new CppModule; 506 m.method_inline(No.isVirtual, "void", "foo", No.isConst); 507 m.method_inline(No.isVirtual, "void", "bar", No.isConst, "int foo"); 508 509 assert(expect == m.render, m.render); 510 } 511 512 @("shall be a enum class") 513 unittest { 514 auto expect = " enum class A { 515 L0, 516 } 517 "; 518 519 auto m = new CppModule; 520 with (m.enum_class("X")) { 521 enum_const("L0"); 522 } 523 } 524 525 @("shall be a ctor with an initialization list") 526 unittest { 527 auto expect = "Class::Class(int x) : bar(), bum() { 528 }"; 529 530 auto m = new CppModule; 531 m.ctor_initlist_body("Class", ["bar()", "bum()"], "int x"); 532 }