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 }