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 }