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.traits : Unqual; 22 import std.string; 23 import std.traits; 24 import std.utf; 25 26 private void pinInternalPointers(T)(ref T value) @trusted nothrow 27 if(is(T == struct)) 28 { 29 import std.traits : Fields, isPointer, isDynamicArray, isStaticArray; 30 31 static foreach(i, Field; Fields!T) 32 { 33 static assert(!isPointer!T, "If we're now supporting pointers, this code needs to be updated"); 34 static assert(!isStaticArray!T, "If we're now supporting static arrays, this code needs to be updated"); 35 36 static if(is(Field == struct)) 37 pinInternalPointers(value.tupleof[i]); 38 else static if(isDynamicArray!Field) 39 { 40 if(value.tupleof[i] !is null) 41 pinPointer(cast(void*)value.tupleof[i].ptr); 42 } 43 else static if(is(Field == class) || is(Field == interface)) 44 { 45 if(value.tupleof[i] !is null) 46 pinPointer(cast(void*)value.tupleof[i]); 47 } 48 } 49 } 50 51 extern(C) export struct returnValue(T) { 52 T value; 53 wstring error; 54 55 this(T value) nothrow { 56 this.value = value; 57 static if (isDynamicArray!T) 58 { 59 if (this.value !is null) 60 pinPointer(cast(void*)this.value.ptr); 61 } 62 else static if (is(T == class) || is(T == interface)) 63 { 64 if (this.value !is null) 65 pinPointer(cast(void*)this.value); 66 } 67 else static if (is(T == struct)) 68 pinInternalPointers(value); 69 } 70 71 this(Exception error) nothrow { 72 this.value = T.init; 73 try { 74 this.error = to!wstring(error.toString()); 75 } catch(Exception ex) { 76 this.error = "Unhandled Exception while marshalling exception data. You should never see this error."; 77 } 78 pinPointer(cast(void*)this.error.ptr); 79 } 80 } 81 82 extern(C) export struct returnVoid { 83 wstring error = null; 84 85 this(Exception error) nothrow { 86 try { 87 this.error = to!wstring(error.toString()); 88 } catch(Exception ex) { 89 this.error = "Unhandled Exception while marshalling exception data. You should never see this error."; 90 } 91 pinPointer(cast(void*)this.error.ptr); 92 } 93 } 94 95 extern(C) export struct datetime { 96 long ticks; 97 long offset; 98 99 public this(Duration value) { 100 this.ticks = value.total!"hnsecs"; 101 this.offset = 0; 102 } 103 104 public this(DateTime value) { 105 this.ticks = SysTime(value).stdTime; 106 this.offset = 0; 107 } 108 109 public this(Date value) { 110 this.ticks = SysTime(value).stdTime; 111 this.offset = 0; 112 } 113 114 public this(TimeOfDay value) { 115 import core.time : Duration, hours, minutes, seconds; 116 Duration t = hours(value.hour) + minutes(value.minute) + seconds(value.second); 117 this.ticks = t.total!"hnsecs"; 118 this.offset = 0; 119 } 120 121 public this(SysTime value) { 122 this.ticks = value.stdTime; 123 this.offset = value.utcOffset.total!"hnsecs"; 124 } 125 } 126 127 public void pinPointer(void* ptr) nothrow { 128 GC.setAttr(ptr, GC.BlkAttr.NO_MOVE); 129 GC.addRoot(ptr); 130 } 131 132 public datetime toDatetime(T)(T value) 133 if (isDateTimeType!T) { 134 return datetime(value); 135 } 136 137 public datetime[] toDatetime1DArray(T)(T[] value) 138 if (isDateTimeType!T) { 139 import std.algorithm : map; 140 import std.array : array; 141 return value.map!datetime.array; 142 } 143 144 public T fromDatetime(T)(datetime value) if (is(Unqual!T == SysTime)) { 145 return SysTime(value.ticks, cast(immutable)new SimpleTimeZone(hnsecs(value.offset))); 146 } 147 148 public T fromDatetime(T)(datetime value) if (is(Unqual!T == DateTime)) { 149 return cast(T)SysTime(value.ticks, LocalTime()); 150 } 151 152 public T fromDatetime(T)(datetime value) if (is(Unqual!T == Date) || is(Unqual!T == TimeOfDay)) { 153 return cast(T)SysTime(value.ticks, cast(immutable)new SimpleTimeZone(hnsecs(0))); 154 } 155 156 public T fromDatetime(T)(datetime value) if (is(Unqual!T == Duration)) { 157 return hnsecs(value.ticks); 158 } 159 160 public T[] fromDatetime1DArray(T)(datetime[] value) 161 if (isDateTimeType!T) { 162 import std.algorithm : map; 163 import std.array : array; 164 return value.map!(fromDatetime!T).array; 165 } 166 167 extern(C) void rt_moduleTlsCtor(); 168 extern(C) void rt_moduleTlsDtor(); 169 170 struct AttachThread 171 { 172 static create() nothrow 173 { 174 import core.thread : Thread, thread_attachThis; 175 176 typeof(this) retval; 177 178 retval.notAttached = Thread.getThis() is null; 179 180 if(retval.notAttached) 181 { 182 try 183 { 184 thread_attachThis(); 185 rt_moduleTlsCtor(); 186 } 187 catch(Exception) 188 assert(0, "An Exception shouldn't be possible here, but it happened anyway."); 189 } 190 191 return retval; 192 } 193 194 ~this() nothrow 195 { 196 import core.thread : thread_detachThis; 197 198 if(notAttached) 199 { 200 try 201 { 202 thread_detachThis(); 203 rt_moduleTlsDtor(); 204 } 205 catch(Exception) 206 assert(0, "An Exception shouldn't be possible here, but it happened anyway."); 207 } 208 } 209 210 private bool notAttached = false; 211 } 212 213 public string wrapCSharp(in Modules modules, OutputFileName outputFile, LibraryName libraryName, RootNamespace rootNamespace) @safe pure { 214 import std.format : format; 215 import std.algorithm: map; 216 import std.array: join; 217 import autowrap.common; 218 import autowrap.csharp.boilerplate; 219 220 if(!__ctfe) return null; 221 222 const modulesList = modules.value.map!(a => a.toString).join(", "); 223 224 return q{ 225 extern(C) export void autowrap_csharp_release(void* ptr) nothrow { 226 import core.memory : GC; 227 GC.clrAttr(ptr, GC.BlkAttr.NO_MOVE); 228 GC.removeRoot(ptr); 229 } 230 231 extern(C) export string autowrap_csharp_createString(wchar* str) nothrow { 232 import std.string : fromStringz; 233 import std.utf : toUTF8; 234 import autowrap.csharp.boilerplate : AttachThread, pinPointer; 235 236 auto attachThread = AttachThread.create(); 237 string temp = toUTF8(str.fromStringz()); 238 pinPointer(cast(void*)temp.ptr); 239 return temp; 240 } 241 242 extern(C) export wstring autowrap_csharp_createWString(wchar* str) nothrow { 243 import std.string : fromStringz; 244 import std.utf : toUTF16; 245 import autowrap.csharp.boilerplate : pinPointer; 246 247 auto attachThread = AttachThread.create(); 248 wstring temp = toUTF16(str.fromStringz()); 249 pinPointer(cast(void*)temp.ptr); 250 return temp; 251 } 252 253 extern(C) export dstring autowrap_csharp_createDString(wchar* str) nothrow { 254 import std.string : fromStringz; 255 import std.utf : toUTF32; 256 import autowrap.csharp.boilerplate : pinPointer; 257 258 auto attachThread = AttachThread.create(); 259 dstring temp = toUTF32(str.fromStringz()); 260 pinPointer(cast(void*)temp.ptr); 261 return temp; 262 } 263 264 static import autowrap.csharp.dlang; 265 mixin(autowrap.csharp.dlang.wrapDLang!(%1$s)); 266 267 //Insert DllMain for Windows only. 268 version(Windows) { 269 %2$s 270 } 271 272 void main() { 273 import std.stdio : File; 274 import autowrap.csharp.common : LibraryName, RootNamespace; 275 import autowrap.csharp.csharp : generateCSharp; 276 string generated = generateCSharp!(%1$s)(LibraryName("%3$s"), RootNamespace("%4$s")); 277 auto f = File("%5$s", "w"); 278 f.writeln(generated); 279 } 280 }.format(modulesList, dllMainMixinStr(), libraryName.value, rootNamespace.value, outputFile.value); 281 }