From a4913509ebcb5aae8ec086e087fbf2ed62c1bd99 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Thu, 3 Jan 2019 02:32:42 +0200 Subject: [PATCH] Added type maps for primitive strings (pointers to char). const char*, const char16_t* and const wchar_t* in particular. This enables comparison of types when resolving ambiguity. Signed-off-by: Dimitar Dobrev --- src/AST/CppTypePrinter.cs | 2 +- .../Generators/CSharp/CSharpMarshal.cs | 150 +++++------------- .../Generators/CSharp/CSharpSources.cs | 9 +- .../Generators/CSharp/CSharpTypePrinter.cs | 20 +-- src/Generator/Types/Std/Stdlib.cs | 104 ++++++++++++ src/Generator/Types/TypeMapDatabase.cs | 45 +++--- tests/CSharp/CSharp.cpp | 4 + tests/CSharp/CSharp.cs | 19 +++ tests/CSharp/CSharp.h | 5 + 9 files changed, 212 insertions(+), 146 deletions(-) diff --git a/src/AST/CppTypePrinter.cs b/src/AST/CppTypePrinter.cs index ddafcfd2..36114035 100644 --- a/src/AST/CppTypePrinter.cs +++ b/src/AST/CppTypePrinter.cs @@ -181,7 +181,7 @@ namespace CppSharp.AST { FunctionType func; if (ResolveTypedefs && !typedef.Declaration.Type.IsPointerTo(out func)) - return typedef.Declaration.Type.Visit(this); + return typedef.Declaration.Type.Visit(this, quals); var qual = GetStringQuals(quals); return $"{qual}{typedef.Declaration.Visit(this)}"; } diff --git a/src/Generator/Generators/CSharp/CSharpMarshal.cs b/src/Generator/Generators/CSharp/CSharpMarshal.cs index 744a4f00..af6eee68 100644 --- a/src/Generator/Generators/CSharp/CSharpMarshal.cs +++ b/src/Generator/Generators/CSharp/CSharpMarshal.cs @@ -46,8 +46,6 @@ namespace CppSharp.Generators.CSharp typePrinter = new CSharpTypePrinter(context.Context); } - public bool MarshalsParameter { get; set; } - public override bool VisitType(Type type, TypeQualifiers quals) { TypeMap typeMap; @@ -132,10 +130,11 @@ namespace CppSharp.Generators.CSharp // const char* and const char[] are the same so we can use a string if (array.Type.Desugar().IsPrimitiveType(PrimitiveType.Char) && array.QualifiedType.Qualifiers.IsConst) - return VisitPointerType(new PointerType - { - QualifiedPointee = array.QualifiedType - }, quals); + { + var pointer = new PointerType { QualifiedPointee = array.QualifiedType }; + Context.ReturnType = new QualifiedType(pointer); + return this.VisitPointerType(pointer, quals); + } MarshalArray(array); break; case ArrayType.ArraySize.Variable: @@ -155,79 +154,45 @@ namespace CppSharp.Generators.CSharp var isRefParam = param != null && (param.IsInOut || param.IsOut); var pointee = pointer.Pointee.Desugar(); - bool marshalPointeeAsString = pointee.IsConstCharString() && isRefParam; + var finalPointee = pointer.GetFinalPointee().Desugar(); + PrimitiveType primitive; + if ((pointee.IsConstCharString() && isRefParam) || + (!finalPointee.IsPrimitiveType(out primitive) && + !finalPointee.IsEnumType())) + return pointer.QualifiedPointee.Visit(this); - if ((pointer.IsConstCharString() && !MarshalsParameter) || - marshalPointeeAsString) + if (isRefParam) { - Context.Return.Write(MarshalStringToManaged(Context.ReturnVarName, - pointer.GetFinalPointee().Desugar() as BuiltinType)); + Context.Return.Write("_{0}", param.Name); return true; } - var finalPointee = pointer.GetFinalPointee(); - PrimitiveType primitive; - if (finalPointee.IsPrimitiveType(out primitive) || finalPointee.IsEnumType()) + if (Context.Context.Options.MarshalCharAsManagedChar && + primitive == PrimitiveType.Char) + Context.Return.Write($"({pointer}) "); + + var type = Context.ReturnType.Type.Desugar( + resolveTemplateSubstitution: false); + if (Context.Function != null && + Context.Function.OperatorKind == CXXOperatorKind.Subscript) { - if (isRefParam) + if (type.IsPrimitiveType(primitive)) { - Context.Return.Write("_{0}", param.Name); - return true; + Context.Return.Write("*"); } - - if (Context.Context.Options.MarshalCharAsManagedChar && - primitive == PrimitiveType.Char) - Context.Return.Write($"({pointer}) "); - - var type = Context.ReturnType.Type.Desugar( - resolveTemplateSubstitution: false); - if (Context.Function != null && - Context.Function.OperatorKind == CXXOperatorKind.Subscript) + else { - if (type.IsPrimitiveType(primitive)) - { - Context.Return.Write("*"); - } - else - { - var templateParameter = type as TemplateParameterType; - if (templateParameter != null) - Context.Return.Write($"({templateParameter.Parameter.Name}) (object) *"); - } + var templateParameter = type as TemplateParameterType; + if (templateParameter != null) + Context.Return.Write($"({templateParameter.Parameter.Name}) (object) *"); } - - if (new QualifiedType(pointer, quals).IsConstRefToPrimitive()) - Context.Return.Write("*"); - - Context.Return.Write(Context.ReturnVarName); - return true; } - return pointer.QualifiedPointee.Visit(this); - } + if (new QualifiedType(pointer, quals).IsConstRefToPrimitive()) + Context.Return.Write("*"); - private string MarshalStringToManaged(string varName, BuiltinType type) - { - var isChar = type.Type == PrimitiveType.Char; - var encoding = isChar ? Encoding.ASCII : Encoding.Unicode; - - if (Equals(encoding, Encoding.ASCII)) - encoding = Context.Context.Options.Encoding; - - if (Equals(encoding, Encoding.ASCII)) - return $"Marshal.PtrToStringAnsi({varName})"; - - if (Equals(encoding, Encoding.UTF8)) - return $"Marshal.PtrToStringUTF8({varName})"; - - // If we reach this, we know the string is Unicode. - if (type.Type == PrimitiveType.Char || - Context.Context.TargetInfo.WCharWidth == 16) - return $"Marshal.PtrToStringUni({varName})"; - - // If we reach this, we should have an UTF-32 wide string. - const string encodingName = "System.Text.Encoding.UTF32"; - return $"CppSharp.Runtime.Helpers.MarshalEncodedString({varName}, {encodingName})"; + Context.Return.Write(Context.ReturnVarName); + return true; } public override bool VisitPrimitiveType(PrimitiveType primitive, TypeQualifiers quals) @@ -377,11 +342,11 @@ namespace CppSharp.Generators.CSharp if (dtor != null && dtor.IsVirtual) { Context.Before.WriteLine("else {0}{1} = ({2}) {3}.{4}({5}{6});", - MarshalsParameter + Context.Parameter != null ? string.Empty : string.Format("{0}.NativeToManagedMap[{1}] = ", qualifiedClass, Context.ReturnVarName), ret, qualifiedIdentifier, qualifiedClass, Helpers.CreateInstanceIdentifier, Context.ReturnVarName, - MarshalsParameter ? ", skipVTables: true" : string.Empty); + Context.Parameter != null ? ", skipVTables: true" : string.Empty); } else { @@ -554,7 +519,6 @@ namespace CppSharp.Generators.CSharp if (templateSubstitution != null) realPointer = templateSubstitution.Replacement.Type.Desugar() as PointerType; realPointer = realPointer ?? pointer; - var pointee = pointer.Pointee.Desugar(); if (Context.Function != null && (realPointer.IsPrimitiveTypeConvertibleToRef() || (templateSubstitution != null && realPointer.Pointee.IsEnumType())) && @@ -597,23 +561,20 @@ namespace CppSharp.Generators.CSharp var param = Context.Parameter; var isRefParam = param != null && (param.IsInOut || param.IsOut); + var pointee = pointer.Pointee.Desugar(); if (pointee.IsConstCharString() && isRefParam) { if (param.IsOut) { Context.Return.Write("IntPtr.Zero"); Context.ArgumentPrefix.Write("&"); + return true; } - else if (param.IsInOut) - { - Context.Return.Write(MarshalStringToUnmanaged(Context.Parameter.Name)); + pointer.QualifiedPointee.Visit(this); + if (param.IsInOut) Context.ArgumentPrefix.Write("&"); - } else - { - Context.Return.Write(MarshalStringToUnmanaged(Context.Parameter.Name)); Context.Cleanup.WriteLine("Marshal.FreeHGlobal({0});", Context.ArgName); - } return true; } @@ -643,11 +604,9 @@ namespace CppSharp.Generators.CSharp return true; } - var marshalAsString = pointer.IsConstCharString(); var finalPointee = pointer.GetFinalPointee(); PrimitiveType primitive; - if (finalPointee.IsPrimitiveType(out primitive) || finalPointee.IsEnumType() || - marshalAsString) + if (finalPointee.IsPrimitiveType(out primitive) || finalPointee.IsEnumType()) { // From MSDN: "note that a ref or out parameter is classified as a moveable // variable". This means we must create a local variable to hold the result @@ -670,23 +629,13 @@ namespace CppSharp.Generators.CSharp } else { - if (!marshalAsString && - Context.Context.Options.MarshalCharAsManagedChar && + if (Context.Context.Options.MarshalCharAsManagedChar && primitive == PrimitiveType.Char) Context.Return.Write($"({typePrinter.PrintNative(pointer)}) "); - if (marshalAsString && (Context.MarshalKind == MarshalKind.NativeField || - Context.MarshalKind == MarshalKind.VTableReturnValue || - Context.MarshalKind == MarshalKind.Variable)) - { - Context.Return.Write(MarshalStringToUnmanaged(Context.Parameter.Name)); - } - else - { - if (qualifiedPointer.IsConstRefToPrimitive()) - Context.Return.Write("&"); - Context.Return.Write(Context.Parameter.Name); - } + if (qualifiedPointer.IsConstRefToPrimitive()) + Context.Return.Write("&"); + Context.Return.Write(Context.Parameter.Name); } return true; @@ -695,21 +644,6 @@ namespace CppSharp.Generators.CSharp return pointer.QualifiedPointee.Visit(this); } - private string MarshalStringToUnmanaged(string varName) - { - if (Equals(Context.Context.Options.Encoding, Encoding.ASCII)) - { - return string.Format("Marshal.StringToHGlobalAnsi({0})", varName); - } - if (Equals(Context.Context.Options.Encoding, Encoding.Unicode) || - Equals(Context.Context.Options.Encoding, Encoding.BigEndianUnicode)) - { - return string.Format("Marshal.StringToHGlobalUni({0})", varName); - } - throw new NotSupportedException(string.Format("{0} is not supported yet.", - Context.Context.Options.Encoding.EncodingName)); - } - public override bool VisitPrimitiveType(PrimitiveType primitive, TypeQualifiers quals) { switch (primitive) diff --git a/src/Generator/Generators/CSharp/CSharpSources.cs b/src/Generator/Generators/CSharp/CSharpSources.cs index 78f7f050..99d5a44e 100644 --- a/src/Generator/Generators/CSharp/CSharpSources.cs +++ b/src/Generator/Generators/CSharp/CSharpSources.cs @@ -927,12 +927,12 @@ namespace CppSharp.Generators.CSharp if (type.IsPointer()) { Type pointee = type.GetFinalPointee(); - if (pointee.IsPrimitiveType() && !type.IsConstCharString()) + if (pointee.IsPrimitiveType()) { Write($"({CSharpTypePrinter.IntPtrType}) "); var templateSubstitution = pointee.Desugar(false) as TemplateParameterSubstitutionType; if (templateSubstitution != null) - Write($"(object) "); + Write("(object) "); } } WriteLine($"{marshal.Context.Return};"); @@ -1672,11 +1672,12 @@ namespace CppSharp.Generators.CSharp { ReturnType = param.QualifiedType, ReturnVarName = param.Name, - ParameterIndex = i + ParameterIndex = i, + Parameter = param }; ctx.PushMarshalKind(MarshalKind.GenericDelegate); - var marshal = new CSharpMarshalNativeToManagedPrinter(ctx) { MarshalsParameter = true }; + var marshal = new CSharpMarshalNativeToManagedPrinter(ctx); param.Visit(marshal); ctx.PopMarshalKind(); diff --git a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs index 4deec6c6..dd2a2744 100644 --- a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs +++ b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs @@ -184,16 +184,16 @@ namespace CppSharp.Generators.CSharp if (allowStrings && pointer.IsConstCharString()) { - if (isManagedContext) - return "string"; - if (Parameter == null || Parameter.Name == Helpers.ReturnIdentifier) - return IntPtrType; - if (Options.Encoding == Encoding.ASCII) - return string.Format("[MarshalAs(UnmanagedType.LPStr)] string"); - if (Options.Encoding == Encoding.Unicode || - Options.Encoding == Encoding.BigEndianUnicode) - return string.Format("[MarshalAs(UnmanagedType.LPWStr)] string"); - throw new NotSupportedException($"{Options.Encoding.EncodingName} is not supported yet."); + TypeMap typeMap; + TypeMapDatabase.FindTypeMap(pointer, out typeMap); + var typePrinterContext = new TypePrinterContext() + { + Kind = Kind, + MarshalKind = MarshalKind, + Type = pointer.Pointee, + Parameter = Parameter + }; + return typeMap.CSharpSignatureType(typePrinterContext).Visit(this); } var pointee = pointer.Pointee.Desugar(); diff --git a/src/Generator/Types/Std/Stdlib.cs b/src/Generator/Types/Std/Stdlib.cs index e89f7e69..2d71c34b 100644 --- a/src/Generator/Types/Std/Stdlib.cs +++ b/src/Generator/Types/Std/Stdlib.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using System.Text; using CppSharp.AST; using CppSharp.AST.Extensions; using CppSharp.Generators; @@ -81,6 +82,109 @@ namespace CppSharp.Types.Std } } + [TypeMap("const char*", GeneratorKind = GeneratorKind.CSharp)] + public class ConstCharPointer : TypeMap + { + public override Type CSharpSignatureType(TypePrinterContext ctx) + { + if (ctx.Kind == TypePrinterContextKind.Managed) + return new CILType(typeof(string)); + + if (ctx.Parameter == null || ctx.Parameter.Name == Helpers.ReturnIdentifier) + return new CustomType(CSharpTypePrinter.IntPtrType); + if (Options.Encoding == Encoding.ASCII) + return new CustomType("[MarshalAs(UnmanagedType.LPStr)] string"); + if (Options.Encoding == Encoding.Unicode || + Options.Encoding == Encoding.BigEndianUnicode) + return new CustomType("[MarshalAs(UnmanagedType.LPWStr)] string"); + throw new System.NotSupportedException( + $"{Options.Encoding.EncodingName} is not supported yet."); + } + + public override void CSharpMarshalToNative(CSharpMarshalContext ctx) + { + if (ctx.Parameter.Usage == ParameterUsage.Unknown && + ctx.MarshalKind != MarshalKind.NativeField && + ctx.MarshalKind != MarshalKind.VTableReturnValue && + ctx.MarshalKind != MarshalKind.Variable) + { + ctx.Return.Write(ctx.Parameter.Name); + return; + } + if (Equals(Options.Encoding, Encoding.ASCII)) + { + ctx.Return.Write($"Marshal.StringToHGlobalAnsi({ctx.Parameter.Name})"); + return; + } + if (Equals(Options.Encoding, Encoding.Unicode) || + Equals(Options.Encoding, Encoding.BigEndianUnicode)) + { + ctx.Return.Write($"Marshal.StringToHGlobalUni({ctx.Parameter.Name})"); + return; + } + throw new System.NotSupportedException( + $"{Options.Encoding.EncodingName} is not supported yet."); + } + + public override void CSharpMarshalToManaged(CSharpMarshalContext ctx) + { + if (ctx.Parameter != null && !ctx.Parameter.IsOut && + !ctx.Parameter.IsInOut) + { + ctx.Return.Write(ctx.Parameter.Name); + return; + } + + Type type = ctx.ReturnType.Type.Desugar(); + Type pointee = type.GetPointee().Desugar(); + var isChar = type.IsPointerToPrimitiveType(PrimitiveType.Char) || + (pointee.IsPointerToPrimitiveType(PrimitiveType.Char) && + ctx.Parameter != null && + (ctx.Parameter.IsInOut || ctx.Parameter.IsOut)); + var encoding = isChar ? Encoding.ASCII : Encoding.Unicode; + + if (Equals(encoding, Encoding.ASCII)) + encoding = Options.Encoding; + + if (Equals(encoding, Encoding.ASCII)) + { + ctx.Return.Write($"Marshal.PtrToStringAnsi({ctx.ReturnVarName})"); + return; + } + if (Equals(encoding, Encoding.UTF8)) + { + ctx.Return.Write($"Marshal.PtrToStringUTF8({ctx.ReturnVarName})"); + return; + } + + // If we reach this, we know the string is Unicode. + if (isChar || ctx.Context.TargetInfo.WCharWidth == 16) + { + ctx.Return.Write($"Marshal.PtrToStringUni({ctx.ReturnVarName})"); + return; + } + // If we reach this, we should have an UTF-32 wide string. + const string encodingName = "System.Text.Encoding.UTF32"; + ctx.Return.Write($@"CppSharp.Runtime.Helpers.MarshalEncodedString({ + ctx.ReturnVarName}, {encodingName})"); + } + } + + [TypeMap("const char[]", GeneratorKind = GeneratorKind.CSharp)] + public class ConstCharArray : ConstCharPointer + { + } + + [TypeMap("const wchar_t*", GeneratorKind = GeneratorKind.CSharp)] + public class ConstWCharTPointer : ConstCharPointer + { + } + + [TypeMap("const char16_t*", GeneratorKind = GeneratorKind.CSharp)] + public class ConstChar16TPointer : ConstCharPointer + { + } + [TypeMap("basic_string, allocator>")] public class String : TypeMap { diff --git a/src/Generator/Types/TypeMapDatabase.cs b/src/Generator/Types/TypeMapDatabase.cs index 2100984d..37fc9e1b 100644 --- a/src/Generator/Types/TypeMapDatabase.cs +++ b/src/Generator/Types/TypeMapDatabase.cs @@ -110,16 +110,6 @@ namespace CppSharp.Types return typeMap.IsEnabled; } - Type desugared = type.Desugar(); - bool printExtra = desugared.GetPointee() != null && - desugared.GetFinalPointee().Desugar().IsPrimitiveType(); - var typePrinter = new CppTypePrinter - { - PrintTypeQualifiers = printExtra, - PrintTypeModifiers = printExtra, - PrintLogicalNames = true - }; - var template = type as TemplateSpecializationType; if (template != null) { @@ -131,22 +121,31 @@ namespace CppSharp.Types out typeMap); } - typePrinter.PrintScopeKind = TypePrintScopeKind.Local; - if (FindTypeMap(type.Visit(typePrinter), out typeMap)) + Type desugared = type.Desugar(); + bool printExtra = desugared.GetPointee() != null && + desugared.GetFinalPointee().Desugar().IsPrimitiveType(); + var typePrinter = new CppTypePrinter { - typeMap.Type = type; - typeMaps[type] = typeMap; - return true; - } + PrintTypeQualifiers = printExtra, + PrintTypeModifiers = printExtra, + PrintLogicalNames = true + }; - typePrinter.PrintScopeKind = TypePrintScopeKind.Qualified; - if (FindTypeMap(type.Visit(typePrinter), out typeMap)) - { - typeMap.Type = type; - typeMaps[type] = typeMap; - return true; - } + foreach (var resolveTypeDefs in new[] { true, false }) + foreach (var typePrintScopeKind in + new[] { TypePrintScopeKind.Local, TypePrintScopeKind.Qualified }) + { + typePrinter.ResolveTypedefs = resolveTypeDefs; + typePrinter.PrintScopeKind = typePrintScopeKind; + if (FindTypeMap(type.Visit(typePrinter), out typeMap)) + { + typeMap.Type = type; + typeMaps[type] = typeMap; + return true; + } + } + typeMap = null; var typedef = type as TypedefType; return typedef != null && FindTypeMap(typedef.Declaration, type, out typeMap); } diff --git a/tests/CSharp/CSharp.cpp b/tests/CSharp/CSharp.cpp index 85752135..12cd2113 100644 --- a/tests/CSharp/CSharp.cpp +++ b/tests/CSharp/CSharp.cpp @@ -2,6 +2,10 @@ #include "CSharp.h" +Foo::Foo(const QString& name) +{ +} + Foo::Foo(const char* name) : publicFieldMappedToEnum(TestFlag::Flag2) { A = 10; diff --git a/tests/CSharp/CSharp.cs b/tests/CSharp/CSharp.cs index 9e7244b9..92127e43 100644 --- a/tests/CSharp/CSharp.cs +++ b/tests/CSharp/CSharp.cs @@ -243,6 +243,25 @@ namespace CppSharp.Tests } } + [TypeMap("QString")] + public class QString : TypeMap + { + public override Type CSharpSignatureType(TypePrinterContext ctx) + { + return new CILType(typeof(string)); + } + + public override void CSharpMarshalToNative(CSharpMarshalContext ctx) + { + ctx.Return.Write("\"test\""); + } + + public override void CSharpMarshalToManaged(CSharpMarshalContext ctx) + { + ctx.Return.Write("\"test\""); + } + } + #endregion } diff --git a/tests/CSharp/CSharp.h b/tests/CSharp/CSharp.h index 119c3f1b..326a55c2 100644 --- a/tests/CSharp/CSharp.h +++ b/tests/CSharp/CSharp.h @@ -8,9 +8,14 @@ #include "AnotherUnit.h" #include "CSharpTemplates.h" +class DLL_API QString +{ +}; + class DLL_API Foo { public: + Foo(const QString& name); Foo(const char* name = 0); Foo(int a, int p = 0); Foo(char16_t ch);