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