From a881a395a832db86e3b14dcce38dafc1a56b41ae Mon Sep 17 00:00:00 2001 From: duckdoom5 Date: Wed, 19 Feb 2025 00:12:43 +0100 Subject: [PATCH] Add optional for csharp --- src/Generator/Types/Std/Stdlib.CSharp.cs | 141 +++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/src/Generator/Types/Std/Stdlib.CSharp.cs b/src/Generator/Types/Std/Stdlib.CSharp.cs index 7a22c8eb..713f0d1c 100644 --- a/src/Generator/Types/Std/Stdlib.CSharp.cs +++ b/src/Generator/Types/Std/Stdlib.CSharp.cs @@ -411,6 +411,147 @@ namespace CppSharp.Types.Std.CSharp } } + [TypeMap("std::optional", GeneratorKindID = GeneratorKind.CSharp_ID)] + public class Optional : TypeMap + { + public override bool IsIgnored + { + get + { + var finalType = Type.GetFinalPointee() ?? Type; + + if (finalType is TagType) + return false; + + if (finalType is not TemplateSpecializationType type) + { + var injectedClassNameType = (InjectedClassNameType)finalType; + type = (TemplateSpecializationType)injectedClassNameType.InjectedSpecializationType.Type; + } + var checker = new TypeIgnoreChecker(TypeMapDatabase); + type.Arguments[0].Type.Visit(checker); + + return checker.IsIgnored; + } + } + + public override Type SignatureType(TypePrinterContext ctx) + { + if (ctx.Kind == TypePrinterContextKind.Managed) + return new CustomType($"System.Nullable<{ctx.GetTemplateParameterList()}>"); + + var typePrinter = new CSharpTypePrinter(null); + typePrinter.PushContext(TypePrinterContextKind.Native); + + if (ctx.Type.Desugar().IsAddress()) + return new CustomType(typePrinter.IntPtrType); + + ClassTemplateSpecialization optionalClass = GetOptionalClass(ctx.Type); + return new CustomType(optionalClass.Visit(typePrinter).Type); + } + + public override void MarshalToNative(MarshalContext ctx) + { + Type type = ctx.Parameter.Type.Desugar(); + ClassTemplateSpecialization optional = GetOptionalClass(type); + var typePrinter = new CSharpTypePrinter(ctx.Context); + + if (!ctx.Parameter.Type.Desugar().IsAddress() && + ctx.MarshalKind != MarshalKind.NativeField) + ctx.Return.Write($"*({typePrinter.PrintNative(optional)}*) "); + + string qualifiedOptional = GetQualifiedOptionalClass(optional); + var assign = optional.Methods.First(m => m.OriginalName == "operator="); + if (ctx.MarshalKind == MarshalKind.NativeField) + { + string var; + if (ctx.ReturnVarName.LastIndexOf('.') > ctx.ReturnVarName.LastIndexOf("->")) + { + var = Generator.GeneratedIdentifier(ctx.ArgName); + ctx.Before.WriteLine($"fixed (void* {var} = &{ctx.ReturnVarName})"); + ctx.Before.WriteOpenBraceAndIndent(); + (ctx as CSharpMarshalContext)!.HasCodeBlock = true; + } + else + { + var = $"&{ctx.ReturnVarName}"; + } + ctx.Return.Write($"{qualifiedOptional}Extensions.{Helpers.InternalStruct}.{assign.Name}(new {typePrinter.IntPtrType}({var}), "); + + ctx.Return.Write($"{ctx.Parameter.Name})"); + ctx.ReturnVarName = string.Empty; + } + else + { + var varOptional = $"__optional{ctx.ParameterIndex}"; + ctx.Before.WriteLine($"var {varOptional} = new {optional.Visit(typePrinter)}();"); + + ctx.Before.Write($"{qualifiedOptional}Extensions.{assign.Name}({varOptional}, "); + + ctx.Before.WriteLine($"{ctx.Parameter.Name});"); + + ctx.Return.Write($"{varOptional}.{Helpers.InstanceIdentifier}"); + ctx.Cleanup.WriteLine($"{varOptional}.Dispose({(!Type.IsAddress() || ctx.Parameter?.IsIndirect == true ? "disposing: true, callNativeDtor:false" : string.Empty)});"); + } + } + + public override void MarshalToManaged(MarshalContext ctx) + { + var type = Type.Desugar(resolveTemplateSubstitution: false); + ClassTemplateSpecialization optional = GetOptionalClass(type); + var value = optional.Methods.First(m => m.OriginalName == "value"); + var typePrinter = new CSharpTypePrinter(ctx.Context); + string qualifiedOptional = GetQualifiedOptionalClass(optional); + string varOptional = $"__optionalRet{ctx.ParameterIndex}"; + + bool usePointer = type.IsAddress() || ctx.MarshalKind == MarshalKind.NativeField || + ctx.MarshalKind == MarshalKind.ReturnVariableArray; + ctx.Before.WriteLine($"var {varOptional} = {optional.Visit(typePrinter)}.{Helpers.CreateInstanceIdentifier}({(usePointer ? string.Empty : $"new {typePrinter.IntPtrType}(&")}{ctx.ReturnVarName}{(usePointer ? string.Empty : ")")});"); + ctx.Before.WriteLine($"if (!{varOptional}.HasValue)"); + ctx.Before.WriteLineIndent("return null;"); + + string retValue = $"{qualifiedOptional}Extensions.{value.Name}({varOptional})"; + if (usePointer) + { + ctx.Return.Write(retValue); + } + else + { + string retString = $"{Generator.GeneratedIdentifier("retValue")}{ctx.ParameterIndex}"; + ctx.Before.WriteLine($"var {retString} = {retValue};"); + ctx.Before.WriteLine($"{varOptional}.Dispose();"); + ctx.Return.Write(retString); + } + } + + private static string GetQualifiedOptionalClass(ClassTemplateSpecialization optional) + { + var declContext = optional.TemplatedDecl.TemplatedDecl; + var names = new Stack(); + while (declContext is not TranslationUnit) + { + var isInlineNamespace = declContext is Namespace { IsInline: true }; + if (!isInlineNamespace) + names.Push(declContext.Name); + declContext = declContext.Namespace; + } + + return $"global::{string.Join(".", names)}"; + } + + private static ClassTemplateSpecialization GetOptionalClass(Type type) + { + var desugared = type.Desugar(); + var template = (desugared.GetFinalPointee() ?? desugared).Desugar(); + + if (template is TemplateSpecializationType templateSpecializationType) + return templateSpecializationType.GetClassTemplateSpecialization(); + + return (ClassTemplateSpecialization)((TagType)template).Declaration; + } + } + + [TypeMap("FILE", GeneratorKindID = GeneratorKind.CSharp_ID)] public class FILE : TypeMap {