1 module autowrap.csharp.csharp;
2 
3 import scriptlike : interp, _interp_text;
4 
5 import autowrap.csharp.common : LibraryName, RootNamespace, getDLangInterfaceType, OutputFileName;
6 import autowrap.reflection : isModule, Modules;
7 
8 import std.ascii : newline;
9 import std.meta: allSatisfy;
10 import std.string : format;
11 
12 private string[string] returnTypes;
13 private CSharpNamespace[string] namespaces;
14 private csharpRange rangeDef;
15 
16 enum string voidTypeString = "void";
17 enum string stringTypeString = "string";
18 enum string wstringTypeString = "wstring";
19 enum string dstringTypeString = "dstring";
20 enum string boolTypeString = "bool";
21 enum string dateTimeTypeString = "std.datetime.date.DateTime";
22 enum string sysTimeTypeString = "std.datetime.systime.SysTime";
23 enum string dateTypeString = "std.datetime.date.Date";
24 enum string timeOfDayTypeString = "std.datetime.date.TimeOfDay";
25 enum string durationTypeString = "core.time.Duration";
26 enum string uuidTypeString = "UUID";
27 enum string charTypeString = "char";
28 enum string wcharTypeString = "wchar";
29 enum string dcharTypeString = "dchar";
30 enum string ubyteTypeString = "ubyte";
31 enum string byteTypeString = "byte";
32 enum string ushortTypeString = "ushort";
33 enum string shortTypeString = "short";
34 enum string uintTypeString = "uint";
35 enum string intTypeString = "int";
36 enum string ulongTypeString = "ulong";
37 enum string longTypeString = "long";
38 enum string floatTypeString = "float";
39 enum string doubleTypeString = "double";
40 enum string sliceTypeString = "slice";
41 
42 enum string dllImportString = "        [DllImport(\"%1$s\", EntryPoint = \"%2$s\", CallingConvention = CallingConvention.Cdecl)]" ~ newline;
43 enum string externFuncString = "        internal static extern %1$s %2$s(%3$s);" ~ newline;
44 
45 enum int fileReservationSize = 33_554_432;
46 enum int aggregateReservationSize = 32_768;
47 
48 //Class used for language built-in reference semantics.
49 private final class CSharpNamespace {
50     public string namespace;
51     public string functions;
52     public CSharpAggregate[string] aggregates;
53 
54     public this(string ns) {
55         namespace = ns;
56         functions = string.init;
57     }
58 
59     public override string toString() {
60         string ret;
61         foreach(csns; namespaces.byValue()) {
62             ret ~= "namespace " ~ csns.namespace ~ " {
63     using System;
64     using System.CodeDom.Compiler;
65     using System.Collections.Generic;
66     using System.Linq;
67     using System.Reflection;
68     using System.Runtime.InteropServices;
69     using Autowrap;
70 
71     public static class Functions {
72 " ~ csns.functions ~ "
73     }" ~ newline ~ newline;
74             foreach(agg; aggregates.byValue()) {
75                 ret ~= agg.toString();
76             }
77             ret ~= "}" ~ newline;
78         }
79         return cast(immutable)ret;
80     }
81 }
82 
83 //Class used for language built-in reference semantics.
84 private final class CSharpAggregate {
85     public string name;
86     public bool isStruct;
87     public string constructors;
88     public string functions;
89     public string methods;
90     public string properties;
91 
92     public this (string name, bool isStruct) {
93         this.name = name;
94         this.isStruct = isStruct;
95         this.constructors = string.init;
96         this.functions = string.init;
97         this.methods = string.init;
98         this.properties = string.init;
99     }
100 
101     public override string toString() {
102         import autowrap.csharp.common : camelToPascalCase;
103 
104         char[] ret;
105         ret.reserve(aggregateReservationSize);
106         ret ~= "    [GeneratedCodeAttribute(\"Autowrap\", \"1.0.0.0\")]" ~ newline;
107         if (isStruct) {
108             ret ~= "    [StructLayout(LayoutKind.Sequential)]" ~ newline;
109             ret ~= mixin(interp!"    public struct ${camelToPascalCase(this.name)} {${newline}");
110         } else {
111             ret ~= mixin(interp!"    public class ${camelToPascalCase(this.name)} : DLangObject {${newline}");
112             ret ~= mixin(interp!"        public static implicit operator IntPtr(${camelToPascalCase(this.name)} ret) { return ret.DLangPointer; }${newline}");
113             ret ~= mixin(interp!"        public static implicit operator ${camelToPascalCase(this.name)}(IntPtr ret) { return new ${camelToPascalCase(this.name)}(ret); }${newline}");
114         }
115         if (functions != string.init) ret ~= functions ~ newline;
116         if (constructors != string.init) ret ~= constructors ~ newline;
117         if (methods != string.init) ret ~= methods ~ newline;
118         if (properties != string.init) ret ~= properties;
119         ret ~= "    }" ~ newline ~ newline;
120         return cast(immutable)ret;
121     }
122 }
123 
124 public struct csharpRange {
125     public string constructors = string.init;
126     public string enumerators = string.init;
127     public string functions = string.init;
128     public string getters = string.init;
129     public string setters = string.init; 
130     public string appendValues = string.init;
131     public string appendArrays = string.init;
132     public string sliceEnd = string.init;
133     public string sliceRange = string.init;
134 
135     public string toString() {
136         immutable string boilerplate = import("Ranges.cs");
137         return boilerplate.format(functions, constructors, getters, setters, sliceEnd, sliceRange, appendValues, appendArrays, enumerators);
138     }
139 }
140 
141 public string generateCSharp(Modules...)(LibraryName libraryName, RootNamespace rootNamespace) if(allSatisfy!(isModule, Modules)) {
142     import core.time : Duration;
143     import autowrap.csharp.common : isDateTimeType;
144     import autowrap.reflection : AllAggregates;
145 
146     generateSliceBoilerplate(libraryName.value);
147 
148     alias aggregates = AllAggregates!Modules;
149     static foreach(agg; aggregates) {
150         static if (!(isDateTimeType!agg)) {
151             generateRangeDef!agg(libraryName.value);
152             generateConstructors!agg(libraryName.value);
153             generateMethods!agg(libraryName.value);
154             generateProperties!agg(libraryName.value);
155             generateFields!agg(libraryName.value);
156         }
157     }
158 
159     generateFunctions!Modules(libraryName.value);
160 
161     char[] ret;
162     ret.reserve(fileReservationSize);
163     foreach(csns; namespaces.byValue()) {
164         ret ~= csns.toString();
165     }
166 
167     ret ~= newline ~ writeCSharpBoilerplate(libraryName.value, rootNamespace.value);
168 
169     return cast(immutable)ret;
170 }
171 
172 private void generateConstructors(T)(string libraryName) if (is(T == class) || is(T == struct)) {
173     import autowrap.csharp.common : getDLangInterfaceName;
174     import std.traits : moduleName, fullyQualifiedName, hasMember, Parameters, ParameterIdentifierTuple;
175     import std.meta: AliasSeq;
176     import std.algorithm : among;
177 
178     alias fqn = fullyQualifiedName!T;
179     const string aggName = __traits(identifier, T);
180     CSharpAggregate csagg = getAggregate(getCSharpName(moduleName!T), getCSharpName(aggName), !is(T == class));
181 
182     static if(hasMember!(T, "__ctor") && __traits(getProtection, __traits(getMember, T, "__ctor")).among("export", "public")) {
183         alias constructors = AliasSeq!(__traits(getOverloads, T, "__ctor"));
184     } else {
185         alias constructors = AliasSeq!();
186     }
187 
188     //Generate constructor methods
189     foreach(c; constructors) {
190         alias paramNames = ParameterIdentifierTuple!c;
191         alias paramTypes = Parameters!c;
192         const string interfaceName = getDLangInterfaceName(fqn, "__ctor");
193         const string methodName = getCSharpMethodInterfaceName(aggName, "__ctor");
194         string ctor = dllImportString.format(libraryName, interfaceName);
195         ctor ~= mixin(interp!"        private static extern ${getDLangReturnType(aggName, is(T == class))} dlang_${methodName}(");
196         static foreach(pc; 0..paramNames.length) {
197             if (is(paramTypes[pc] == class)) {
198                 ctor ~= mixin(interp!"IntPtr ${paramNames[pc]}, ");
199             } else {
200                 ctor ~= mixin(interp!"${is(paramTypes[pc] == bool) ? \"[MarshalAs(UnmanagedType.Bool)]\" : string.init}${getDLangInterfaceType(fullyQualifiedName!(paramTypes[pc]))} ${paramNames[pc]}, ");
201             }
202         }
203         if (paramNames.length > 0) {
204             ctor = ctor[0..$-2];
205         }
206         ctor ~= ");" ~ newline;
207         ctor ~= mixin(interp!"        public ${getCSharpName(aggName)}(");
208         static foreach(pc; 0..paramNames.length) {
209             ctor ~= mixin(interp!"${getCSharpInterfaceType(fullyQualifiedName!(paramTypes[pc]))} ${paramNames[pc]}, ");
210         }
211         if (paramNames.length > 0) {
212             ctor = ctor[0..$-2];
213         }
214         if (is(T == class)) {
215             ctor ~= mixin(interp!") : base(dlang_${methodName}(");
216             static foreach(pc; 0..paramNames.length) {
217                 static if (is(paramTypes[pc] == string)) {
218                     ctor ~= mixin(interp!"SharedFunctions.CreateString(${paramNames[pc]}), ");
219                 } else static if (is(paramTypes[pc] == wstring)) {
220                     ctor ~= mixin(interp!"SharedFunctions.CreateWString(${paramNames[pc]}), ");
221                 } else static if (is(paramTypes[pc] == dstring)) {
222                     ctor ~= mixin(interp!"SharedFunctions.CreateDString(${paramNames[pc]}), ");
223                 } else {
224                     ctor ~= mixin(interp!"${paramNames[pc]}, ");
225                 }
226             }
227             if (paramNames.length > 0) {
228                 ctor = ctor[0..$-2];
229             }
230             ctor ~= ")";
231         }
232         ctor ~= ") { }" ~ newline;
233         csagg.constructors ~= ctor;
234     }
235     if (is(T == class)) {
236         csagg.constructors ~= mixin(interp!"        internal ${getCSharpName(aggName)}(IntPtr ptr) : base(ptr) { }${newline}");
237     }
238 }
239 
240 private void generateMethods(T)(string libraryName) if (is(T == class) || is(T == interface) || is(T == struct)) {
241     import autowrap.csharp.common : camelToPascalCase, getDLangInterfaceName;
242     import std.traits : moduleName, isArray, fullyQualifiedName, isFunction, functionAttributes, FunctionAttribute, ReturnType, Parameters, ParameterIdentifierTuple;
243     import std.conv : to;
244 
245     alias fqn = fullyQualifiedName!T;
246     const string aggName = __traits(identifier, T);
247     CSharpAggregate csagg = getAggregate(getCSharpName(moduleName!T), getCSharpName(aggName), !is(T == class));
248 
249     foreach(m; __traits(allMembers, T)) {
250         if (m == "__ctor" || m == "toHash" || m == "opEquals" || m == "opCmp" || m == "factory") {
251             continue;
252         }
253         const string methodName = camelToPascalCase(cast(string)m);
254         const string methodInterfaceName = getCSharpMethodInterfaceName(aggName, cast(string)m);
255 
256         static if (is(typeof(__traits(getMember, T, m)))) {
257             foreach(oc, mo; __traits(getOverloads, T, m)) {
258                 static if(isFunction!mo) {
259                     string exp = string.init;
260                     enum bool isProperty = cast(bool)(functionAttributes!mo & FunctionAttribute.property);
261                     alias returnType = ReturnType!mo;
262                     alias returnTypeStr = fullyQualifiedName!returnType;
263                     alias paramTypes = Parameters!mo;
264                     alias paramNames = ParameterIdentifierTuple!mo;
265                     const string interfaceName = getDLangInterfaceName(fqn, m) ~ to!string(oc);
266 
267                     exp ~= dllImportString.format(libraryName, interfaceName);
268                     exp ~= "        private static extern ";
269                     if (!is(returnType == void)) {
270                         exp ~= getDLangReturnType(returnTypeStr, is(returnType == class));
271                     } else {
272                         exp ~= "return_void_error";
273                     }
274                     exp ~= mixin(interp!" dlang_${methodInterfaceName}(");
275                     if (is(T == struct)) {
276                         exp ~= mixin(interp!"ref ${getDLangInterfaceType(fqn)} __obj__, ");
277                     } else if (is(T == class) || is(T == interface)) {
278                         exp ~= "IntPtr __obj__, ";
279                     }
280                     static foreach(pc; 0..paramNames.length) {
281                         if (is(paramTypes[pc] == class)) {
282                             exp ~= mixin(interp!"IntPtr ${paramNames[pc]}, ");
283                         } else {
284                             exp ~= mixin(interp!"${getDLangInterfaceType(fullyQualifiedName!(paramTypes[pc]))} ${paramNames[pc]}, ");
285                         }
286                     }
287                     exp = exp[0..$-2];
288                     exp ~= ");" ~ newline;
289                     if (!isProperty) {
290                         exp ~= mixin(interp!"        public ${methodName == \"ToString\" ? \"override \" : string.init}${getCSharpInterfaceType(returnTypeStr)} ${methodName}(");
291                         static foreach(pc; 0..paramNames.length) {
292                             if (is(paramTypes[pc] == string) || is(paramTypes[pc] == wstring) || is(paramTypes[pc] == dstring)) {
293                                 exp ~= mixin(interp!"${getCSharpInterfaceType(fullyQualifiedName!(paramTypes[pc]))} ${paramNames[pc]}, ");
294                             } else if (isArray!(paramTypes[pc])) {
295                                 exp ~= mixin(interp!"Range<${getCSharpInterfaceType(fullyQualifiedName!(paramTypes[pc]))}> ${paramNames[pc]}, ");
296                             } else {
297                                 exp ~= mixin(interp!"${getCSharpInterfaceType(fullyQualifiedName!(paramTypes[pc]))} ${paramNames[pc]}, ");
298                             }
299                         }
300                         if (paramNames.length > 0) {
301                             exp = exp[0..$-2];
302                         }
303                         exp ~= ") {" ~ newline;
304                         exp ~= mixin(interp!"            var dlang_ret = dlang_${methodInterfaceName}(${is(T == struct) ? \"ref \" : string.init}this, ");
305                         static foreach(pc; 0..paramNames.length) {
306                             static if (is(paramTypes[pc] == string)) {
307                                 exp ~= mixin(interp!"SharedFunctions.CreateString(${paramNames[pc]}), ");
308                             } else static if (is(paramTypes[pc] == wstring)) {
309                                 exp ~= mixin(interp!"SharedFunctions.CreateWstring(${paramNames[pc]}), ");
310                             } else static if (is(paramTypes[pc] == dstring)) {
311                                 exp ~= mixin(interp!"SharedFunctions.CreateDstring(${paramNames[pc]}), ");
312                             } else {
313                                 exp ~= paramNames[pc] ~ ", ";
314                             }
315                         }
316                         exp = exp[0..$-2];
317                         exp ~= ");" ~ newline;
318                         if (!is(returnType == void)) {
319                             if (is(returnType == string)) {
320                                 exp ~= "            return SharedFunctions.SliceToString(dlang_ret, DStringType._string);" ~ newline;
321                             } else if (is(returnType == wstring)) {
322                                 exp ~= "            return SharedFunctions.SliceToString(dlang_ret, DStringType._wstring);" ~ newline;
323                             } else if (is(returnType == dstring)) {
324                                 exp ~= "            return SharedFunctions.SliceToString(dlang_ret, DStringType._dstring);" ~ newline;
325                             } else {
326                                 exp ~= "            return dlang_ret;" ~ newline;
327                             }
328                         }
329                         exp ~= "        }" ~ newline;
330                     }
331                     csagg.methods ~= exp;
332                 }
333             }
334         }
335     }
336 }
337 
338 private void generateProperties(T)(string libraryName) if (is(T == class) || is(T == interface) || is(T == struct)) {
339     import autowrap.csharp.common : camelToPascalCase;
340     import std.traits : moduleName, isArray, fullyQualifiedName, functionAttributes, FunctionAttribute, ReturnType, Parameters;
341 
342     alias fqn = fullyQualifiedName!T;
343     const string aggName = __traits(identifier, T);
344     CSharpAggregate csagg = getAggregate(getCSharpName(moduleName!T), getCSharpName(aggName), !is(T == class));
345 
346     foreach(m; __traits(allMembers, T)) {
347         if (m == "__ctor" || m == "toHash" || m == "opEquals" || m == "opCmp" || m == "factory") {
348             continue;
349         }
350         const string methodName = camelToPascalCase(cast(string)m);
351         const string methodInterfaceName = getCSharpMethodInterfaceName(aggName, cast(string)m);
352 
353         static if (is(typeof(__traits(getMember, T, m)))) {
354             const olc = __traits(getOverloads, T, m).length;
355             static if(olc > 0 && olc <= 2) {
356                 bool isProperty = false;
357                 bool propertyGet = false;
358                 bool propertySet = false;
359                 foreach(mo; __traits(getOverloads, T, m)) {
360                     static if (cast(bool)(functionAttributes!mo & FunctionAttribute.property)) {
361                         isProperty = true;
362                         alias returnType = ReturnType!mo;
363                         alias paramTypes = Parameters!mo;
364                         if (paramTypes.length == 0) {
365                             propertyGet = true;
366                         } else {
367                             propertySet = true;
368                         }
369                     }
370                 }
371 
372                 if (isProperty) {
373                     string prop = string.init;
374                     alias propertyType = ReturnType!(__traits(getOverloads, T, m)[0]);
375                     if (is(propertyType == string) || is(propertyType == wstring) || is(propertyType == dstring)) {
376                         prop = mixin(interp!"        public string ${methodName} { ");
377                     } else if (isArray!(propertyType)) {
378                         prop = mixin(interp!"        public Range<${getCSharpInterfaceType(fullyQualifiedName!propertyType)}> ${methodName} { ");
379                     } else {
380                         prop = mixin(interp!"        public ${getCSharpInterfaceType(fullyQualifiedName!propertyType)} ${methodName} { ");
381                     }
382                     if (propertyGet) {
383                         if (is(propertyType == string)) {
384                             prop ~= mixin(interp!"get => SharedFunctions.SliceToString(dlang_${methodInterfaceName}(${is(T == class) ? string.init : \"ref \"}this), DStringType._string);");
385                         } else if (is(propertyType == wstring)) {
386                             prop ~= mixin(interp!"get => SharedFunctions.SliceToString(dlang_${methodInterfaceName}(${is(T == class) ? string.init : \"ref \"}this), DStringType._wstring);");
387                         } else if (is(propertyType == dstring)) {
388                             prop ~= mixin(interp!"get => SharedFunctions.SliceToString(dlang_${methodInterfaceName}(${is(T == class) ? string.init : \"ref \"}this), DStringType._dstring);");
389                         } else if (is(propertyType == string[])) {
390                             prop ~= mixin(interp!"get => new Range<string>(dlang_${methodInterfaceName}(${is(T == class) ? string.init : \"ref \"}this), DStringType._string); ");
391                         } else if (is(propertyType == wstring[])) {
392                             prop ~= mixin(interp!"get => new Range<string>(dlang_${methodInterfaceName}(${is(T == class) ? string.init : \"ref \"}this), DStringType._wstring); ");
393                         } else if (is(propertyType == dstring[])) {
394                             prop ~= mixin(interp!"get => new Range<string>(dlang_${methodInterfaceName}(${is(T == class) ? string.init : \"ref \"}this), DStringType._dstring); ");
395                         } else if (isArray!(propertyType)) {
396                             prop ~= mixin(interp!"get => new Range<${getCSharpInterfaceType(fullyQualifiedName!propertyType)}>(dlang_${methodInterfaceName}(${is(T == class) ? string.init : \"ref \"}this), DStringType.None); ");
397                         } else if (is(propertyType == class)) {
398                             prop ~= mixin(interp!"get => new ${getCSharpInterfaceType(fullyQualifiedName!propertyType)}(dlang_${methodInterfaceName}(${is(T == class) ? string.init : \"ref \"}this)); ");
399                         } else {
400                             prop ~= mixin(interp!"get => dlang_${methodInterfaceName}(${is(T == class) ? string.init : \"ref \"}this); ");
401                         }
402                     }
403                     if (propertySet) {
404                         if (is(propertyType == string)) {
405                             prop ~= mixin(interp!"set => dlang_${methodInterfaceName}(this, SharedFunctions.CreateString(value));");
406                         } else if (is(propertyType == wstring)) {
407                             prop ~= mixin(interp!"set => dlang_${methodInterfaceName}(this, SharedFunctions.CreateWstring(value));");
408                         } else if (is(propertyType == dstring)) {
409                             prop ~= mixin(interp!"set => dlang_${methodInterfaceName}(this, SharedFunctions.CreateDstring(value));");
410                         } else if (is(propertyType == string[])) {
411                             prop ~= mixin(interp!"set => dlang_${methodInterfaceName}(${is(T == class) ? string.init : \"ref \"}this, value.ToSlice(DStringType._string)); ");
412                         } else if (is(propertyType == wstring[])) {
413                             prop ~= mixin(interp!"set => dlang_${methodInterfaceName}(${is(T == class) ? string.init : \"ref \"}this, value.ToSlice(DStringType._wstring)); ");
414                         } else if (is(propertyType == dstring[])) {
415                             prop ~= mixin(interp!"set => dlang_${methodInterfaceName}(${is(T == class) ? string.init : \"ref \"}this, value.ToSlice(DStringType._dstring)); ");
416                         } else if (isArray!(propertyType)) {
417                             prop ~= mixin(interp!"set => dlang_${methodInterfaceName}(${is(T == class) ? string.init : \"ref \"}this, value.ToSlice()); ");
418                         } else {
419                             prop ~= mixin(interp!"set => dlang_${methodInterfaceName}(${is(T == class) ? string.init : \"ref \"}this, value); ");
420                         }
421                     }
422                     prop ~= "}" ~ newline;
423                     csagg.properties ~= prop;
424                 }
425             }
426         }
427     }
428 }
429 
430 private void generateFields(T)(string libraryName) if (is(T == class) || is(T == interface) || is(T == struct)) {
431     import autowrap.csharp.common : getDLangInterfaceName;
432     import std.traits : moduleName, isArray, fullyQualifiedName, Fields, FieldNameTuple;
433 
434     alias fqn = fullyQualifiedName!T;
435     const string aggName = __traits(identifier, T);
436     CSharpAggregate csagg = getAggregate(getCSharpName(moduleName!T), getCSharpName(aggName), !is(T == class));
437 
438     alias fieldTypes = Fields!T;
439     alias fieldNames = FieldNameTuple!T;
440 
441     if (is(T == class) || is(T == interface)) {
442         static foreach(fc; 0..fieldTypes.length) {
443             static if (is(typeof(__traits(getMember, T, fieldNames[fc])))) {
444                 csagg.properties ~= dllImportString.format(libraryName, getDLangInterfaceName(fqn, fieldNames[fc] ~ "_get"));
445                 csagg.properties ~= mixin(interp!"        private static extern ${getDLangReturnType(fullyQualifiedName!(fieldTypes[fc]), true)} dlang_${fieldNames[fc]}_get(IntPtr ptr);${newline}");
446                 if (is(fieldTypes[fc] == bool)) {
447                     csagg.properties ~= dllImportString.format(libraryName, getDLangInterfaceName(fqn, fieldNames[fc] ~ "_set"));
448                     csagg.properties ~= mixin(interp!"        private static extern void dlang_${fieldNames[fc]}_set(IntPtr ptr, [MarshalAs(UnmanagedType.Bool)] ${getDLangInterfaceType(fullyQualifiedName!(fieldTypes[fc]))} value);${newline}");
449                 } else if (is(fieldTypes[fc] == class)) {
450                     csagg.properties ~= dllImportString.format(libraryName, getDLangInterfaceName(fqn, fieldNames[fc] ~ "_set"));
451                     csagg.properties ~= mixin(interp!"        private static extern void dlang_${fieldNames[fc]}_set(IntPtr ptr, IntPtr value);${newline}");
452                 } else {
453                     csagg.properties ~= dllImportString.format(libraryName, getDLangInterfaceName(fqn, fieldNames[fc] ~ "_set"));
454                     csagg.properties ~= mixin(interp!"        private static extern void dlang_${fieldNames[fc]}_set(IntPtr ptr, ${getDLangInterfaceType(fullyQualifiedName!(fieldTypes[fc]))} value);${newline}");
455                 }
456 
457                 if (is(fieldTypes[fc] == string)) {
458                     csagg.properties ~= mixin(interp!"        public string ${getCSharpName(fieldNames[fc])} { get => SharedFunctions.SliceToString(dlang_${fieldNames[fc]}_get(this), DStringType._string); set => dlang_${fieldNames[fc]}_set(this, SharedFunctions.CreateString(value)); }${newline}");
459                 } else if (is(fieldTypes[fc] == wstring)) {
460                     csagg.properties ~= mixin(interp!"        public string ${getCSharpName(fieldNames[fc])} { get => SharedFunctions.SliceToString(dlang_${fieldNames[fc]}_get(this), DStringType._wstring); set => dlang_${fieldNames[fc]}_set(this, SharedFunctions.CreateWstring(value)); }${newline}");
461                 } else if (is(fieldTypes[fc] == dstring)) {
462                     csagg.properties ~= mixin(interp!"        public string ${getCSharpName(fieldNames[fc])} { get => SharedFunctions.SliceToString(dlang_${fieldNames[fc]}_get(this), DStringType._dstring); set => dlang_${fieldNames[fc]}_set(this, SharedFunctions.CreateDstring(value)); }${newline}");
463                 } else if (is(fieldTypes[fc] == string[])) {
464                     csagg.properties ~= mixin(interp!"        public Range<string> ${getCSharpName(fieldNames[fc])} { get => new Range<string>(dlang_${fieldNames[fc]}_get(this), DStringType._string); set => dlang_${fieldNames[fc]}_set(this, value.ToSlice(DStringType._string)); }${newline}");
465                 } else if (is(fieldTypes[fc] == wstring[])) {
466                     csagg.properties ~= mixin(interp!"        public Range<string> ${getCSharpName(fieldNames[fc])} { get => new Range<string>(dlang_${fieldNames[fc]}_get(this), DStringType._wstring); set => dlang_${fieldNames[fc]}_set(this, value.ToSlice(DStringType._wstring)); }${newline}");
467                 } else if (is(fieldTypes[fc] == dstring[])) {
468                     csagg.properties ~= mixin(interp!"        public Range<string> ${getCSharpName(fieldNames[fc])} { get => new Range<string>(dlang_${fieldNames[fc]}_get(this), DStringType._dstring); set => dlang_${fieldNames[fc]}_set(this, value.ToSlice(DStringType._dstring)); }${newline}");
469                 } else if (isArray!(fieldTypes[fc])) {
470                     csagg.properties ~= mixin(interp!"        public Range<${getCSharpInterfaceType(fullyQualifiedName!(fieldTypes[fc]))}> ${getCSharpName(fieldNames[fc])} { get => new Range<${getCSharpInterfaceType(fullyQualifiedName!(fieldTypes[fc]))}>(dlang_${fieldNames[fc]}_get(this), DStringType.None); set => dlang_${fieldNames[fc]}_set(this, value.ToSlice()); }${newline}");
471                 } else if (is(fieldTypes[fc] == class)) {
472                     csagg.properties ~= mixin(interp!"        public ${getCSharpInterfaceType(fullyQualifiedName!(fieldTypes[fc]))} ${getCSharpName(fieldNames[fc])} { get => new ${getCSharpInterfaceType(fullyQualifiedName!(fieldTypes[fc]))}(dlang_${fieldNames[fc]}_get(this)); set => dlang_${fieldNames[fc]}_set(this, value); }${newline}");
473                 } else {
474                     csagg.properties ~= mixin(interp!"        public ${getCSharpInterfaceType(fullyQualifiedName!(fieldTypes[fc]))} ${getCSharpName(fieldNames[fc])} { get => dlang_${fieldNames[fc]}_get(this); set => dlang_${fieldNames[fc]}_set(this, value); }${newline}");
475                 }
476             }
477         }
478     } else if(is(T == struct)) {
479         static foreach(fc; 0..fieldTypes.length) {
480             static if (is(typeof(__traits(getMember, T, fieldNames[fc])))) {
481                 if (isArray!(fieldTypes[fc])) {
482                     csagg.properties ~= mixin(interp!"        private slice _${getCSharpName(fieldNames[fc])};${newline}");
483                     if (is(fieldTypes[fc] == string)) {
484                         csagg.properties ~= mixin(interp!"        public string ${getCSharpName(fieldNames[fc])} { get => SharedFunctions.SliceToString(_${getCSharpName(fieldNames[fc])}, DStringType._string); set => _${getCSharpName(fieldNames[fc])} = SharedFunctions.CreateString(value); }${newline}");
485                     } else if (is(fieldTypes[fc] == wstring)) {
486                         csagg.properties ~= mixin(interp!"        public string ${getCSharpName(fieldNames[fc])} { get => SharedFunctions.SliceToString(_${getCSharpName(fieldNames[fc])}, DStringType._wstring); set => _${getCSharpName(fieldNames[fc])} = SharedFunctions.CreateWstring(value); }${newline}");
487                     } else if (is(fieldTypes[fc] == dstring)) {
488                         csagg.properties ~= mixin(interp!"        public string ${getCSharpName(fieldNames[fc])} { get => SharedFunctions.SliceToString(_${getCSharpName(fieldNames[fc])}, DStringType._dstring); set => _${getCSharpName(fieldNames[fc])} = SharedFunctions.CreateDstring(value); }${newline}");
489                     } else if (is(fieldTypes[fc] == string[])) {
490                         csagg.properties ~= mixin(interp!"        public Range<string> ${getCSharpName(fieldNames[fc])} { get => new Range<string>(_${fieldNames[fc]}, DStringType._string); set => _${fieldNames[fc]} = value.ToSlice(DStringType._string); }${newline}");
491                     } else if (is(fieldTypes[fc] == wstring[])) {
492                         csagg.properties ~= mixin(interp!"        public Range<string> ${getCSharpName(fieldNames[fc])} { get => new Range<string>(_${fieldNames[fc]}, DStringType._wstring); set => _${fieldNames[fc]} = value.ToSlice(DStringType._wstring); }${newline}");
493                     } else if (is(fieldTypes[fc] == dstring[])) {
494                         csagg.properties ~= mixin(interp!"        public Range<string> ${getCSharpName(fieldNames[fc])} { get => new Range<string>(_${fieldNames[fc]}, DStringType._dstring); set => _${fieldNames[fc]} = value.ToSlice(DStringType._dstring); }${newline}");
495                     } else {
496                         csagg.properties ~= mixin(interp!"        public Range<${getCSharpInterfaceType(fullyQualifiedName!(fieldTypes[fc]))}> ${getCSharpName(fieldNames[fc])} { get => new Range<${getCSharpInterfaceType(fullyQualifiedName!(fieldTypes[fc]))}>(_${fieldNames[fc]}, DStringType.None); set => _${fieldNames[fc]} = value.ToSlice(); }${newline}");
497                     }
498                 } else if (is(fieldTypes[fc] == bool)) {
499                     csagg.properties ~= mixin(interp!"        [MarshalAs(UnmanagedType.U1)] public ${getDLangInterfaceType(fullyQualifiedName!(fieldTypes[fc]))} ${getCSharpName(fieldNames[fc])};${newline}");
500                 } else if (is(fieldTypes[fc] == class)) {
501                     csagg.properties ~= mixin(interp!"        private IntPtr _${getCSharpName(fieldNames[fc])};${newline}");
502                     csagg.properties ~= mixin(interp!"        public ${getCSharpInterfaceType(fullyQualifiedName!(fieldTypes[fc]))} ${getCSharpName(fieldNames[fc])} { get => new ${getCSharpInterfaceType(fullyQualifiedName!(fieldTypes[fc]))}(_${fieldNames[fc]}); set => _${fieldNames[fc]} = value); }${newline}");
503                 } else {
504                     csagg.properties ~= mixin(interp!"        public ${getDLangInterfaceType(fullyQualifiedName!(fieldTypes[fc]))} ${getCSharpName(fieldNames[fc])};${newline}");
505                 }
506             }
507         }
508     }
509 }
510 
511 private void generateFunctions(Modules...)(string libraryName) if(allSatisfy!(isModule, Modules)) {
512     import autowrap.csharp.common : getDLangInterfaceName;
513     import autowrap.reflection: AllFunctions;
514     import std.traits : isArray, fullyQualifiedName, ReturnType, Parameters, ParameterIdentifierTuple;
515 
516     foreach(func; AllFunctions!Modules) {
517         alias modName = func.moduleName;
518         alias funcName = func.name;
519         CSharpNamespace ns = getNamespace(getCSharpName(modName));
520 
521         alias returnType = ReturnType!(__traits(getMember, func.module_, func.name));
522         alias returnTypeStr = fullyQualifiedName!(ReturnType!(__traits(getMember, func.module_, func.name)));
523         alias paramTypes = Parameters!(__traits(getMember, func.module_, func.name));
524         alias paramNames = ParameterIdentifierTuple!(__traits(getMember, func.module_, func.name));
525         const string interfaceName = getDLangInterfaceName(modName, null, funcName);
526         const string methodName = getCSharpMethodInterfaceName(null, funcName);
527         string retType = getDLangReturnType(returnTypeStr, is(returnType == class));
528         string funcStr = dllImportString.format(libraryName, interfaceName) ~ newline;
529         funcStr ~= mixin(interp!"        private static extern ${retType} dlang_${methodName}(");
530         static foreach(pc; 0..paramNames.length) {
531             funcStr ~= mixin(interp!"${is(paramTypes[pc] == bool) ? \"[MarshalAs(UnmanagedType.Bool)]\" : string.init}${getDLangInterfaceType(fullyQualifiedName!(paramTypes[pc]))} ${paramNames[pc]}, ");
532         }
533         if (paramNames.length > 0) {
534             funcStr = funcStr[0..$-2];
535         }
536         funcStr ~= ");" ~ newline;
537         if (is(returnType == string) || is(returnType == wstring) || is(returnType == dstring)) {
538             funcStr ~= mixin(interp!"        public static string ${methodName}(");
539         } else if (isArray!(returnType)) {
540             funcStr ~= mixin(interp!"        public static Range<${getCSharpInterfaceType(returnTypeStr)}> ${methodName}(");
541         } else {
542             funcStr ~= mixin(interp!"        public static ${getCSharpInterfaceType(returnTypeStr)} ${methodName}(");
543         }
544         static foreach(pc; 0..paramNames.length) {
545             if (is(paramTypes[pc] == string) || is(paramTypes[pc] == wstring) || is(paramTypes[pc] == dstring)) {
546                 funcStr ~= mixin(interp!"${getCSharpInterfaceType(fullyQualifiedName!(paramTypes[pc]))} ${paramNames[pc]}, ");
547             } else if (isArray!(paramTypes[pc])) {
548                 funcStr ~= mixin(interp!"Range<${getCSharpInterfaceType(fullyQualifiedName!(paramTypes[pc]))}> ${paramNames[pc]}, ");
549             } else {
550                 funcStr ~= mixin(interp!"${getCSharpInterfaceType(fullyQualifiedName!(paramTypes[pc]))} ${paramNames[pc]}, ");
551             }
552         }
553         if (paramNames.length > 0) {
554             funcStr = funcStr[0..$-2];
555         }
556         funcStr ~= ") {" ~ newline;
557         funcStr ~= mixin(interp!"            var dlang_ret = dlang_${methodName}(");
558         static foreach(pc; 0..paramNames.length) {
559             if (is(paramTypes[pc] == string)) {
560                 funcStr ~= mixin(interp!"SharedFunctions.CreateString(${paramNames[pc]}), ");
561             } else if (is(paramTypes[pc] == wstring)) {
562                 funcStr ~= mixin(interp!"SharedFunctions.CreateWString(${paramNames[pc]}), ");
563             } else if (is(paramTypes[pc] == dstring)) {
564                 funcStr ~= mixin(interp!"SharedFunctions.CreateDString(${paramNames[pc]}), ");
565             } else if (isArray!(paramTypes[pc])) {
566                 funcStr ~= mixin(interp!"${paramNames[pc]}.ToSlice(), ");
567             } else {
568                 funcStr ~= mixin(interp!"${paramNames[pc]}, ");
569             }
570         }
571         if (paramNames.length > 0) {
572             funcStr = funcStr[0..$-2];
573         }
574         funcStr ~= ");" ~ newline;
575         if (is(returnType == void)) {
576             funcStr ~= "            dlang_ret.EnsureValid();" ~ newline;
577         } else {
578             if (is(returnType == string)) {
579                 funcStr ~= "            return SharedFunctions.SliceToString(dlang_ret, DStringType._string);" ~ newline;
580             } else if (is(returnType == wstring)) {
581                 funcStr ~= "            return SharedFunctions.SliceToString(dlang_ret, DStringType._wstring);" ~ newline;
582             } else if (is(returnType == dstring)) {
583                 funcStr ~= "            return SharedFunctions.SliceToString(dlang_ret, DStringType._dstring);" ~ newline;
584             } else if (is(returnType == string[])) {
585                 funcStr ~= mixin(interp!"            return new Range<string>(dlang_ret, DStringType._string);${newline}");
586             } else if (is(returnType == wstring[])) {
587                 funcStr ~= mixin(interp!"            return new Range<string>(dlang_ret, DStringType._wstring);${newline}");
588             } else if (is(returnType == dstring[])) {
589                 funcStr ~= mixin(interp!"            return new Range<string>(dlang_ret, DStringType._dstring);${newline}");
590             } else if (isArray!(returnType)) {
591                 funcStr ~= mixin(interp!"            return new Range<${getCSharpInterfaceType(returnTypeStr)}>(dlang_ret, DStringType.None);${newline}");
592             } else {
593                 funcStr ~= "            return dlang_ret;" ~ newline;
594             }
595         }
596         funcStr ~= "        }" ~ newline;
597         ns.functions ~= funcStr;
598     }
599 }
600 
601 private string getDLangInterfaceType(string type) {
602     if (type[$-2..$] == "[]") return "slice";
603 
604     switch(type) {
605         //Types that require special marshalling types
606         case voidTypeString: return "void";
607         case stringTypeString: return "slice";
608         case wstringTypeString: return "slice";
609         case dstringTypeString: return "slice";
610         case sysTimeTypeString: return "datetime";
611         case dateTimeTypeString: return "datetime";
612         case timeOfDayTypeString: return "datetime";
613         case dateTypeString: return "datetime";
614         case durationTypeString: return "datetime";
615         case boolTypeString: return "bool";
616 
617         //Types that can be marshalled by default
618         case charTypeString: return "byte";
619         case wcharTypeString: return "char";
620         case dcharTypeString: return "uint";
621         case ubyteTypeString: return "byte";
622         case byteTypeString: return "sbyte";
623         case ushortTypeString: return "ushort";
624         case shortTypeString: return "short";
625         case uintTypeString: return "uint";
626         case intTypeString: return "int";
627         case ulongTypeString: return "ulong";
628         case longTypeString: return "long";
629         case floatTypeString: return "float";
630         case doubleTypeString: return "double";
631         default: return getCSharpName(type);
632     }
633 }
634 
635 private string getCSharpInterfaceType(string type) {
636     if (type[$-2..$] == "[]") type = type[0..$-2];
637 
638     switch (type) {
639         //Types that require special marshalling types
640         case voidTypeString: return "void";
641         case stringTypeString: return "string";
642         case wstringTypeString: return "string";
643         case dstringTypeString: return "string";
644         case sysTimeTypeString: return "DateTimeOffset";
645         case dateTimeTypeString: return "DateTime";
646         case timeOfDayTypeString: return "DateTime";
647         case dateTypeString: return "DateTime";
648         case durationTypeString: return "TimeSpan";
649         case boolTypeString: return "bool";
650 
651         //Types that can be marshalled by default
652         case charTypeString: return "byte";
653         case wcharTypeString: return "char";
654         case dcharTypeString: return "uint";
655         case ubyteTypeString: return "byte";
656         case byteTypeString: return "sbyte";
657         case ushortTypeString: return "ushort";
658         case shortTypeString: return "short";
659         case uintTypeString: return "uint";
660         case intTypeString: return "int";
661         case ulongTypeString: return "ulong";
662         case longTypeString: return "long";
663         case floatTypeString: return "float";
664         case doubleTypeString: return "double";
665         default: return getCSharpName(type);
666     }
667 }
668 
669 private string getDLangReturnType(string type, bool isClass) {
670     import std.algorithm : among;
671 
672     string rtname = getReturnErrorTypeName(getDLangInterfaceType(type));
673     //These types have predefined C# types.
674     if (type.among(dateTypeString, dateTimeTypeString, sysTimeTypeString, timeOfDayTypeString, durationTypeString,
675         voidTypeString, boolTypeString, stringTypeString, wstringTypeString, dstringTypeString) || rtname == "return_slice_error") {
676         return rtname;
677     }
678 
679     string typeStr = "    [GeneratedCodeAttribute(\"Autowrap\", \"1.0.0.0\")]
680     [StructLayout(LayoutKind.Sequential)]
681     internal struct " ~ rtname ~ " {
682         private void EnsureValid() {
683             var errStr = SharedFunctions.SliceToString(_error, DStringType._wstring);
684             if (!string.IsNullOrEmpty(errStr)) throw new DLangException(errStr);
685         }
686 ";
687 
688     if (isClass) {
689         typeStr ~= mixin(interp!"        public static implicit operator IntPtr(${rtname} ret) { ret.EnsureValid(); return ret._value; }${newline}");
690         typeStr ~= "        private IntPtr _value;" ~ newline;
691     } else {
692         typeStr ~= mixin(interp!"        public static implicit operator ${getCSharpInterfaceType(type)}(${rtname} ret) { ret.EnsureValid(); return ret._value; }${newline}");
693         typeStr ~= mixin(interp!"        private ${getCSharpInterfaceType(type)} _value;${newline}");
694     }
695     typeStr ~= "        private slice _error;" ~ newline;
696     typeStr ~= "    }" ~ newline ~ newline;
697 
698     if ((rtname in returnTypes) is null) {
699         returnTypes[rtname] = typeStr;
700     }
701     return rtname;
702 }
703 
704 private string getCSharpMethodInterfaceName(string aggName, string funcName) {
705     import autowrap.csharp.common : camelToPascalCase;
706     import std.string : split;
707     import std.string : replace;
708 
709     if (aggName == "DateTime" || aggName == "DateTimeOffset" || aggName == "TimeSpan") {
710         aggName = "Datetime";
711     }
712 
713     string name = string.init;
714     if (aggName !is null && aggName != string.init) {
715         name ~= camelToPascalCase(aggName) ~ "_";
716     }
717     name ~= camelToPascalCase(funcName);
718     return name.replace(".", "_");
719 }
720 
721 private string getReturnErrorTypeName(string aggName) {
722     import std.string : split;
723     import std.array : join;
724     return mixin(interp!"return_${aggName.split(\".\").join(\"_\")}_error");
725 }
726 
727 private string getCSharpName(string dlangName) {
728     import autowrap.csharp.common : camelToPascalCase;
729     import std.algorithm : map;
730     import std.string : split;
731     import std.array : join;
732     return dlangName.split(".").map!camelToPascalCase.join(".");
733 }
734 
735 private CSharpNamespace getNamespace(string name) {
736     return namespaces.require(name, new CSharpNamespace(name));
737 }
738 
739 private CSharpAggregate getAggregate(string namespace, string name, bool isStruct) {
740     CSharpNamespace ns = namespaces.require(namespace, new CSharpNamespace(namespace));
741     return ns.aggregates.require(name, new CSharpAggregate(name, isStruct));
742 }
743 
744 private void generateRangeDef(T)(string libraryName) {
745     import autowrap.csharp.common : getDLangSliceInterfaceName;
746     import std.traits : fullyQualifiedName;
747 
748     alias fqn = fullyQualifiedName!T;
749     const string csn = getCSharpName(fqn);
750 
751     rangeDef.functions ~= dllImportString.format(libraryName, getDLangSliceInterfaceName(fqn, "Create"));
752     rangeDef.functions ~= externFuncString.format("return_slice_error", getCSharpMethodInterfaceName(fqn, "Create"), "IntPtr capacity");
753     rangeDef.functions ~= dllImportString.format(libraryName, getDLangSliceInterfaceName(fqn, "Slice"));
754     rangeDef.functions ~= externFuncString.format("return_slice_error", getCSharpMethodInterfaceName(fqn, "Slice"), "slice dslice, IntPtr begin, IntPtr end");
755     rangeDef.functions ~= dllImportString.format(libraryName, getDLangSliceInterfaceName(fqn, "AppendSlice"));
756     rangeDef.functions ~= externFuncString.format("return_slice_error", getCSharpMethodInterfaceName(fqn, "AppendSlice"), "slice dslice, slice array");
757     rangeDef.constructors ~= mixin(interp!"            else if (typeof(T) == typeof(${getCSharpInterfaceType(fqn)})) this._slice = RangeFunctions.${getCSharpMethodInterfaceName(fqn, \"Create\")}(new IntPtr(capacity));${newline}");
758     rangeDef.sliceEnd ~= mixin(interp!"            else if (typeof(T) == typeof(${getCSharpInterfaceType(fqn)})) return new Range<T>(RangeFunctions.${getCSharpMethodInterfaceName(fqn, \"Slice\")}(_slice, new IntPtr(begin), _slice.length), DStringType.None);${newline}");
759     rangeDef.sliceRange ~= mixin(interp!"            else if (typeof(T) == typeof(${getCSharpInterfaceType(fqn)})) return new Range<T>(RangeFunctions.${getCSharpMethodInterfaceName(fqn, \"Slice\")}(_slice, new IntPtr(begin), new IntPtr(end)), DStringType.None);${newline}");
760     rangeDef.setters ~= mixin(interp!"                else if (typeof(T) == typeof(${getCSharpInterfaceType(fqn)})) RangeFunctions.${getCSharpMethodInterfaceName(fqn, \"Set\")}(_slice, new IntPtr(i), (${getCSharpInterfaceType(fqn)})(object)value);${newline}");
761     rangeDef.appendValues ~= mixin(interp!"            else if (typeof(T) == typeof(${getCSharpInterfaceType(fqn)})) { this._slice = RangeFunctions.${getCSharpMethodInterfaceName(fqn, \"AppendValue\")}(this._slice, (${getCSharpInterfaceType(fqn)})(object)value); }${newline}");
762     rangeDef.appendArrays ~= mixin(interp!"            else if (typeof(T) == typeof(${getCSharpInterfaceType(fqn)})) { range._slice = RangeFunctions.${getCSharpMethodInterfaceName(fqn, \"AppendSlice\")}(range._slice, source._slice); return range; }${newline}");
763     if (is(T == class) || is(T == interface)) {
764         rangeDef.functions ~= dllImportString.format(libraryName, getDLangSliceInterfaceName(fqn, "Get"));
765         rangeDef.functions ~= externFuncString.format(getDLangReturnType(fqn, true), getCSharpMethodInterfaceName(fqn, "Get"), "slice dslice, IntPtr index");
766         rangeDef.functions ~= dllImportString.format(libraryName, getDLangSliceInterfaceName(fqn, "Set"));
767         rangeDef.functions ~= externFuncString.format("return_void_error", getCSharpMethodInterfaceName(fqn, "Set"), "slice dslice, IntPtr index, IntPtr value");
768         rangeDef.functions ~= dllImportString.format(libraryName, getDLangSliceInterfaceName(fqn, "AppendValue"));
769         rangeDef.functions ~= externFuncString.format("return_slice_error", getCSharpMethodInterfaceName(fqn, "AppendValue"), "slice dslice, IntPtr value");
770         rangeDef.getters ~= mixin(interp!"                else if (typeof(T) == typeof(${getCSharpInterfaceType(fqn)})) return (T)(object)new ${getCSharpInterfaceType(fqn)}(RangeFunctions.${getCSharpMethodInterfaceName(fqn, \"Get\")}(_slice, new IntPtr(i)));${newline}");
771         rangeDef.enumerators ~= mixin(interp!"                else if (typeof(T) == typeof(${getCSharpInterfaceType(fqn)})) yield return (T)(object)new ${getCSharpInterfaceType(fqn)}(RangeFunctions.${getCSharpMethodInterfaceName(fqn, \"Get\")}(_slice, new IntPtr(i)));${newline}");
772     } else {
773         rangeDef.functions ~= dllImportString.format(libraryName, getDLangSliceInterfaceName(fqn, "Get"));
774         rangeDef.functions ~= externFuncString.format(getDLangReturnType(fqn, false), getCSharpMethodInterfaceName(fqn, "Get"), "slice dslice, IntPtr index");
775         rangeDef.functions ~= dllImportString.format(libraryName, getDLangSliceInterfaceName(fqn, "Set"));
776         rangeDef.functions ~= externFuncString.format("return_void_error", getCSharpMethodInterfaceName(fqn, "Set"), mixin(interp!"slice dslice, IntPtr index, ${getDLangInterfaceType(fqn)} value"));
777         rangeDef.functions ~= dllImportString.format(libraryName, getDLangSliceInterfaceName(fqn, "AppendValue"));
778         rangeDef.functions ~= externFuncString.format("return_slice_error", getCSharpMethodInterfaceName(fqn, "AppendValue"), mixin(interp!"slice dslice, ${getDLangInterfaceType(fqn)} value"));
779         rangeDef.getters ~= mixin(interp!"                else if (typeof(T) == typeof(${getCSharpInterfaceType(fqn)})) return (T)(object)(${getCSharpInterfaceType(fqn)})RangeFunctions.${getCSharpMethodInterfaceName(fqn, \"Get\")}(_slice, new IntPtr(i));${newline}");
780         rangeDef.enumerators ~= mixin(interp!"                else if (typeof(T) == typeof(${getCSharpInterfaceType(fqn)})) yield return (T)(object)(${getCSharpInterfaceType(fqn)})RangeFunctions.${getCSharpMethodInterfaceName(fqn, \"Get\")}(_slice, new IntPtr(i));${newline}");
781     }
782 }
783 
784 private void generateSliceBoilerplate(string libraryName) {
785     import std.datetime: DateTime, SysTime, Duration;
786 
787     void generateStringBoilerplate(string dlangType, string csharpType) {
788         rangeDef.enumerators ~= mixin(interp!"                else if (typeof(T) == typeof(string) && _strings == null && _type == DStringType._${dlangType}) yield return (T)(object)SharedFunctions.SliceToString(RangeFunctions.${csharpType}_Get(_slice, new IntPtr(i)), DStringType._${dlangType});${newline}");
789         rangeDef.getters ~= mixin(interp!"                else if (typeof(T) == typeof(string) && _strings == null && _type == DStringType._${dlangType}) return (T)(object)SharedFunctions.SliceToString(RangeFunctions.${csharpType}_Get(_slice, new IntPtr(i)), DStringType._${dlangType});${newline}");
790         rangeDef.setters ~= mixin(interp!"                else if (typeof(T) == typeof(string) && _strings == null && _type == DStringType._${dlangType}) RangeFunctions.${csharpType}_Set(_slice, new IntPtr(i), SharedFunctions.Create${csharpType}((string)(object)value));${newline}");
791         rangeDef.sliceEnd ~= mixin(interp!"            else if (typeof(T) == typeof(string) && _strings == null && _type == DStringType._${dlangType}) return new Range<T>(RangeFunctions.${csharpType}_Slice(_slice, new IntPtr(begin), _slice.length), _type);${newline}");
792         rangeDef.sliceRange ~= mixin(interp!"            else if (typeof(T) == typeof(string) && _strings == null && _type == DStringType._${dlangType}) return new Range<T>(RangeFunctions.${csharpType}_Slice(_slice, new IntPtr(begin), new IntPtr(end)), _type);${newline}");
793         rangeDef.appendValues ~= mixin(interp!"            else if (typeof(T) == typeof(string) && _strings == null && _type == DStringType._${dlangType}) { _slice = RangeFunctions.${csharpType}_AppendValue(_slice, SharedFunctions.Create${csharpType}((string)(object)value)); }${newline}");
794         rangeDef.appendArrays ~= mixin(interp!"            else if (typeof(T) == typeof(string) && range._strings == null && range._type == DStringType._${dlangType}) { range._slice = RangeFunctions.${csharpType}_AppendSlice(range._slice, source._slice); return range; }${newline}");
795 
796         rangeDef.functions ~= dllImportString.format(libraryName, mixin(interp!"autowrap_csharp_${csharpType}_Create"));
797         rangeDef.functions ~= externFuncString.format("return_slice_error", csharpType ~ "_Create", "IntPtr capacity");
798         rangeDef.functions ~= dllImportString.format(libraryName, mixin(interp!"autowrap_csharp_${csharpType}_Get"));
799         rangeDef.functions ~= externFuncString.format("return_slice_error", csharpType ~ "_Get", "slice dslice, IntPtr index");
800         rangeDef.functions ~= dllImportString.format(libraryName, mixin(interp!"autowrap_csharp_${csharpType}_Set"));
801         rangeDef.functions ~= externFuncString.format("return_void_error", csharpType ~ "_Set", "slice dslice, IntPtr index, slice value");
802         rangeDef.functions ~= dllImportString.format(libraryName, mixin(interp!"autowrap_csharp_${csharpType}_Slice"));
803         rangeDef.functions ~= externFuncString.format("return_slice_error", csharpType ~ "_Slice", "slice dslice, IntPtr begin, IntPtr end");
804         rangeDef.functions ~= dllImportString.format(libraryName, mixin(interp!"autowrap_csharp_${csharpType}_AppendValue"));
805         rangeDef.functions ~= externFuncString.format("return_slice_error", csharpType ~ "_AppendValue", "slice dslice, slice value");
806         rangeDef.functions ~= dllImportString.format(libraryName, mixin(interp!"autowrap_csharp_${csharpType}_AppendSlice"));
807         rangeDef.functions ~= externFuncString.format("return_slice_error", csharpType ~ "_AppendSlice", "slice dslice, slice array");
808     }
809 
810     //bool
811     rangeDef.functions ~= dllImportString.format(libraryName, "autowrap_csharp_Bool_Create");
812     rangeDef.functions ~= externFuncString.format("return_slice_error", "Bool_Create", "IntPtr capacity");
813     rangeDef.functions ~= dllImportString.format(libraryName, "autowrap_csharp_Bool_Get");
814     rangeDef.functions ~= "        [return: MarshalAs(UnmanagedType.Bool)]" ~ newline;
815     rangeDef.functions ~= externFuncString.format("return_bool_error", "Bool_Get", "slice dslice, IntPtr index");
816     rangeDef.functions ~= dllImportString.format(libraryName, "autowrap_csharp_Bool_Set");
817     rangeDef.functions ~= externFuncString.format("return_void_error", "Bool_Set", "slice dslice, IntPtr index, [MarshalAs(UnmanagedType.Bool)] bool value");
818     rangeDef.functions ~= dllImportString.format(libraryName, "autowrap_csharp_Bool_Slice");
819     rangeDef.functions ~= externFuncString.format("return_slice_error", "Bool_Slice", "slice dslice, IntPtr begin, IntPtr end");
820     rangeDef.functions ~= dllImportString.format(libraryName, "autowrap_csharp_Bool_AppendValue");
821     rangeDef.functions ~= externFuncString.format("return_slice_error", "Bool_AppendValue", "slice dslice, [MarshalAs(UnmanagedType.Bool)] bool value");
822     rangeDef.functions ~= dllImportString.format(libraryName, "autowrap_csharp_Bool_AppendSlice");
823     rangeDef.functions ~= externFuncString.format("return_slice_error", "Bool_AppendSlice", "slice dslice, slice array");
824     rangeDef.constructors ~= "            if (typeof(T) == typeof(bool)) this._slice = RangeFunctions.Bool_Create(new IntPtr(capacity));" ~ newline;
825     rangeDef.enumerators ~= "                if (typeof(T) == typeof(bool)) yield return (T)(object)RangeFunctions.Bool_Get(_slice, new IntPtr(i));" ~ newline;
826     rangeDef.getters ~= "                if (typeof(T) == typeof(bool)) return (T)(object)RangeFunctions.Bool_Get(_slice, new IntPtr(i));" ~ newline;
827     rangeDef.setters ~= "                if (typeof(T) == typeof(bool)) RangeFunctions.Bool_Set(_slice, new IntPtr(i), (bool)(object)value);" ~ newline;
828     rangeDef.sliceEnd ~= "            if (typeof(T) == typeof(bool)) return new Range<T>(RangeFunctions.Bool_Slice(_slice, new IntPtr(begin), _slice.length), DStringType.None);" ~ newline;
829     rangeDef.sliceRange ~= "            if (typeof(T) == typeof(bool)) return new Range<T>(RangeFunctions.Bool_Slice(_slice, new IntPtr(begin), new IntPtr(end)), DStringType.None);" ~ newline;
830     rangeDef.appendValues ~= "            if (typeof(T) == typeof(bool)) { this._slice = RangeFunctions.Bool_AppendValue(this._slice, (bool)(object)value); }" ~ newline;
831     rangeDef.appendArrays ~= "            if (typeof(T) == typeof(bool)) { range._slice = RangeFunctions.Bool_AppendSlice(range._slice, source._slice); return range; }" ~ newline;
832 
833     rangeDef.enumerators ~= "                else if (typeof(T) == typeof(string) && _strings != null && _type == DStringType.None) yield return (T)(object)_strings[(int)i];" ~ newline;
834     rangeDef.getters ~= "                else if (typeof(T) == typeof(string) && _strings != null && _type == DStringType.None) return (T)(object)_strings[(int)i];" ~ newline;
835     rangeDef.setters ~= "                else if (typeof(T) == typeof(string) && _strings != null && _type == DStringType.None) _strings[(int)i] = (string)(object)value;" ~ newline;
836     rangeDef.sliceEnd ~= "            else if (typeof(T) == typeof(string) && _strings != null && _type == DStringType.None) return new Range<T>((IEnumerable<T>)_strings.Skip((int)begin));" ~ newline;
837     rangeDef.sliceRange ~= "            else if (typeof(T) == typeof(string) && _strings != null && _type == DStringType.None) return new Range<T>((IEnumerable<T>)_strings.Skip((int)begin).Take((int)end - (int)begin));" ~ newline;
838     rangeDef.appendValues ~= "            else if (typeof(T) == typeof(string) && this._strings != null && this._type == DStringType.None) { this._strings.Add((string)(object)value); }" ~ newline;
839     rangeDef.appendArrays ~= "            else if (typeof(T) == typeof(string) && range._strings != null && range._type == DStringType.None) { foreach(T s in source) range._strings.Add((string)(object)s); return range; }" ~ newline;
840 
841     generateStringBoilerplate("string", "String");
842     generateStringBoilerplate("wstring", "Wstring");
843     generateStringBoilerplate("dstring", "Dstring");
844 
845     generateRangeDef!byte(libraryName);
846     generateRangeDef!ubyte(libraryName);
847     generateRangeDef!short(libraryName);
848     generateRangeDef!ushort(libraryName);
849     generateRangeDef!int(libraryName);
850     generateRangeDef!uint(libraryName);
851     generateRangeDef!long(libraryName);
852     generateRangeDef!ulong(libraryName);
853     generateRangeDef!float(libraryName);
854     generateRangeDef!double(libraryName);
855 
856     generateRangeDef!DateTime(libraryName);
857     generateRangeDef!SysTime(libraryName);
858     generateRangeDef!Duration(libraryName);
859 }
860 
861 private string writeCSharpBoilerplate(string libraryName, string rootNamespace) {
862     string returnTypesStr = string.init;
863     foreach(string rt; returnTypes.byValue) {
864         returnTypesStr ~= rt;
865     }
866     immutable string boilerplate = import("Boilerplate.cs");
867     return boilerplate.format(libraryName, rootNamespace, returnTypesStr, rangeDef.toString());
868 }