1 module autowrap.csharp.csharp;
2 
3 import scriptlike : interp, _interp_text;
4 
5 import autowrap.csharp.common : LibraryName, RootNamespace, OutputFileName;
6 import autowrap.reflection : isModule, Modules;
7 
8 import std.ascii : newline;
9 import std.meta: allSatisfy;
10 import std.string : format;
11 import std.typecons : Tuple;
12 
13 private string[string] returnTypes;
14 private CSharpNamespace[string] namespaces;
15 private csharpRange rangeDef;
16 
17 enum string voidTypeString = "void";
18 enum string stringTypeString = "string";
19 enum string wstringTypeString = "wstring";
20 enum string dstringTypeString = "dstring";
21 enum string boolTypeString = "bool";
22 enum string dateTimeTypeString = "std.datetime.date.DateTime";
23 enum string sysTimeTypeString = "std.datetime.systime.SysTime";
24 enum string dateTypeString = "std.datetime.date.Date";
25 enum string timeOfDayTypeString = "std.datetime.date.TimeOfDay";
26 enum string durationTypeString = "core.time.Duration";
27 enum string uuidTypeString = "UUID";
28 enum string charTypeString = "char";
29 enum string wcharTypeString = "wchar";
30 enum string dcharTypeString = "dchar";
31 enum string ubyteTypeString = "ubyte";
32 enum string byteTypeString = "byte";
33 enum string ushortTypeString = "ushort";
34 enum string shortTypeString = "short";
35 enum string uintTypeString = "uint";
36 enum string intTypeString = "int";
37 enum string ulongTypeString = "ulong";
38 enum string longTypeString = "long";
39 enum string floatTypeString = "float";
40 enum string doubleTypeString = "double";
41 enum string sliceTypeString = "slice";
42 
43 enum string dllImportString = "        [DllImport(\"%1$s\", EntryPoint = \"%2$s\", CallingConvention = CallingConvention.Cdecl)]" ~ newline;
44 enum string externFuncString = "        internal static extern %1$s %2$s(%3$s);" ~ newline;
45 
46 enum int fileReservationSize = 33_554_432;
47 enum int aggregateReservationSize = 32_768;
48 
49 //Class used for language built-in reference semantics.
50 private final class CSharpNamespace {
51     public string namespace;
52     public string functions;
53     public CSharpAggregate[string] aggregates;
54 
55     public this(string ns) {
56         namespace = ns;
57         functions = string.init;
58     }
59 
60     public override string toString() {
61         string ret = "namespace " ~ namespace ~ " {
62     using System;
63     using System.CodeDom.Compiler;
64     using System.Collections.Generic;
65     using System.Linq;
66     using System.Reflection;
67     using System.Runtime.InteropServices;
68     using Autowrap;
69 
70     public static class Functions {
71 " ~ functions ~ "
72     }" ~ newline ~ newline;
73             foreach(agg; aggregates.byValue()) {
74                 ret ~= agg.toString();
75             }
76             ret ~= "}" ~ newline;
77         return cast(immutable)ret;
78     }
79 }
80 
81 //Class used for language built-in reference semantics.
82 private final class CSharpAggregate {
83     public string name;
84     public bool isStruct;
85     public string constructors;
86     public string functions;
87     public string methods;
88     public string properties;
89 
90     public this (string name, bool isStruct) {
91         this.name = name;
92         this.isStruct = isStruct;
93         this.constructors = string.init;
94         this.functions = string.init;
95         this.methods = string.init;
96         this.properties = string.init;
97     }
98 
99     public override string toString() {
100         import autowrap.csharp.common : camelToPascalCase;
101 
102         char[] ret;
103         ret.reserve(aggregateReservationSize);
104         ret ~= "    [GeneratedCodeAttribute(\"Autowrap\", \"1.0.0.0\")]" ~ newline;
105         if (isStruct) {
106             ret ~= "    [StructLayout(LayoutKind.Sequential)]" ~ newline;
107             ret ~= mixin(interp!"    public struct ${camelToPascalCase(this.name)} {${newline}");
108         } else {
109             ret ~= mixin(interp!"    public class ${camelToPascalCase(this.name)} : DLangObject {${newline}");
110             ret ~= mixin(interp!"        public static implicit operator IntPtr(${camelToPascalCase(this.name)} ret) { return ret.DLangPointer; }${newline}");
111             ret ~= mixin(interp!"        public static implicit operator ${camelToPascalCase(this.name)}(IntPtr ret) { return new ${camelToPascalCase(this.name)}(ret); }${newline}");
112         }
113         if (functions != string.init) ret ~= functions ~ newline;
114         if (constructors != string.init) ret ~= constructors ~ newline;
115         if (methods != string.init) ret ~= methods ~ newline;
116         if (properties != string.init) ret ~= properties;
117         ret ~= "    }" ~ newline ~ newline;
118         return cast(immutable)ret;
119     }
120 }
121 
122 public struct csharpRange {
123     public string constructors = string.init;
124     public string enumerators = string.init;
125     public string functions = string.init;
126     public string getters = string.init;
127     public string setters = string.init;
128     public string appendValues = string.init;
129     public string appendArrays = string.init;
130     public string sliceEnd = string.init;
131     public string sliceRange = string.init;
132 
133     public string toString() {
134         immutable string boilerplate = import("Ranges.cs");
135         return boilerplate.format(functions, constructors, getters, setters, sliceEnd, sliceRange, appendValues, appendArrays, enumerators);
136     }
137 }
138 
139 public string generateCSharp(Modules...)(LibraryName libraryName, RootNamespace rootNamespace) if(allSatisfy!(isModule, Modules)) {
140     import core.time : Duration;
141     import autowrap.csharp.common : isDateTimeType, verifySupported;
142     import autowrap.reflection : AllAggregates;
143 
144     generateSliceBoilerplate(libraryName.value);
145 
146     static foreach(Agg; AllAggregates!Modules)
147     {
148         static if(verifySupported!Agg && !isDateTimeType!Agg)
149         {
150             generateRangeDef!Agg(libraryName.value);
151             generateConstructors!Agg(libraryName.value);
152             generateMethods!Agg(libraryName.value);
153             generateProperties!Agg(libraryName.value);
154             generateFields!Agg(libraryName.value);
155         }
156     }
157 
158     generateFunctions!Modules(libraryName.value);
159 
160     char[] ret;
161     ret.reserve(fileReservationSize);
162     foreach(csns; namespaces.byValue()) {
163         ret ~= csns.toString();
164     }
165 
166     ret ~= newline ~ writeCSharpBoilerplate(libraryName.value, rootNamespace.value);
167 
168     return cast(immutable)ret;
169 }
170 
171 private void generateConstructors(T)(string libraryName) if (is(T == class) || is(T == struct)) {
172     import autowrap.csharp.common : getDLangInterfaceName, numDefaultArgs, verifySupported;
173 
174     import std.algorithm : among;
175     import std.conv : to;
176     import std.format : format;
177     import std.meta: AliasSeq, Filter;
178     import std.traits : moduleName, fullyQualifiedName, hasMember, Parameters, ParameterIdentifierTuple;
179 
180     alias fqn = fullyQualifiedName!T;
181     const string aggName = __traits(identifier, T);
182     CSharpAggregate csagg = getAggregate(getCSharpName(moduleName!T), getCSharpName(aggName), !is(T == class));
183 
184     //Generate constructor methods
185     static if(hasMember!(T, "__ctor") && __traits(getProtection, __traits(getMember, T, "__ctor")).among("export", "public"))
186     {
187         foreach(i, c; __traits(getOverloads, T, "__ctor"))
188         {
189             if (__traits(getProtection, c).among("export", "public"))
190             {
191                 alias paramNames = ParameterIdentifierTuple!c;
192                 alias ParamTypes = Parameters!c;
193 
194                 static if(Filter!(verifySupported, ParamTypes).length != ParamTypes.length)
195                     continue;
196 
197                 static foreach(nda; 0 .. numDefaultArgs!c + 1)
198                 {{
199                     enum numParams = ParamTypes.length - nda;
200 
201                     enum interfaceName = format("%s%s_%s", getDLangInterfaceName(fqn, "__ctor"), i, numParams);
202                     enum methodInterfaceName = format("%s%s_%s", getCSharpMethodInterfaceName(aggName, "__ctor"), i, numParams);
203 
204                     string ctor = dllImportString.format(libraryName, interfaceName);
205                     ctor ~= mixin(interp!"        private static extern ${getDLangReturnType!(T, T)()} dlang_${methodInterfaceName}(");
206 
207                     static foreach(pc; 0 .. numParams)
208                     {
209                         if (is(ParamTypes[pc] == class))
210                             ctor ~= mixin(interp!"IntPtr ${paramNames[pc]}, ");
211                         else
212                             ctor ~= mixin(interp!"${is(ParamTypes[pc] == bool) ? \"[MarshalAs(UnmanagedType.Bool)]\" : string.init}${getDLangInterfaceType!(ParamTypes[pc], T)()} ${paramNames[pc]}, ");
213                     }
214 
215                     if (numParams != 0)
216                         ctor = ctor[0 .. $ - 2];
217 
218                     ctor ~= ");" ~ newline;
219                     ctor ~= mixin(interp!"        public ${getCSharpName(aggName)}(");
220                     static foreach(pc; 0 .. numParams)
221                         ctor ~= mixin(interp!"${getCSharpInterfaceType(fullyQualifiedName!(ParamTypes[pc]))} ${paramNames[pc]}, ");
222 
223                     if (numParams != 0)
224                         ctor = ctor[0 .. $ - 2];
225 
226                     static if (is(T == class))
227                         ctor ~= mixin(interp!") : base(dlang_${methodInterfaceName}(");
228                     else static if(is(T == struct))
229                     {
230                         ctor ~= ") {" ~ newline;
231                         ctor ~= mixin(interp!"            this = dlang_${methodInterfaceName}(");
232                     }
233                     else
234                         static assert(false, "Somehow, this type has a constructor even though it is neither a class nor a struct: " ~ T.stringof);
235 
236                     static foreach(pc; 0 .. numParams)
237                     {
238                         static if (is(ParamTypes[pc] == string))
239                             ctor ~= mixin(interp!"SharedFunctions.CreateString(${paramNames[pc]}), ");
240                         else static if (is(ParamTypes[pc] == wstring))
241                             ctor ~= mixin(interp!"SharedFunctions.CreateWString(${paramNames[pc]}), ");
242                         else static if (is(ParamTypes[pc] == dstring))
243                             ctor ~= mixin(interp!"SharedFunctions.CreateDString(${paramNames[pc]}), ");
244                         else
245                             ctor ~= mixin(interp!"${paramNames[pc]}, ");
246                     }
247 
248                     if (numParams != 0)
249                         ctor = ctor[0 .. $ - 2];
250 
251                     ctor ~= ")";
252 
253                     static if (is(T == class))
254                         ctor ~= ") { }" ~ newline;
255                     else
256                     {
257                         ctor ~= ";" ~ newline;
258                         ctor ~= "        }" ~ newline;
259                     }
260 
261                     csagg.constructors ~= ctor;
262                 }}
263             }
264         }
265     }
266 
267     if (is(T == class))
268         csagg.constructors ~= mixin(interp!"        internal ${getCSharpName(aggName)}(IntPtr ptr) : base(ptr) { }${newline}");
269 }
270 
271 private void generateMethods(T)(string libraryName)
272     if (is(T == class) || is(T == interface) || is(T == struct))
273 {
274     import autowrap.csharp.common : getDLangInterfaceName, numDefaultArgs, verifySupported;
275 
276     import std.algorithm.comparison : among;
277     import std.conv : to;
278     import std.format : format;
279     import std.meta : AliasSeq, Filter;
280     import std.traits : moduleName, isDynamicArray, fullyQualifiedName, isFunction, functionAttributes, FunctionAttribute,
281                         ReturnType, Parameters, ParameterIdentifierTuple;
282 
283     alias fqn = fullyQualifiedName!T;
284     const string aggName = __traits(identifier, T);
285     CSharpAggregate csagg = getAggregate(getCSharpName(moduleName!T), getCSharpName(aggName), !is(T == class));
286 
287     foreach(m; __traits(allMembers, T))
288     {
289         static if (!m.among("__ctor", "toHash", "opEquals", "opCmp", "factory") &&
290                    is(typeof(__traits(getMember, T, m))))
291         {
292             enum methodName = getCSMemberName(aggName, cast(string)m);
293 
294             foreach(oc, mo; __traits(getOverloads, T, m))
295             {
296                 static if(isFunction!mo)
297                 {
298                     static foreach(nda; 0 .. numDefaultArgs!mo + 1)
299                     {{
300                         alias RT = ReturnType!mo;
301                         alias ParamTypes = Parameters!mo;
302                         alias Types = AliasSeq!(RT, ParamTypes);
303 
304                         static if(Filter!(verifySupported, Types).length != Types.length)
305                             continue;
306                         else
307                         {
308                             enum numParams = ParamTypes.length - nda;
309                             alias paramNames = ParameterIdentifierTuple!mo;
310 
311                             enum dlangInterfaceName = format("%s%s_%s", getDLangInterfaceName(fqn, m), oc, numParams);
312                             enum methodInterfaceName = format("dlang_%s", getCSharpMethodInterfaceName(aggName, cast(string)m));
313 
314                             string exp = dllImportString.format(libraryName, dlangInterfaceName);
315                             exp ~= "        private static extern ";
316 
317                             if (!is(RT == void))
318                                 exp ~= getDLangReturnType!(RT, T)();
319                             else
320                                 exp ~= "return_void_error";
321 
322                             exp ~= mixin(interp!" ${methodInterfaceName}(");
323 
324                             if (is(T == struct))
325                                 exp ~= mixin(interp!"ref ${getDLangInterfaceType!(T, T)()} __obj__, ");
326                             else if (is(T == class) || is(T == interface))
327                                 exp ~= "IntPtr __obj__, ";
328 
329                             static foreach(pc; 0 .. numParams)
330                             {
331                                 if (is(ParamTypes[pc] == class))
332                                     exp ~= mixin(interp!"IntPtr ${paramNames[pc]}, ");
333                                 else
334                                     exp ~= mixin(interp!"${getDLangInterfaceType!(ParamTypes[pc], T)()} ${paramNames[pc]}, ");
335                             }
336 
337                             exp = exp[0 .. $-2];
338                             exp ~= ");" ~ newline;
339 
340                             if ((functionAttributes!mo & FunctionAttribute.property) == 0)
341                             {
342                                 enum returnTypeStr = fullyQualifiedName!RT;
343 
344                                 exp ~= mixin(interp!`        public ${methodName == "ToString" ? "override " : ""}${getCSharpInterfaceType(returnTypeStr)} ${methodName}(`);
345                                 static foreach(pc; 0 .. numParams)
346                                 {
347                                     if (is(ParamTypes[pc] == string) || is(ParamTypes[pc] == wstring) || is(ParamTypes[pc] == dstring))
348                                         exp ~= mixin(interp!"${getCSharpInterfaceType(fullyQualifiedName!(ParamTypes[pc]))} ${paramNames[pc]}, ");
349                                     else if (isDynamicArray!(ParamTypes[pc]))
350                                         exp ~= mixin(interp!"Range<${getCSharpInterfaceType(fullyQualifiedName!(ParamTypes[pc]))}> ${paramNames[pc]}, ");
351                                     else
352                                         exp ~= mixin(interp!"${getCSharpInterfaceType(fullyQualifiedName!(ParamTypes[pc]))} ${paramNames[pc]}, ");
353                                 }
354 
355                                 if (numParams != 0)
356                                     exp = exp[0 .. $-2];
357 
358                                 exp ~= ") {" ~ newline;
359                                 exp ~= mixin(interp!`            var dlang_ret = ${methodInterfaceName}(${is(T == struct) ? "ref " : ""}this, `);
360 
361                                 static foreach(pc; 0 .. numParams)
362                                 {
363                                     static if (is(ParamTypes[pc] == string))
364                                         exp ~= mixin(interp!"SharedFunctions.CreateString(${paramNames[pc]}), ");
365                                     else static if (is(ParamTypes[pc] == wstring))
366                                         exp ~= mixin(interp!"SharedFunctions.CreateWstring(${paramNames[pc]}), ");
367                                     else static if (is(ParamTypes[pc] == dstring))
368                                         exp ~= mixin(interp!"SharedFunctions.CreateDstring(${paramNames[pc]}), ");
369                                     else
370                                         exp ~= paramNames[pc] ~ ", ";
371                                 }
372 
373                                 exp = exp[0 .. $-2];
374                                 exp ~= ");" ~ newline;
375 
376                                 if (!is(RT == void))
377                                 {
378                                     if (is(RT == string))
379                                         exp ~= "            return SharedFunctions.SliceToString(dlang_ret, DStringType._string);" ~ newline;
380                                     else if (is(RT == wstring))
381                                         exp ~= "            return SharedFunctions.SliceToString(dlang_ret, DStringType._wstring);" ~ newline;
382                                     else if (is(RT == dstring))
383                                         exp ~= "            return SharedFunctions.SliceToString(dlang_ret, DStringType._dstring);" ~ newline;
384                                     else
385                                         exp ~= "            return dlang_ret;" ~ newline;
386                                 }
387 
388                                 exp ~= "        }" ~ newline;
389                             }
390 
391                             csagg.methods ~= exp;
392                         }
393                     }}
394                 }
395             }
396         }
397     }
398 }
399 
400 private void generateProperties(T)(string libraryName)
401     if (is(T == class) || is(T == interface) || is(T == struct))
402 {
403     import autowrap.csharp.common : verifySupported;
404 
405     import std.algorithm.comparison : among;
406     import std.format : format;
407     import std.meta : AliasSeq, Filter, staticIndexOf;
408     import std.traits : moduleName, isDynamicArray, fullyQualifiedName;
409 
410     enum fqn = fullyQualifiedName!T;
411     enum aggName = __traits(identifier, T);
412     CSharpAggregate csagg = getAggregate(getCSharpName(moduleName!T), getCSharpName(aggName), !is(T == class));
413 
414     foreach(m; __traits(allMembers, T))
415     {
416         static if (!m.among("__ctor", "toHash", "opEquals", "opCmp", "factory") &&
417                    is(typeof(__traits(getMember, T, m))))
418         {
419             enum methodName = getCSMemberName(aggName, cast(string)m);
420 
421             alias GType = GetterPropertyType!(T, m);
422             alias STypes = SetterPropertyTypes!(T, m);
423 
424             static if(GType.length == 1)
425             {
426                 alias PT = GType[0];
427                 enum getterInterfaceName = format("dlang_%s", getCSharpMethodInterfaceName(aggName, cast(string)m));
428 
429                 static if(staticIndexOf!(PT, STypes) != -1)
430                     enum setterInterfaceName = format("dlang_%s", getCSharpMethodInterfaceName(aggName, cast(string)m));
431             }
432             else static if(STypes.length == 1)
433             {
434                 alias PT = STypes[0];
435                 enum setterInterfaceName = format("dlang_%s", getCSharpMethodInterfaceName(aggName, cast(string)m));
436             }
437 
438             static if(is(PT) && verifySupported!PT)
439             {
440                 static if (is(PT == string) || is(PT == wstring) || is(PT == dstring))
441                     string prop = mixin(interp!"        public string ${methodName} { ");
442                 else static if (isDynamicArray!PT)
443                     string prop = mixin(interp!"        public Range<${getCSharpInterfaceType(fullyQualifiedName!PT)}> ${methodName} { ");
444                 else
445                     string prop = mixin(interp!"        public ${getCSharpInterfaceType(fullyQualifiedName!PT)} ${methodName} { ");
446 
447                 static if (is(typeof(getterInterfaceName)))
448                 {
449                     static if (is(PT == string))
450                         prop ~= mixin(interp!`get => SharedFunctions.SliceToString(${getterInterfaceName}(${is(T == class) ? "" : "ref "}this), DStringType._string);`);
451                     else static if (is(PT == wstring))
452                         prop ~= mixin(interp!`get => SharedFunctions.SliceToString(${getterInterfaceName}(${is(T == class) ? "" : "ref "}this), DStringType._wstring);`);
453                     else static if (is(PT == dstring))
454                         prop ~= mixin(interp!`get => SharedFunctions.SliceToString(${getterInterfaceName}(${is(T == class) ? "" : "ref "}this), DStringType._dstring);`);
455                     else static if (is(PT == string[]))
456                         prop ~= mixin(interp!`get => new Range<string>(${getterInterfaceName}(${is(T == class) ? "" : "ref "}this), DStringType._string); `);
457                     else static if (is(PT == wstring[]))
458                         prop ~= mixin(interp!`get => new Range<string>(${getterInterfaceName}(${is(T == class) ? "" : "ref "}this), DStringType._wstring); `);
459                     else static if (is(PT == dstring[]))
460                         prop ~= mixin(interp!`get => new Range<string>(${getterInterfaceName}(${is(T == class) ? "" : "ref "}this), DStringType._dstring); `);
461                     else static if (isDynamicArray!PT)
462                         prop ~= mixin(interp!`get => new Range<${getCSharpInterfaceType(fullyQualifiedName!PT)}>(${getterInterfaceName}(${is(T == class) ? "" : "ref "}this), DStringType.None); `);
463                     else static if (is(PT == class))
464                         prop ~= mixin(interp!`get => new ${getCSharpInterfaceType(fullyQualifiedName!PT)}(${getterInterfaceName}(${is(T == class) ? "" : "ref "}this)); `);
465                     else
466                         prop ~= mixin(interp!`get => ${getterInterfaceName}(${is(T == class) ? "" : "ref "}this); `);
467                 }
468 
469                 static if (is(typeof(setterInterfaceName)))
470                 {
471                     static if (is(PT == string))
472                         prop ~= mixin(interp!`set => ${setterInterfaceName}(this, SharedFunctions.CreateString(value));`);
473                     else static if (is(PT == wstring))
474                         prop ~= mixin(interp!`set => ${setterInterfaceName}(this, SharedFunctions.CreateWstring(value));`);
475                     else static if (is(PT == dstring))
476                         prop ~= mixin(interp!`set => ${setterInterfaceName}(this, SharedFunctions.CreateDstring(value));`);
477                     else static if (is(PT == string[]))
478                         prop ~= mixin(interp!`set => ${setterInterfaceName}(${is(T == class) ? "" : "ref "}this, value.ToSlice(DStringType._string)); `);
479                     else static if (is(PT == wstring[]))
480                         prop ~= mixin(interp!`set => ${setterInterfaceName}(${is(T == class) ? "" : "ref "}this, value.ToSlice(DStringType._wstring)); `);
481                     else static if (is(PT == dstring[]))
482                         prop ~= mixin(interp!`set => ${setterInterfaceName}(${is(T == class) ? "" : "ref "}this, value.ToSlice(DStringType._dstring)); `);
483                     else static if (isDynamicArray!PT)
484                         prop ~= mixin(interp!`set => ${setterInterfaceName}(${is(T == class) ? "" : "ref "}this, value.ToSlice()); `);
485                     else
486                         prop ~= mixin(interp!`set => ${setterInterfaceName}(${is(T == class) ? "" : "ref "}this, value); `);
487                 }
488 
489                 prop ~= "}" ~ newline;
490                 csagg.properties ~= prop;
491             }
492         }
493     }
494 }
495 
496 private void generateFields(T)(string libraryName) if (is(T == class) || is(T == interface) || is(T == struct)) {
497     import autowrap.csharp.common : getDLangInterfaceName, verifySupported;
498     import std.traits : moduleName, isDynamicArray, fullyQualifiedName, Fields, FieldNameTuple;
499 
500     alias fqn = fullyQualifiedName!T;
501     const string aggName = __traits(identifier, T);
502     CSharpAggregate csagg = getAggregate(getCSharpName(moduleName!T), getCSharpName(aggName), !is(T == class));
503 
504     alias FieldTypes = Fields!T;
505     alias fieldNames = FieldNameTuple!T;
506 
507     if (is(T == class) || is(T == interface))
508     {
509         static foreach(fc; 0..FieldTypes.length)
510         {{
511             alias FT = FieldTypes[fc];
512             static if(verifySupported!FT)
513             {
514                 alias fn = fieldNames[fc];
515                 static if (is(typeof(__traits(getMember, T, fn))))
516                 {
517                     csagg.properties ~= dllImportString.format(libraryName, getDLangInterfaceName(fqn, fn ~ "_get"));
518                     csagg.properties ~= mixin(interp!"        private static extern ${getDLangReturnType!(FT, T)} dlang_${fn}_get(IntPtr ptr);${newline}");
519                     if (is(FT == bool)) {
520                         csagg.properties ~= dllImportString.format(libraryName, getDLangInterfaceName(fqn, fn ~ "_set"));
521                         csagg.properties ~= mixin(interp!"        private static extern void dlang_${fn}_set(IntPtr ptr, [MarshalAs(UnmanagedType.Bool)] ${getDLangInterfaceType!(FT, T)()} value);${newline}");
522                     } else if (is(FT == class)) {
523                         csagg.properties ~= dllImportString.format(libraryName, getDLangInterfaceName(fqn, fn ~ "_set"));
524                         csagg.properties ~= mixin(interp!"        private static extern void dlang_${fn}_set(IntPtr ptr, IntPtr value);${newline}");
525                     } else {
526                         csagg.properties ~= dllImportString.format(libraryName, getDLangInterfaceName(fqn, fn ~ "_set"));
527                         csagg.properties ~= mixin(interp!"        private static extern void dlang_${fn}_set(IntPtr ptr, ${getDLangInterfaceType!(FT, T)()} value);${newline}");
528                     }
529 
530                     string memberName = getCSMemberName(aggName, fn);
531 
532                     if (is(FT == string)) {
533                         csagg.properties ~= mixin(interp!"        public string ${memberName} { get => SharedFunctions.SliceToString(dlang_${fn}_get(this), DStringType._string); set => dlang_${fn}_set(this, SharedFunctions.CreateString(value)); }${newline}");
534                     } else if (is(FT == wstring)) {
535                         csagg.properties ~= mixin(interp!"        public string ${memberName} { get => SharedFunctions.SliceToString(dlang_${fn}_get(this), DStringType._wstring); set => dlang_${fn}_set(this, SharedFunctions.CreateWstring(value)); }${newline}");
536                     } else if (is(FT == dstring)) {
537                         csagg.properties ~= mixin(interp!"        public string ${memberName} { get => SharedFunctions.SliceToString(dlang_${fn}_get(this), DStringType._dstring); set => dlang_${fn}_set(this, SharedFunctions.CreateDstring(value)); }${newline}");
538                     } else if (is(FT == string[])) {
539                         csagg.properties ~= mixin(interp!"        public Range<string> ${memberName} { get => new Range<string>(dlang_${fn}_get(this), DStringType._string); set => dlang_${fn}_set(this, value.ToSlice(DStringType._string)); }${newline}");
540                     } else if (is(FT == wstring[])) {
541                         csagg.properties ~= mixin(interp!"        public Range<string> ${memberName} { get => new Range<string>(dlang_${fn}_get(this), DStringType._wstring); set => dlang_${fn}_set(this, value.ToSlice(DStringType._wstring)); }${newline}");
542                     } else if (is(FT == dstring[])) {
543                         csagg.properties ~= mixin(interp!"        public Range<string> ${memberName} { get => new Range<string>(dlang_${fn}_get(this), DStringType._dstring); set => dlang_${fn}_set(this, value.ToSlice(DStringType._dstring)); }${newline}");
544                     } else if (isDynamicArray!FT) {
545                         csagg.properties ~= mixin(interp!"        public Range<${getCSharpInterfaceType(fullyQualifiedName!(FT))}> ${memberName} { get => new Range<${getCSharpInterfaceType(fullyQualifiedName!(FT))}>(dlang_${fn}_get(this), DStringType.None); set => dlang_${fn}_set(this, value.ToSlice()); }${newline}");
546                     } else if (is(FT == class)) {
547                         csagg.properties ~= mixin(interp!"        public ${getCSharpInterfaceType(fullyQualifiedName!(FT))} ${memberName} { get => new ${getCSharpInterfaceType(fullyQualifiedName!(FT))}(dlang_${fn}_get(this)); set => dlang_${fn}_set(this, value); }${newline}");
548                     } else {
549                         csagg.properties ~= mixin(interp!"        public ${getCSharpInterfaceType(fullyQualifiedName!(FT))} ${memberName} { get => dlang_${fn}_get(this); set => dlang_${fn}_set(this, value); }${newline}");
550                     }
551                 }
552             }
553         }}
554     }
555     else if(is(T == struct))
556     {
557         static foreach(fc; 0..FieldTypes.length)
558         {{
559             alias FT = FieldTypes[fc];
560             static if(verifySupported!FT)
561             {
562                 alias fn = fieldNames[fc];
563                 static if (is(typeof(__traits(getMember, T, fn))))
564                 {
565                     auto nameTuple = getCSFieldNameTuple(aggName, fn);
566                     auto pubName = nameTuple.public_;
567                     auto privName = nameTuple.private_;
568 
569                     if (isDynamicArray!FT) {
570                         csagg.properties ~= mixin(interp!"        private slice ${privName};${newline}");
571                         if (is(FT == string)) {
572                             csagg.properties ~= mixin(interp!"        public string ${pubName} { get => SharedFunctions.SliceToString(${privName}, DStringType._string); set => ${privName} = SharedFunctions.CreateString(value); }${newline}");
573                         } else if (is(FT == wstring)) {
574                             csagg.properties ~= mixin(interp!"        public string ${pubName} { get => SharedFunctions.SliceToString(${privName}, DStringType._wstring); set => ${privName} = SharedFunctions.CreateWstring(value); }${newline}");
575                         } else if (is(FT == dstring)) {
576                             csagg.properties ~= mixin(interp!"        public string ${pubName} { get => SharedFunctions.SliceToString(${privName}, DStringType._dstring); set => ${privName} = SharedFunctions.CreateDstring(value); }${newline}");
577                         } else if (is(FT == string[])) {
578                             csagg.properties ~= mixin(interp!"        public Range<string> ${pubName} { get => new Range<string>(_${fn}, DStringType._string); set => _${fn} = value.ToSlice(DStringType._string); }${newline}");
579                         } else if (is(FT == wstring[])) {
580                             csagg.properties ~= mixin(interp!"        public Range<string> ${pubName} { get => new Range<string>(_${fn}, DStringType._wstring); set => _${fn} = value.ToSlice(DStringType._wstring); }${newline}");
581                         } else if (is(FT == dstring[])) {
582                             csagg.properties ~= mixin(interp!"        public Range<string> ${pubName} { get => new Range<string>(_${fn}, DStringType._dstring); set => _${fn} = value.ToSlice(DStringType._dstring); }${newline}");
583                         } else {
584                             csagg.properties ~= mixin(interp!"        public Range<${getCSharpInterfaceType(fullyQualifiedName!(FT))}> ${pubName} { get => new Range<${getCSharpInterfaceType(fullyQualifiedName!(FT))}>(_${fn}, DStringType.None); set => _${fn} = value.ToSlice(); }${newline}");
585                         }
586                     } else if (is(FT == bool)) {
587                         csagg.properties ~= mixin(interp!"        [MarshalAs(UnmanagedType.U1)] public ${getDLangInterfaceType!(FT, T)()} ${pubName};${newline}");
588                     } else if (is(FT == class)) {
589                         csagg.properties ~= mixin(interp!"        private IntPtr ${privName};${newline}");
590                         csagg.properties ~= mixin(interp!"        public ${getCSharpInterfaceType(fullyQualifiedName!(FT))} ${pubName} { get => new ${getCSharpInterfaceType(fullyQualifiedName!(FT))}(_${fn}); set => _${fn} = value); }${newline}");
591                     } else {
592                         csagg.properties ~= mixin(interp!"        public ${getDLangInterfaceType!(FT, T)()} ${pubName};${newline}");
593                     }
594                 }
595             }
596         }}
597     }
598 }
599 
600 private void generateFunctions(Modules...)(string libraryName)
601     if(allSatisfy!(isModule, Modules))
602 {
603     import autowrap.csharp.common : getDLangInterfaceName, numDefaultArgs, verifySupported;
604     import autowrap.reflection: AllFunctions;
605 
606     import std.format : format;
607     import std.meta : AliasSeq, Filter;
608     import std.traits : isDynamicArray, fullyQualifiedName, ReturnType, Parameters, ParameterIdentifierTuple;
609 
610     foreach(func; AllFunctions!Modules)
611     {
612         foreach(oc, overload; __traits(getOverloads, func.module_, func.name))
613         {
614             alias RT = ReturnType!overload;
615             alias ParamTypes = Parameters!overload;
616             alias Types = AliasSeq!(RT, ParamTypes);
617 
618             static if(Filter!(verifySupported, Types).length != Types.length)
619                 continue;
620             else
621             {
622                 static foreach(nda; 0 .. numDefaultArgs!overload + 1)
623                 {{
624                     enum numParams = ParamTypes.length - nda;
625                     enum interfaceName = format("%s%s_%s", getDLangInterfaceName(func.moduleName, null, func.name), oc, numParams);
626                     enum methodName = getCSharpMethodInterfaceName(null, func.name);
627                     enum methodInterfaceName = format("dlang_%s", methodName);
628 
629                     enum returnTypeStr = getCSharpInterfaceType(fullyQualifiedName!RT);
630                     alias paramNames = ParameterIdentifierTuple!overload;
631 
632                     string retType = getDLangReturnType!RT();
633                     string funcStr = dllImportString.format(libraryName, interfaceName) ~ newline;
634                     funcStr ~= mixin(interp!"        private static extern ${retType} ${methodInterfaceName}(");
635                     static foreach(pc; 0 .. numParams)
636                         funcStr ~= mixin(interp!"${is(ParamTypes[pc] == bool) ? \"[MarshalAs(UnmanagedType.Bool)]\" : string.init}${getDLangInterfaceType!(ParamTypes[pc])()} ${paramNames[pc]}, ");
637 
638                     if (numParams != 0)
639                         funcStr = funcStr[0 .. $ - 2];
640 
641                     funcStr ~= ");" ~ newline;
642 
643                     if (is(RT == string) || is(RT == wstring) || is(RT == dstring))
644                         funcStr ~= mixin(interp!"        public static string ${methodName}(");
645                     else if (isDynamicArray!RT)
646                         funcStr ~= mixin(interp!"        public static Range<${returnTypeStr}> ${methodName}(");
647                     else
648                         funcStr ~= mixin(interp!"        public static ${returnTypeStr} ${methodName}(");
649 
650                     static foreach(pc; 0 .. numParams)
651                     {
652                         if (is(ParamTypes[pc] == string) || is(ParamTypes[pc] == wstring) || is(ParamTypes[pc] == dstring))
653                             funcStr ~= mixin(interp!"${getCSharpInterfaceType(fullyQualifiedName!(ParamTypes[pc]))} ${paramNames[pc]}, ");
654                         else if (isDynamicArray!(ParamTypes[pc]))
655                             funcStr ~= mixin(interp!"Range<${getCSharpInterfaceType(fullyQualifiedName!(ParamTypes[pc]))}> ${paramNames[pc]}, ");
656                         else
657                             funcStr ~= mixin(interp!"${getCSharpInterfaceType(fullyQualifiedName!(ParamTypes[pc]))} ${paramNames[pc]}, ");
658                     }
659 
660                     if (numParams != 0)
661                         funcStr = funcStr[0 .. $ - 2];
662 
663                     funcStr ~= ") {" ~ newline;
664                     funcStr ~= mixin(interp!"            var dlang_ret = ${methodInterfaceName}(");
665 
666                     static foreach(pc; 0 .. numParams)
667                     {
668                         if (is(ParamTypes[pc] == string))
669                             funcStr ~= mixin(interp!"SharedFunctions.CreateString(${paramNames[pc]}), ");
670                         else if (is(ParamTypes[pc] == wstring))
671                             funcStr ~= mixin(interp!"SharedFunctions.CreateWString(${paramNames[pc]}), ");
672                         else if (is(ParamTypes[pc] == dstring))
673                             funcStr ~= mixin(interp!"SharedFunctions.CreateDString(${paramNames[pc]}), ");
674                         else if (isDynamicArray!(ParamTypes[pc]))
675                             funcStr ~= mixin(interp!"${paramNames[pc]}.ToSlice(), ");
676                         else
677                             funcStr ~= mixin(interp!"${paramNames[pc]}, ");
678                     }
679 
680                     if (numParams != 0)
681                         funcStr = funcStr[0 .. $ - 2];
682 
683                     funcStr ~= ");" ~ newline;
684 
685                     if (is(RT == void))
686                         funcStr ~= "            dlang_ret.EnsureValid();" ~ newline;
687                     else
688                     {
689                         if (is(RT == string))
690                             funcStr ~= "            return SharedFunctions.SliceToString(dlang_ret, DStringType._string);" ~ newline;
691                         else if (is(RT == wstring))
692                             funcStr ~= "            return SharedFunctions.SliceToString(dlang_ret, DStringType._wstring);" ~ newline;
693                         else if (is(RT == dstring))
694                             funcStr ~= "            return SharedFunctions.SliceToString(dlang_ret, DStringType._dstring);" ~ newline;
695                         else if (is(RT == string[]))
696                             funcStr ~= mixin(interp!"            return new Range<string>(dlang_ret, DStringType._string);${newline}");
697                         else if (is(RT == wstring[]))
698                             funcStr ~= mixin(interp!"            return new Range<string>(dlang_ret, DStringType._wstring);${newline}");
699                         else if (is(RT == dstring[]))
700                             funcStr ~= mixin(interp!"            return new Range<string>(dlang_ret, DStringType._dstring);${newline}");
701                         else if (isDynamicArray!RT)
702                             funcStr ~= mixin(interp!"            return new Range<${returnTypeStr}>(dlang_ret, DStringType.None);${newline}");
703                         else
704                             funcStr ~= "            return dlang_ret;" ~ newline;
705                     }
706 
707                     funcStr ~= "        }" ~ newline;
708 
709                     getNamespace(getCSharpName(func.moduleName)).functions ~= funcStr;
710                 }}
711             }
712         }
713     }
714 }
715 
716 private string getDLangInterfaceType(T, Parent...)()
717     if(Parent.length <= 1)
718 {
719     import std.traits : fullyQualifiedName, isBuiltinType;
720 
721     static if(!isBuiltinType!T && Parent.length == 1)
722     {
723         static if(is(T == Parent[0]))
724             enum typeName = __traits(identifier, T);
725         else
726             enum typeName = fullyQualifiedName!T;
727     }
728     else
729         enum typeName = fullyQualifiedName!T;
730 
731     if (typeName[$-2..$] == "[]") return "slice";
732 
733     switch(typeName)
734     {
735         //Types that require special marshalling types
736         case voidTypeString: return "void";
737         case stringTypeString: return "slice";
738         case wstringTypeString: return "slice";
739         case dstringTypeString: return "slice";
740         case sysTimeTypeString: return "datetime";
741         case dateTimeTypeString: return "datetime";
742         case timeOfDayTypeString: return "datetime";
743         case dateTypeString: return "datetime";
744         case durationTypeString: return "datetime";
745         case boolTypeString: return "bool";
746 
747         //Types that can be marshalled by default
748         case charTypeString: return "byte";
749         case wcharTypeString: return "char";
750         case dcharTypeString: return "uint";
751         case ubyteTypeString: return "byte";
752         case byteTypeString: return "sbyte";
753         case ushortTypeString: return "ushort";
754         case shortTypeString: return "short";
755         case uintTypeString: return "uint";
756         case intTypeString: return "int";
757         case ulongTypeString: return "ulong";
758         case longTypeString: return "long";
759         case floatTypeString: return "float";
760         case doubleTypeString: return "double";
761         default: return getCSharpName(typeName);
762     }
763 }
764 
765 private string getCSharpInterfaceType(string type) {
766     if (type[$-2..$] == "[]") type = type[0..$-2];
767 
768     switch (type) {
769         //Types that require special marshalling types
770         case voidTypeString: return "void";
771         case stringTypeString: return "string";
772         case wstringTypeString: return "string";
773         case dstringTypeString: return "string";
774         case sysTimeTypeString: return "DateTimeOffset";
775         case dateTimeTypeString: return "DateTime";
776         case timeOfDayTypeString: return "DateTime";
777         case dateTypeString: return "DateTime";
778         case durationTypeString: return "TimeSpan";
779         case boolTypeString: return "bool";
780 
781         //Types that can be marshalled by default
782         case charTypeString: return "byte";
783         case wcharTypeString: return "char";
784         case dcharTypeString: return "uint";
785         case ubyteTypeString: return "byte";
786         case byteTypeString: return "sbyte";
787         case ushortTypeString: return "ushort";
788         case shortTypeString: return "short";
789         case uintTypeString: return "uint";
790         case intTypeString: return "int";
791         case ulongTypeString: return "ulong";
792         case longTypeString: return "long";
793         case floatTypeString: return "float";
794         case doubleTypeString: return "double";
795         default: return getCSharpName(type);
796     }
797 }
798 
799 private string getDLangReturnType(T, Parent...)()
800     if(Parent.length <= 1)
801 {
802     import std.algorithm : among;
803     import std.traits : fullyQualifiedName;
804 
805     enum rtname = getReturnErrorTypeName(getDLangInterfaceType!T());
806 
807     static if(Parent.length == 1)
808     {
809         static if(is(T == Parent[0]))
810             enum typeName = __traits(identifier, T);
811         else
812             enum typeName = fullyQualifiedName!T;
813     }
814     else
815         enum typeName = fullyQualifiedName!T;
816 
817     //These types have predefined C# types.
818     if (typeName.among(dateTypeString, dateTimeTypeString, sysTimeTypeString, timeOfDayTypeString, durationTypeString,
819         voidTypeString, boolTypeString, stringTypeString, wstringTypeString, dstringTypeString) || rtname == "return_slice_error") {
820         return rtname;
821     }
822 
823     string typeStr = "    [GeneratedCodeAttribute(\"Autowrap\", \"1.0.0.0\")]
824     [StructLayout(LayoutKind.Sequential)]
825     internal struct " ~ rtname ~ " {
826         private void EnsureValid() {
827             var errStr = SharedFunctions.SliceToString(_error, DStringType._wstring);
828             if (!string.IsNullOrEmpty(errStr)) throw new DLangException(errStr);
829         }
830 ";
831 
832     static if (is(T == class) || is(T == interface))
833     {
834         typeStr ~= mixin(interp!"        public static implicit operator IntPtr(${rtname} ret) { ret.EnsureValid(); return ret._value; }${newline}");
835         typeStr ~= "        private IntPtr _value;" ~ newline;
836     }
837     else
838     {
839         typeStr ~= mixin(interp!"        public static implicit operator ${getCSharpInterfaceType(typeName)}(${rtname} ret) { ret.EnsureValid(); return ret._value; }${newline}");
840         typeStr ~= mixin(interp!"        private ${getCSharpInterfaceType(typeName)} _value;${newline}");
841     }
842     typeStr ~= "        private slice _error;" ~ newline;
843     typeStr ~= "    }" ~ newline ~ newline;
844 
845     if ((rtname in returnTypes) is null) {
846         returnTypes[rtname] = typeStr;
847     }
848     return rtname;
849 }
850 
851 private string getCSharpMethodInterfaceName(string aggName, string funcName) {
852     import autowrap.csharp.common : camelToPascalCase;
853     import std.string : split;
854     import std.string : replace;
855 
856     if (aggName == "DateTime" || aggName == "DateTimeOffset" || aggName == "TimeSpan") {
857         aggName = "Datetime";
858     }
859 
860     string name = string.init;
861     if (aggName !is null && aggName != string.init) {
862         name ~= camelToPascalCase(aggName) ~ "_";
863     }
864     name ~= camelToPascalCase(funcName);
865     return name.replace(".", "_");
866 }
867 
868 private string getReturnErrorTypeName(string aggName) {
869     import std.string : split;
870     import std.array : join;
871     return mixin(interp!"return_${aggName.split(\".\").join(\"_\")}_error");
872 }
873 
874 private string getCSharpName(string dlangName) {
875     import autowrap.csharp.common : camelToPascalCase;
876     import std.algorithm : map;
877     import std.string : split;
878     import std.array : join;
879     return dlangName.split(".").map!camelToPascalCase.join(".");
880 }
881 
882 private CSharpNamespace getNamespace(string name) {
883     return namespaces.require(name, new CSharpNamespace(name));
884 }
885 
886 private CSharpAggregate getAggregate(string namespace, string name, bool isStruct) {
887     CSharpNamespace ns = namespaces.require(namespace, new CSharpNamespace(namespace));
888     return ns.aggregates.require(name, new CSharpAggregate(name, isStruct));
889 }
890 
891 private void generateRangeDef(T)(string libraryName) {
892     import autowrap.csharp.common : getDLangSliceInterfaceName;
893     import std.traits : fullyQualifiedName;
894 
895     alias fqn = fullyQualifiedName!T;
896     const string csn = getCSharpName(fqn);
897 
898     rangeDef.functions ~= dllImportString.format(libraryName, getDLangSliceInterfaceName(fqn, "Create"));
899     rangeDef.functions ~= externFuncString.format("return_slice_error", getCSharpMethodInterfaceName(fqn, "Create"), "IntPtr capacity");
900     rangeDef.functions ~= dllImportString.format(libraryName, getDLangSliceInterfaceName(fqn, "Slice"));
901     rangeDef.functions ~= externFuncString.format("return_slice_error", getCSharpMethodInterfaceName(fqn, "Slice"), "slice dslice, IntPtr begin, IntPtr end");
902     rangeDef.functions ~= dllImportString.format(libraryName, getDLangSliceInterfaceName(fqn, "AppendSlice"));
903     rangeDef.functions ~= externFuncString.format("return_slice_error", getCSharpMethodInterfaceName(fqn, "AppendSlice"), "slice dslice, slice array");
904     rangeDef.constructors ~= mixin(interp!"            else if (typeof(T) == typeof(${getCSharpInterfaceType(fqn)})) this._slice = RangeFunctions.${getCSharpMethodInterfaceName(fqn, \"Create\")}(new IntPtr(capacity));${newline}");
905     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}");
906     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}");
907     rangeDef.setters ~= mixin(interp!"                else if (typeof(T) == typeof(${getCSharpInterfaceType(fqn)})) RangeFunctions.${getCSharpMethodInterfaceName(fqn, \"Set\")}(_slice, new IntPtr(i), (${getCSharpInterfaceType(fqn)})(object)value);${newline}");
908     rangeDef.appendValues ~= mixin(interp!"            else if (typeof(T) == typeof(${getCSharpInterfaceType(fqn)})) { this._slice = RangeFunctions.${getCSharpMethodInterfaceName(fqn, \"AppendValue\")}(this._slice, (${getCSharpInterfaceType(fqn)})(object)value); }${newline}");
909     rangeDef.appendArrays ~= mixin(interp!"            else if (typeof(T) == typeof(${getCSharpInterfaceType(fqn)})) { range._slice = RangeFunctions.${getCSharpMethodInterfaceName(fqn, \"AppendSlice\")}(range._slice, source._slice); return range; }${newline}");
910     if (is(T == class) || is(T == interface))
911     {
912         rangeDef.functions ~= dllImportString.format(libraryName, getDLangSliceInterfaceName(fqn, "Get"));
913         rangeDef.functions ~= externFuncString.format(getDLangReturnType!T(), getCSharpMethodInterfaceName(fqn, "Get"), "slice dslice, IntPtr index");
914         rangeDef.functions ~= dllImportString.format(libraryName, getDLangSliceInterfaceName(fqn, "Set"));
915         rangeDef.functions ~= externFuncString.format("return_void_error", getCSharpMethodInterfaceName(fqn, "Set"), "slice dslice, IntPtr index, IntPtr value");
916         rangeDef.functions ~= dllImportString.format(libraryName, getDLangSliceInterfaceName(fqn, "AppendValue"));
917         rangeDef.functions ~= externFuncString.format("return_slice_error", getCSharpMethodInterfaceName(fqn, "AppendValue"), "slice dslice, IntPtr value");
918         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}");
919         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}");
920     }
921     else
922     {
923         enum dlangIT = getDLangInterfaceType!T;
924         rangeDef.functions ~= dllImportString.format(libraryName, getDLangSliceInterfaceName(fqn, "Get"));
925         rangeDef.functions ~= externFuncString.format(getDLangReturnType!T(), getCSharpMethodInterfaceName(fqn, "Get"), "slice dslice, IntPtr index");
926         rangeDef.functions ~= dllImportString.format(libraryName, getDLangSliceInterfaceName(fqn, "Set"));
927         rangeDef.functions ~= externFuncString.format("return_void_error", getCSharpMethodInterfaceName(fqn, "Set"), mixin(interp!"slice dslice, IntPtr index, ${dlangIT} value"));
928         rangeDef.functions ~= dllImportString.format(libraryName, getDLangSliceInterfaceName(fqn, "AppendValue"));
929         rangeDef.functions ~= externFuncString.format("return_slice_error", getCSharpMethodInterfaceName(fqn, "AppendValue"), mixin(interp!"slice dslice, ${dlangIT} value"));
930         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}");
931         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}");
932     }
933 }
934 
935 private void generateSliceBoilerplate(string libraryName) {
936     import std.datetime: DateTime, SysTime, Duration;
937 
938     void generateStringBoilerplate(string dlangType, string csharpType) {
939         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}");
940         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}");
941         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}");
942         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}");
943         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}");
944         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}");
945         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}");
946 
947         rangeDef.functions ~= dllImportString.format(libraryName, mixin(interp!"autowrap_csharp_${csharpType}_Create"));
948         rangeDef.functions ~= externFuncString.format("return_slice_error", csharpType ~ "_Create", "IntPtr capacity");
949         rangeDef.functions ~= dllImportString.format(libraryName, mixin(interp!"autowrap_csharp_${csharpType}_Get"));
950         rangeDef.functions ~= externFuncString.format("return_slice_error", csharpType ~ "_Get", "slice dslice, IntPtr index");
951         rangeDef.functions ~= dllImportString.format(libraryName, mixin(interp!"autowrap_csharp_${csharpType}_Set"));
952         rangeDef.functions ~= externFuncString.format("return_void_error", csharpType ~ "_Set", "slice dslice, IntPtr index, slice value");
953         rangeDef.functions ~= dllImportString.format(libraryName, mixin(interp!"autowrap_csharp_${csharpType}_Slice"));
954         rangeDef.functions ~= externFuncString.format("return_slice_error", csharpType ~ "_Slice", "slice dslice, IntPtr begin, IntPtr end");
955         rangeDef.functions ~= dllImportString.format(libraryName, mixin(interp!"autowrap_csharp_${csharpType}_AppendValue"));
956         rangeDef.functions ~= externFuncString.format("return_slice_error", csharpType ~ "_AppendValue", "slice dslice, slice value");
957         rangeDef.functions ~= dllImportString.format(libraryName, mixin(interp!"autowrap_csharp_${csharpType}_AppendSlice"));
958         rangeDef.functions ~= externFuncString.format("return_slice_error", csharpType ~ "_AppendSlice", "slice dslice, slice array");
959     }
960 
961     //bool
962     rangeDef.functions ~= dllImportString.format(libraryName, "autowrap_csharp_Bool_Create");
963     rangeDef.functions ~= externFuncString.format("return_slice_error", "Bool_Create", "IntPtr capacity");
964     rangeDef.functions ~= dllImportString.format(libraryName, "autowrap_csharp_Bool_Get");
965     rangeDef.functions ~= "        [return: MarshalAs(UnmanagedType.Bool)]" ~ newline;
966     rangeDef.functions ~= externFuncString.format("return_bool_error", "Bool_Get", "slice dslice, IntPtr index");
967     rangeDef.functions ~= dllImportString.format(libraryName, "autowrap_csharp_Bool_Set");
968     rangeDef.functions ~= externFuncString.format("return_void_error", "Bool_Set", "slice dslice, IntPtr index, [MarshalAs(UnmanagedType.Bool)] bool value");
969     rangeDef.functions ~= dllImportString.format(libraryName, "autowrap_csharp_Bool_Slice");
970     rangeDef.functions ~= externFuncString.format("return_slice_error", "Bool_Slice", "slice dslice, IntPtr begin, IntPtr end");
971     rangeDef.functions ~= dllImportString.format(libraryName, "autowrap_csharp_Bool_AppendValue");
972     rangeDef.functions ~= externFuncString.format("return_slice_error", "Bool_AppendValue", "slice dslice, [MarshalAs(UnmanagedType.Bool)] bool value");
973     rangeDef.functions ~= dllImportString.format(libraryName, "autowrap_csharp_Bool_AppendSlice");
974     rangeDef.functions ~= externFuncString.format("return_slice_error", "Bool_AppendSlice", "slice dslice, slice array");
975     rangeDef.constructors ~= "            if (typeof(T) == typeof(bool)) this._slice = RangeFunctions.Bool_Create(new IntPtr(capacity));" ~ newline;
976     rangeDef.enumerators ~= "                if (typeof(T) == typeof(bool)) yield return (T)(object)RangeFunctions.Bool_Get(_slice, new IntPtr(i));" ~ newline;
977     rangeDef.getters ~= "                if (typeof(T) == typeof(bool)) return (T)(object)RangeFunctions.Bool_Get(_slice, new IntPtr(i));" ~ newline;
978     rangeDef.setters ~= "                if (typeof(T) == typeof(bool)) RangeFunctions.Bool_Set(_slice, new IntPtr(i), (bool)(object)value);" ~ newline;
979     rangeDef.sliceEnd ~= "            if (typeof(T) == typeof(bool)) return new Range<T>(RangeFunctions.Bool_Slice(_slice, new IntPtr(begin), _slice.length), DStringType.None);" ~ newline;
980     rangeDef.sliceRange ~= "            if (typeof(T) == typeof(bool)) return new Range<T>(RangeFunctions.Bool_Slice(_slice, new IntPtr(begin), new IntPtr(end)), DStringType.None);" ~ newline;
981     rangeDef.appendValues ~= "            if (typeof(T) == typeof(bool)) { this._slice = RangeFunctions.Bool_AppendValue(this._slice, (bool)(object)value); }" ~ newline;
982     rangeDef.appendArrays ~= "            if (typeof(T) == typeof(bool)) { range._slice = RangeFunctions.Bool_AppendSlice(range._slice, source._slice); return range; }" ~ newline;
983 
984     rangeDef.enumerators ~= "                else if (typeof(T) == typeof(string) && _strings != null && _type == DStringType.None) yield return (T)(object)_strings[(int)i];" ~ newline;
985     rangeDef.getters ~= "                else if (typeof(T) == typeof(string) && _strings != null && _type == DStringType.None) return (T)(object)_strings[(int)i];" ~ newline;
986     rangeDef.setters ~= "                else if (typeof(T) == typeof(string) && _strings != null && _type == DStringType.None) _strings[(int)i] = (string)(object)value;" ~ newline;
987     rangeDef.sliceEnd ~= "            else if (typeof(T) == typeof(string) && _strings != null && _type == DStringType.None) return new Range<T>((IEnumerable<T>)_strings.Skip((int)begin));" ~ newline;
988     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;
989     rangeDef.appendValues ~= "            else if (typeof(T) == typeof(string) && this._strings != null && this._type == DStringType.None) { this._strings.Add((string)(object)value); }" ~ newline;
990     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;
991 
992     generateStringBoilerplate("string", "String");
993     generateStringBoilerplate("wstring", "Wstring");
994     generateStringBoilerplate("dstring", "Dstring");
995 
996     generateRangeDef!byte(libraryName);
997     generateRangeDef!ubyte(libraryName);
998     generateRangeDef!short(libraryName);
999     generateRangeDef!ushort(libraryName);
1000     generateRangeDef!int(libraryName);
1001     generateRangeDef!uint(libraryName);
1002     generateRangeDef!long(libraryName);
1003     generateRangeDef!ulong(libraryName);
1004     generateRangeDef!float(libraryName);
1005     generateRangeDef!double(libraryName);
1006 
1007     generateRangeDef!DateTime(libraryName);
1008     generateRangeDef!SysTime(libraryName);
1009     generateRangeDef!Duration(libraryName);
1010 }
1011 
1012 private string writeCSharpBoilerplate(string libraryName, string rootNamespace) {
1013     string returnTypesStr = string.init;
1014     foreach(string rt; returnTypes.byValue) {
1015         returnTypesStr ~= rt;
1016     }
1017     immutable string boilerplate = import("Boilerplate.cs");
1018     return boilerplate.format(libraryName, rootNamespace, returnTypesStr, rangeDef.toString());
1019 }
1020 
1021 // C# doesn't allow members to have the same name as the class or struct,
1022 // because it would conflict with how they name constructors.
1023 string getCSMemberName(string dlangTypeName, string dlangMemberName)
1024 {
1025     import autowrap.csharp.common : camelToPascalCase;
1026     string retval = camelToPascalCase(dlangMemberName);
1027     if(retval == getCSharpName(dlangTypeName))
1028         retval ~= "_";
1029     return retval;
1030 }
1031 
1032 Tuple!(string, "public_", string, "private_") getCSFieldNameTuple(string dlangTypeName, string dlangFieldName)
1033 {
1034     import std.typecons : tuple;
1035     auto memberName = getCSMemberName(dlangTypeName, dlangFieldName);
1036     return typeof(return)(memberName, "_" ~ memberName);
1037 }
1038 
1039 private template GetterPropertyType(T, string memberName)
1040 {
1041     import std.meta : AliasSeq, staticMap;
1042     import std.traits : fullyQualifiedName;
1043 
1044     alias GetterPropertyType = staticMap!(GetterType, __traits(getOverloads, T, memberName));
1045     static assert(GetterPropertyType.length <= 1,
1046                   "It should not be possible to have multiple getters with the same name. " ~
1047                   fullyQualifiedName!T ~ "." ~ memberName);
1048 
1049     static template GetterType(alias func)
1050     {
1051         import std.traits : FunctionAttribute, functionAttributes, Parameters, ReturnType;
1052 
1053         static if((functionAttributes!func & FunctionAttribute.property) != 0 &&
1054                   !is(ReturnType!func == void) &&
1055                   Parameters!func.length == 0)
1056         {
1057             alias GetterType = ReturnType!func;
1058         }
1059         else
1060             alias GetterType = AliasSeq!();
1061     }
1062 }
1063 
1064 // This does ignore getters which return by ref, but they can be checked by
1065 // looking at the getter, and it wouldn't work to use such a function as a
1066 // setter without an extra D function around it to call it, which we don't
1067 // currently do. But even if we wanted to handle such a case, it's simpler to
1068 // check the getter than to treat it as another setter.
1069 private template SetterPropertyTypes(T, string memberName)
1070 {
1071     import std.meta : AliasSeq, staticMap;
1072 
1073     alias SetterPropertyTypes = staticMap!(SetterType, __traits(getOverloads, T, memberName));
1074 
1075     static template SetterType(alias func)
1076     {
1077         import std.traits : FunctionAttribute, functionAttributes, Parameters, ReturnType;
1078 
1079         alias ParamTypes = Parameters!func;
1080 
1081         static if(ParamTypes.length == 1 && (functionAttributes!func & FunctionAttribute.property) != 0)
1082             alias SetterType = ParamTypes;
1083         else
1084             alias SetterType = AliasSeq!();
1085     }
1086 }
1087 
1088 // Unfortunately, while these tests have been tested on their own, they don't
1089 // currently run as part of autowrap's tests, because dub test doesn't work for
1090 // the csharp folder, and the top level one does not run them.
1091 unittest
1092 {
1093     import std.meta : AliasSeq;
1094 
1095     static struct S
1096     {
1097         @property void noprop() { assert(0); }
1098         @property void noprop(int, int) { assert(0); }
1099         @property int noprop(int, int) { assert(0); }
1100 
1101         int noprop2() { assert(0); }
1102         void noprop2(string) { assert(0); }
1103         int noprop2(int) { assert(0); }
1104         bool noprop2(float) { assert(0); }
1105 
1106         @property int getter() { assert(0); }
1107         @property void getter(int, int) { assert(0); }
1108         @property string getter(string, int) { assert(0); }
1109 
1110         @property ref string getter2() { assert(0); }
1111         void getter2(string) { assert(0); }
1112         ref int getter2(int) { assert(0); }
1113         bool getter2(float) { assert(0); }
1114 
1115         @property string setter(int) { assert(0); }
1116         @property string setter(float, bool) { assert(0); }
1117         @property int setter(bool, int) { assert(0); }
1118 
1119         int setter2() { assert(0); }
1120         @property void setter2(string) { assert(0); }
1121         @property int setter2(int) { assert(0); }
1122         @property bool setter2(wstring, bool) { assert(0); }
1123         @property bool setter2(float) { assert(0); }
1124 
1125         @property int both() { assert(0); }
1126         @property void both(string) { assert(0); }
1127         @property int both(int) { assert(0); }
1128         @property bool both(float) { assert(0); }
1129     }
1130 
1131     static assert(GetterPropertyType!(S, "noprop").length == 0);
1132     static assert(SetterPropertyTypes!(S, "noprop").length == 0);
1133 
1134     static assert(GetterPropertyType!(S, "noprop2").length == 0);
1135     static assert(SetterPropertyTypes!(S, "noprop2").length == 0);
1136 
1137     static assert(is(GetterPropertyType!(S, "getter") == AliasSeq!int));
1138     static assert(SetterPropertyTypes!(S, "getter").length == 0);
1139 
1140     static assert(is(GetterPropertyType!(S, "getter2") == AliasSeq!string));
1141     static assert(SetterPropertyTypes!(S, "getter2").length == 0);
1142 
1143     static assert(GetterPropertyType!(S, "setter").length == 0);
1144     static assert(is(SetterPropertyTypes!(S, "setter") == AliasSeq!int));
1145 
1146     static assert(GetterPropertyType!(S, "setter2").length == 0);
1147     static assert(is(SetterPropertyTypes!(S, "setter2") == AliasSeq!(string, int, float)));
1148 
1149     static assert(is(GetterPropertyType!(S, "both") == AliasSeq!int));
1150     static assert(is(SetterPropertyTypes!(S, "both") == AliasSeq!(string, int, float)));
1151 }
1152 
1153 private template isNotVoid(T...)
1154     if(T.length == 1)
1155 {
1156     enum isNotVoid = !is(T[0] == void);
1157 }