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