Browse Source

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 <dpldobrev@protonmail.com>
pull/1156/head
Dimitar Dobrev 7 years ago committed by João Matos
parent
commit
a4913509eb
  1. 2
      src/AST/CppTypePrinter.cs
  2. 102
      src/Generator/Generators/CSharp/CSharpMarshal.cs
  3. 9
      src/Generator/Generators/CSharp/CSharpSources.cs
  4. 20
      src/Generator/Generators/CSharp/CSharpTypePrinter.cs
  5. 104
      src/Generator/Types/Std/Stdlib.cs
  6. 33
      src/Generator/Types/TypeMapDatabase.cs
  7. 4
      tests/CSharp/CSharp.cpp
  8. 19
      tests/CSharp/CSharp.cs
  9. 5
      tests/CSharp/CSharp.h

2
src/AST/CppTypePrinter.cs

@ -181,7 +181,7 @@ namespace CppSharp.AST
{ {
FunctionType func; FunctionType func;
if (ResolveTypedefs && !typedef.Declaration.Type.IsPointerTo(out 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); var qual = GetStringQuals(quals);
return $"{qual}{typedef.Declaration.Visit(this)}"; return $"{qual}{typedef.Declaration.Visit(this)}";
} }

102
src/Generator/Generators/CSharp/CSharpMarshal.cs

@ -46,8 +46,6 @@ namespace CppSharp.Generators.CSharp
typePrinter = new CSharpTypePrinter(context.Context); typePrinter = new CSharpTypePrinter(context.Context);
} }
public bool MarshalsParameter { get; set; }
public override bool VisitType(Type type, TypeQualifiers quals) public override bool VisitType(Type type, TypeQualifiers quals)
{ {
TypeMap typeMap; TypeMap typeMap;
@ -132,10 +130,11 @@ namespace CppSharp.Generators.CSharp
// const char* and const char[] are the same so we can use a string // const char* and const char[] are the same so we can use a string
if (array.Type.Desugar().IsPrimitiveType(PrimitiveType.Char) && if (array.Type.Desugar().IsPrimitiveType(PrimitiveType.Char) &&
array.QualifiedType.Qualifiers.IsConst) array.QualifiedType.Qualifiers.IsConst)
return VisitPointerType(new PointerType
{ {
QualifiedPointee = array.QualifiedType var pointer = new PointerType { QualifiedPointee = array.QualifiedType };
}, quals); Context.ReturnType = new QualifiedType(pointer);
return this.VisitPointerType(pointer, quals);
}
MarshalArray(array); MarshalArray(array);
break; break;
case ArrayType.ArraySize.Variable: case ArrayType.ArraySize.Variable:
@ -155,20 +154,13 @@ namespace CppSharp.Generators.CSharp
var isRefParam = param != null && (param.IsInOut || param.IsOut); var isRefParam = param != null && (param.IsInOut || param.IsOut);
var pointee = pointer.Pointee.Desugar(); var pointee = pointer.Pointee.Desugar();
bool marshalPointeeAsString = pointee.IsConstCharString() && isRefParam; var finalPointee = pointer.GetFinalPointee().Desugar();
if ((pointer.IsConstCharString() && !MarshalsParameter) ||
marshalPointeeAsString)
{
Context.Return.Write(MarshalStringToManaged(Context.ReturnVarName,
pointer.GetFinalPointee().Desugar() as BuiltinType));
return true;
}
var finalPointee = pointer.GetFinalPointee();
PrimitiveType primitive; PrimitiveType primitive;
if (finalPointee.IsPrimitiveType(out primitive) || finalPointee.IsEnumType()) if ((pointee.IsConstCharString() && isRefParam) ||
{ (!finalPointee.IsPrimitiveType(out primitive) &&
!finalPointee.IsEnumType()))
return pointer.QualifiedPointee.Visit(this);
if (isRefParam) if (isRefParam)
{ {
Context.Return.Write("_{0}", param.Name); Context.Return.Write("_{0}", param.Name);
@ -203,33 +195,6 @@ namespace CppSharp.Generators.CSharp
return true; return true;
} }
return pointer.QualifiedPointee.Visit(this);
}
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})";
}
public override bool VisitPrimitiveType(PrimitiveType primitive, TypeQualifiers quals) public override bool VisitPrimitiveType(PrimitiveType primitive, TypeQualifiers quals)
{ {
switch (primitive) switch (primitive)
@ -377,11 +342,11 @@ namespace CppSharp.Generators.CSharp
if (dtor != null && dtor.IsVirtual) if (dtor != null && dtor.IsVirtual)
{ {
Context.Before.WriteLine("else {0}{1} = ({2}) {3}.{4}({5}{6});", Context.Before.WriteLine("else {0}{1} = ({2}) {3}.{4}({5}{6});",
MarshalsParameter Context.Parameter != null
? string.Empty ? string.Empty
: string.Format("{0}.NativeToManagedMap[{1}] = ", qualifiedClass, Context.ReturnVarName), : string.Format("{0}.NativeToManagedMap[{1}] = ", qualifiedClass, Context.ReturnVarName),
ret, qualifiedIdentifier, qualifiedClass, Helpers.CreateInstanceIdentifier, Context.ReturnVarName, ret, qualifiedIdentifier, qualifiedClass, Helpers.CreateInstanceIdentifier, Context.ReturnVarName,
MarshalsParameter ? ", skipVTables: true" : string.Empty); Context.Parameter != null ? ", skipVTables: true" : string.Empty);
} }
else else
{ {
@ -554,7 +519,6 @@ namespace CppSharp.Generators.CSharp
if (templateSubstitution != null) if (templateSubstitution != null)
realPointer = templateSubstitution.Replacement.Type.Desugar() as PointerType; realPointer = templateSubstitution.Replacement.Type.Desugar() as PointerType;
realPointer = realPointer ?? pointer; realPointer = realPointer ?? pointer;
var pointee = pointer.Pointee.Desugar();
if (Context.Function != null && if (Context.Function != null &&
(realPointer.IsPrimitiveTypeConvertibleToRef() || (realPointer.IsPrimitiveTypeConvertibleToRef() ||
(templateSubstitution != null && realPointer.Pointee.IsEnumType())) && (templateSubstitution != null && realPointer.Pointee.IsEnumType())) &&
@ -597,23 +561,20 @@ namespace CppSharp.Generators.CSharp
var param = Context.Parameter; var param = Context.Parameter;
var isRefParam = param != null && (param.IsInOut || param.IsOut); var isRefParam = param != null && (param.IsInOut || param.IsOut);
var pointee = pointer.Pointee.Desugar();
if (pointee.IsConstCharString() && isRefParam) if (pointee.IsConstCharString() && isRefParam)
{ {
if (param.IsOut) if (param.IsOut)
{ {
Context.Return.Write("IntPtr.Zero"); Context.Return.Write("IntPtr.Zero");
Context.ArgumentPrefix.Write("&"); Context.ArgumentPrefix.Write("&");
return true;
} }
else if (param.IsInOut) pointer.QualifiedPointee.Visit(this);
{ if (param.IsInOut)
Context.Return.Write(MarshalStringToUnmanaged(Context.Parameter.Name));
Context.ArgumentPrefix.Write("&"); Context.ArgumentPrefix.Write("&");
}
else else
{
Context.Return.Write(MarshalStringToUnmanaged(Context.Parameter.Name));
Context.Cleanup.WriteLine("Marshal.FreeHGlobal({0});", Context.ArgName); Context.Cleanup.WriteLine("Marshal.FreeHGlobal({0});", Context.ArgName);
}
return true; return true;
} }
@ -643,11 +604,9 @@ namespace CppSharp.Generators.CSharp
return true; return true;
} }
var marshalAsString = pointer.IsConstCharString();
var finalPointee = pointer.GetFinalPointee(); var finalPointee = pointer.GetFinalPointee();
PrimitiveType primitive; PrimitiveType primitive;
if (finalPointee.IsPrimitiveType(out primitive) || finalPointee.IsEnumType() || if (finalPointee.IsPrimitiveType(out primitive) || finalPointee.IsEnumType())
marshalAsString)
{ {
// From MSDN: "note that a ref or out parameter is classified as a moveable // 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 // variable". This means we must create a local variable to hold the result
@ -670,24 +629,14 @@ namespace CppSharp.Generators.CSharp
} }
else else
{ {
if (!marshalAsString && if (Context.Context.Options.MarshalCharAsManagedChar &&
Context.Context.Options.MarshalCharAsManagedChar &&
primitive == PrimitiveType.Char) primitive == PrimitiveType.Char)
Context.Return.Write($"({typePrinter.PrintNative(pointer)}) "); 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()) if (qualifiedPointer.IsConstRefToPrimitive())
Context.Return.Write("&"); Context.Return.Write("&");
Context.Return.Write(Context.Parameter.Name); Context.Return.Write(Context.Parameter.Name);
} }
}
return true; return true;
} }
@ -695,21 +644,6 @@ namespace CppSharp.Generators.CSharp
return pointer.QualifiedPointee.Visit(this); 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) public override bool VisitPrimitiveType(PrimitiveType primitive, TypeQualifiers quals)
{ {
switch (primitive) switch (primitive)

9
src/Generator/Generators/CSharp/CSharpSources.cs

@ -927,12 +927,12 @@ namespace CppSharp.Generators.CSharp
if (type.IsPointer()) if (type.IsPointer())
{ {
Type pointee = type.GetFinalPointee(); Type pointee = type.GetFinalPointee();
if (pointee.IsPrimitiveType() && !type.IsConstCharString()) if (pointee.IsPrimitiveType())
{ {
Write($"({CSharpTypePrinter.IntPtrType}) "); Write($"({CSharpTypePrinter.IntPtrType}) ");
var templateSubstitution = pointee.Desugar(false) as TemplateParameterSubstitutionType; var templateSubstitution = pointee.Desugar(false) as TemplateParameterSubstitutionType;
if (templateSubstitution != null) if (templateSubstitution != null)
Write($"(object) "); Write("(object) ");
} }
} }
WriteLine($"{marshal.Context.Return};"); WriteLine($"{marshal.Context.Return};");
@ -1672,11 +1672,12 @@ namespace CppSharp.Generators.CSharp
{ {
ReturnType = param.QualifiedType, ReturnType = param.QualifiedType,
ReturnVarName = param.Name, ReturnVarName = param.Name,
ParameterIndex = i ParameterIndex = i,
Parameter = param
}; };
ctx.PushMarshalKind(MarshalKind.GenericDelegate); ctx.PushMarshalKind(MarshalKind.GenericDelegate);
var marshal = new CSharpMarshalNativeToManagedPrinter(ctx) { MarshalsParameter = true }; var marshal = new CSharpMarshalNativeToManagedPrinter(ctx);
param.Visit(marshal); param.Visit(marshal);
ctx.PopMarshalKind(); ctx.PopMarshalKind();

20
src/Generator/Generators/CSharp/CSharpTypePrinter.cs

@ -184,16 +184,16 @@ namespace CppSharp.Generators.CSharp
if (allowStrings && pointer.IsConstCharString()) if (allowStrings && pointer.IsConstCharString())
{ {
if (isManagedContext) TypeMap typeMap;
return "string"; TypeMapDatabase.FindTypeMap(pointer, out typeMap);
if (Parameter == null || Parameter.Name == Helpers.ReturnIdentifier) var typePrinterContext = new TypePrinterContext()
return IntPtrType; {
if (Options.Encoding == Encoding.ASCII) Kind = Kind,
return string.Format("[MarshalAs(UnmanagedType.LPStr)] string"); MarshalKind = MarshalKind,
if (Options.Encoding == Encoding.Unicode || Type = pointer.Pointee,
Options.Encoding == Encoding.BigEndianUnicode) Parameter = Parameter
return string.Format("[MarshalAs(UnmanagedType.LPWStr)] string"); };
throw new NotSupportedException($"{Options.Encoding.EncodingName} is not supported yet."); return typeMap.CSharpSignatureType(typePrinterContext).Visit(this);
} }
var pointee = pointer.Pointee.Desugar(); var pointee = pointer.Pointee.Desugar();

104
src/Generator/Types/Std/Stdlib.cs

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using CppSharp.AST; using CppSharp.AST;
using CppSharp.AST.Extensions; using CppSharp.AST.Extensions;
using CppSharp.Generators; 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<char, char_traits<char>, allocator<char>>")] [TypeMap("basic_string<char, char_traits<char>, allocator<char>>")]
public class String : TypeMap public class String : TypeMap
{ {

33
src/Generator/Types/TypeMapDatabase.cs

@ -110,16 +110,6 @@ namespace CppSharp.Types
return typeMap.IsEnabled; 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; var template = type as TemplateSpecializationType;
if (template != null) if (template != null)
{ {
@ -131,22 +121,31 @@ namespace CppSharp.Types
out typeMap); out typeMap);
} }
typePrinter.PrintScopeKind = TypePrintScopeKind.Local; Type desugared = type.Desugar();
if (FindTypeMap(type.Visit(typePrinter), out typeMap)) bool printExtra = desugared.GetPointee() != null &&
desugared.GetFinalPointee().Desugar().IsPrimitiveType();
var typePrinter = new CppTypePrinter
{ {
typeMap.Type = type; PrintTypeQualifiers = printExtra,
typeMaps[type] = typeMap; PrintTypeModifiers = printExtra,
return true; PrintLogicalNames = true
} };
typePrinter.PrintScopeKind = TypePrintScopeKind.Qualified; 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)) if (FindTypeMap(type.Visit(typePrinter), out typeMap))
{ {
typeMap.Type = type; typeMap.Type = type;
typeMaps[type] = typeMap; typeMaps[type] = typeMap;
return true; return true;
} }
}
typeMap = null;
var typedef = type as TypedefType; var typedef = type as TypedefType;
return typedef != null && FindTypeMap(typedef.Declaration, type, out typeMap); return typedef != null && FindTypeMap(typedef.Declaration, type, out typeMap);
} }

4
tests/CSharp/CSharp.cpp

@ -2,6 +2,10 @@
#include "CSharp.h" #include "CSharp.h"
Foo::Foo(const QString& name)
{
}
Foo::Foo(const char* name) : publicFieldMappedToEnum(TestFlag::Flag2) Foo::Foo(const char* name) : publicFieldMappedToEnum(TestFlag::Flag2)
{ {
A = 10; A = 10;

19
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 #endregion
} }

5
tests/CSharp/CSharp.h

@ -8,9 +8,14 @@
#include "AnotherUnit.h" #include "AnotherUnit.h"
#include "CSharpTemplates.h" #include "CSharpTemplates.h"
class DLL_API QString
{
};
class DLL_API Foo class DLL_API Foo
{ {
public: public:
Foo(const QString& name);
Foo(const char* name = 0); Foo(const char* name = 0);
Foo(int a, int p = 0); Foo(int a, int p = 0);
Foo(char16_t ch); Foo(char16_t ch);

Loading…
Cancel
Save