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 import core.time;
18 import core.memory;
19 import std.conv;
20 import std.datetime : DateTime, SysTime, Date, TimeOfDay, SimpleTimeZone, LocalTime;
21 import std.meta : Unqual;
22 import std.string;
23 import std.traits;
24 import std.utf;
25 
26 extern(C) export struct returnValue(T) {
27     T value;
28     wstring error;
29 
30     this(T value) nothrow {
31         this.value = value;
32         static if (isArray!(T) || is(T == class) || is(T == interface)) {
33             if (this.value !is null) {
34                 pinPointer(cast(void*)this.error.ptr);
35             }
36         }
37         this.error = null;
38     }
39 
40     this(Exception error) nothrow {
41         this.value = T.init;
42         try {
43             this.error = to!wstring(error.toString());
44         } catch(Exception ex) {
45             this.error = "Unhandled Exception while marshalling exception data. You should never see this error.";
46         }
47         pinPointer(cast(void*)this.error.ptr);
48     }
49 }
50 
51 extern(C) export struct returnVoid {
52     wstring error = null;
53 
54     this(Exception error) nothrow {
55         try {
56             this.error = to!wstring(error.toString());
57         } catch(Exception ex) {
58             this.error = "Unhandled Exception while marshalling exception data. You should never see this error.";
59         }
60         pinPointer(cast(void*)this.error.ptr);
61     }
62 }
63 
64 extern(C) export struct datetime {
65     long ticks;
66     long offset;
67 
68     public this(Duration value) {
69         this.ticks = value.total!"hnsecs";
70         this.offset = 0;
71     }
72 
73     public this(DateTime value) {
74         this.ticks = SysTime(value).stdTime;
75         this.offset = 0;
76     }
77 
78     public this(Date value) {
79         this.ticks = SysTime(value).stdTime;
80         this.offset = 0;
81     }
82 
83     public this(TimeOfDay value) {
84         import core.time : Duration, hours, minutes, seconds;
85         Duration t = hours(value.hour) + minutes(value.minute) + seconds(value.second);
86         this.ticks = t.total!"hnsecs";
87         this.offset = 0;
88     }
89 
90     public this(SysTime value) {
91         this.ticks = value.stdTime;
92         this.offset = value.utcOffset.total!"hnsecs";
93     }
94 }
95 
96 public void pinPointer(void* ptr) nothrow {
97     GC.setAttr(ptr, GC.BlkAttr.NO_MOVE);
98     GC.addRoot(ptr);
99 }
100 
101 public datetime toDatetime(T)(T value)
102 if (isDateTimeType!T) {
103     return datetime(value);
104 }
105 
106 public datetime[] toDatetime1DArray(T)(T[] value)
107 if (isDateTimeType!T) {
108     import std.algorithm : map;
109     import std.array : array;
110     return value.map!datetime.array;
111 }
112 
113 public T fromDatetime(T)(datetime value) if (is(Unqual!T == SysTime)) {
114     return SysTime(value.ticks, cast(immutable)new SimpleTimeZone(hnsecs(value.offset)));
115 }
116 
117 public T fromDatetime(T)(datetime value) if (is(Unqual!T == DateTime)) {
118     return cast(T)SysTime(value.ticks, LocalTime());
119 }
120 
121 public T fromDatetime(T)(datetime value) if (is(Unqual!T == Date) || is(Unqual!T == TimeOfDay)) {
122     return cast(T)SysTime(value.ticks, cast(immutable)new SimpleTimeZone(hnsecs(0)));
123 }
124 
125 public T fromDatetime(T)(datetime value) if (is(Unqual!T == Duration)) {
126     return hnsecs(value.ticks);
127 }
128 
129 public T[] fromDatetime1DArray(T)(datetime[] value)
130 if (isDateTimeType!T) {
131     import std.algorithm : map;
132     import std.array : array;
133     return value.map!(fromDatetime!T).array;
134 }
135 
136 public string wrapCSharp(in Modules modules, OutputFileName outputFile, LibraryName libraryName, RootNamespace rootNamespace) @safe pure {
137     import std.format : format;
138     import std.algorithm: map;
139     import std.array: join;
140     import autowrap.common;
141     import autowrap.csharp.boilerplate;
142 
143     if(!__ctfe) return null;
144 
145     const modulesList = modules.value.map!(a => a.toString).join(", ");
146 
147     return q{
148         import core.memory : GC;
149         import std.conv : to;
150         import std.utf : toUTF8, toUTF16, toUTF32;
151         import std.typecons;
152         import std.string : fromStringz;
153         import autowrap.csharp.boilerplate;
154         import autowrap.csharp.dlang : wrapDLang;
155 
156         extern(C) export void autowrap_csharp_release(void* ptr) nothrow {
157             GC.clrAttr(ptr, GC.BlkAttr.NO_MOVE);
158             GC.removeRoot(ptr);
159         }
160 
161         extern(C) export string autowrap_csharp_createString(wchar* str) nothrow {
162             string temp = toUTF8(to!wstring(str.fromStringz()));
163             pinPointer(cast(void*)temp.ptr);
164             return temp;
165         }
166 
167         extern(C) export wstring autowrap_csharp_createWString(wchar* str) nothrow {
168             wstring temp = toUTF16(to!wstring(str.fromStringz()));
169             pinPointer(cast(void*)temp.ptr);
170             return temp;
171         }
172 
173         extern(C) export dstring autowrap_csharp_createDString(wchar* str) nothrow {
174             dstring temp = toUTF32(to!wstring(str.fromStringz()));
175             pinPointer(cast(void*)temp.ptr);
176             return temp;
177         }
178 
179         mixin(wrapDLang!(%1$s));
180 
181         //Insert DllMain for Windows only.
182         version(Windows) {
183             %2$s
184         }
185 
186         void main() {
187             import std.stdio;
188             string generated = generateCSharp!(%1$s)(LibraryName("%3$s"), RootNamespace("%4$s"));
189             auto f = File("%5$s", "w");
190             f.writeln(generated);
191         }
192     }.format(modulesList, dllMainMixinStr(), libraryName.value, rootNamespace.value, outputFile.value);
193 }