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