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 }