1 /**
2    Necessary boilerplate for C#/.NET.
3 
4    To wrap all functions/return/parameter types and struct/class definitions from
5    a list of modules, write this in a "main" module and generate mylib.{so,dll}:
6 
7    ------
8    mixin wrapCSharp("mylib", Modules("module1", "module2", ...));
9    ------
10  */
11 module autowrap.csharp.boilerplate;
12 
13 import autowrap.common;
14 import autowrap.reflection;
15 import autowrap.csharp;
16 
17 public struct OutputFileName {
18     string value;
19 }
20 
21 public string wrapCSharp(in Modules modules, OutputFileName outputFile, LibraryName libraryName, RootNamespace rootNamespace) @safe pure {
22     import std.format : format;
23     import std.algorithm: map;
24     import std.array: join;
25 
26     if(!__ctfe) return null;
27 
28     const modulesList = modules.value.map!(a => a.toString).join(", ");
29 
30     return q{
31         //Insert shared boilerplate code
32         %1$s
33 
34         import std.typecons;
35         import autowrap.csharp.boilerplate;
36         import autowrap.csharp.dlang : wrapDLang;
37 
38         immutable string t = wrapDLang!(%2$s);
39         mixin(t);
40         //pragma(msg, t); //Uncomment to see generated D interface code, useful for debugging.
41 
42         //Insert DllMain for Windows only.
43         version(Windows) {
44             %3$s
45         }
46 
47         void main() {
48             import std.stdio;
49             string generated = generateCSharp!(%2$s)(LibraryName("%4$s"), RootNamespace("%5$s"));
50             auto f = File("%6$s", "w");
51             f.writeln(generated);
52         }
53     }.format(commonBoilerplate(), modulesList, dllMainMixinStr(), libraryName.value, rootNamespace.value, outputFile.value);
54 }
55 
56 /**
57    Returns a string to be mixed in that defines the boilerplate code needed by all C# interfaces.
58  */
59 private string commonBoilerplate() @safe pure {
60     return q{
61         import core.memory;
62         import std.conv;
63         import std.string;
64         import std.traits;
65         import std.utf;
66 
67         extern(C) export struct returnValue(T) {
68             T value;
69             wstring error;
70 
71             this(T value) nothrow {
72                 this.value = value;
73                 static if (isArray!(T) || is(T == class) || is(T == interface)) {
74                     if (this.value !is null) {
75                         pinPointer(cast(void*)this.error.ptr);
76                     }
77                 }
78                 this.error = null;
79             }
80 
81             this(Exception error) nothrow {
82                 this.value = T.init;
83                 try {
84                     this.error = to!wstring(error.toString());
85                 } catch(Exception ex) {
86                     this.error = "Unhandled Exception while marshalling exception data. You should never see this error.";
87                 }
88                 pinPointer(cast(void*)this.error.ptr);
89             }
90         }
91 
92         extern(C) export struct returnVoid {
93             wstring error = null;
94 
95             this(Exception error) nothrow {
96                 try {
97                     this.error = to!wstring(error.toString());
98                 } catch(Exception ex) {
99                     this.error = "Unhandled Exception while marshalling exception data. You should never see this error.";
100                 }
101                 pinPointer(cast(void*)this.error.ptr);
102             }
103         }
104 
105         public void pinPointer(void* ptr) nothrow {
106             GC.setAttr(ptr, GC.BlkAttr.NO_MOVE);
107             GC.addRoot(ptr);
108         }
109 
110         extern(C) export void autowrap_csharp_release(void* ptr) nothrow {
111             GC.clrAttr(ptr, GC.BlkAttr.NO_MOVE);
112             GC.removeRoot(ptr);
113         }
114 
115         extern(C) export string autowrap_csharp_createString(wchar* str) nothrow {
116             string temp = toUTF8(to!wstring(str.fromStringz()));
117             pinPointer(cast(void*)temp.ptr);
118             return temp;
119         }
120 
121         extern(C) export wstring autowrap_csharp_createWString(wchar* str) nothrow {
122             wstring temp = toUTF16(to!wstring(str.fromStringz()));
123             pinPointer(cast(void*)temp.ptr);
124             return temp;
125         }
126 
127         extern(C) export dstring autowrap_csharp_createDString(wchar* str) nothrow {
128             dstring temp = toUTF32(to!wstring(str.fromStringz()));
129             pinPointer(cast(void*)temp.ptr);
130             return temp;
131         }
132     };
133 }