diff --git a/src/AST/TypeExtensions.cs b/src/AST/TypeExtensions.cs index f90dfa0f..9d7bfe45 100644 --- a/src/AST/TypeExtensions.cs +++ b/src/AST/TypeExtensions.cs @@ -229,5 +229,20 @@ } return finalPointee; } + + public static PointerType GetFinalPointer(this Type t) + { + var type = t as PointerType; + + if (type == null) + return null; + + var pointee = type.Desugar().GetPointee(); + + if (pointee.IsPointer()) + return pointee.GetFinalPointer(); + + return type; + } } } \ No newline at end of file diff --git a/src/Generator/Generators/CSharp/CSharpMarshal.cs b/src/Generator/Generators/CSharp/CSharpMarshal.cs index ece8f5bc..7e99887f 100644 --- a/src/Generator/Generators/CSharp/CSharpMarshal.cs +++ b/src/Generator/Generators/CSharp/CSharpMarshal.cs @@ -142,20 +142,24 @@ namespace CppSharp.Generators.CSharp if (!VisitType(pointer, quals)) return false; + var param = Context.Parameter; + var isRefParam = param != null && (param.IsInOut || param.IsOut); + var pointee = pointer.Pointee.Desugar(); + bool marshalPointeeAsString = CSharpTypePrinter.IsConstCharString(pointee) && isRefParam; - if (CSharpTypePrinter.IsConstCharString(pointer)) + if (CSharpTypePrinter.IsConstCharString(pointer) || marshalPointeeAsString) { Context.Return.Write(MarshalStringToManaged(Context.ReturnVarName, - pointer.Pointee.Desugar() as BuiltinType)); + pointer.GetFinalPointee() as BuiltinType)); return true; } + var finalPointee = pointer.GetFinalPointee(); PrimitiveType primitive; - if (pointee.IsPrimitiveType(out primitive) || pointee.IsEnumType()) + if (finalPointee.IsPrimitiveType(out primitive) || finalPointee.IsEnumType()) { - var param = Context.Parameter; - if (param != null && (param.IsOut || param.IsInOut)) + if (isRefParam) { Context.Return.Write("_{0}", param.Name); return true; @@ -386,18 +390,20 @@ namespace CppSharp.Generators.CSharp if (!VisitType(pointer, quals)) return false; + var param = Context.Parameter; + var isRefParam = param != null && (param.IsInOut || param.IsOut); + var pointee = pointer.Pointee.Desugar(); + bool marshalPointeeAsString = CSharpTypePrinter.IsConstCharString(pointee) && isRefParam; - if ((pointee.IsPrimitiveType(PrimitiveType.Char) || - pointee.IsPrimitiveType(PrimitiveType.WideChar)) && - pointer.QualifiedPointee.Qualifiers.IsConst) + if (CSharpTypePrinter.IsConstCharString(pointer) || marshalPointeeAsString) { - if (Context.Parameter.IsOut) + if (param.IsOut) { Context.Return.Write("IntPtr.Zero"); CSharpContext.ArgumentPrefix.Write("&"); } - else if (Context.Parameter.IsInOut) + else if (param.IsInOut) { Context.Return.Write(MarshalStringToUnmanaged(Context.Parameter.Name)); CSharpContext.ArgumentPrefix.Write("&"); @@ -437,18 +443,17 @@ namespace CppSharp.Generators.CSharp return true; } + var finalPointee = pointer.GetFinalPointee(); PrimitiveType primitive; - if (pointee.IsPrimitiveType(out primitive) || pointee.IsEnumType()) + if (finalPointee.IsPrimitiveType(out primitive) || finalPointee.IsEnumType()) { - var param = Context.Parameter; - // 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 // and then assign this value to the parameter. - if (param.IsOut || param.IsInOut) + if (isRefParam) { - var typeName = Type.TypePrinterDelegate(pointee); + var typeName = Type.TypePrinterDelegate(finalPointee); if (param.IsInOut) Context.SupportBefore.WriteLine("{0} _{1} = {1};", typeName, param.Name); diff --git a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs index 7897768d..c5cf2c27 100644 --- a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs +++ b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs @@ -192,9 +192,9 @@ namespace CppSharp.Generators.CSharp pointer.QualifiedPointee.Qualifiers.IsConst; } - public static bool IsConstCharString(QualifiedType qualType) + public static bool IsConstCharString(Type type) { - var desugared = qualType.Type.Desugar(); + var desugared = type.Desugar(); if (!(desugared is PointerType)) return false; @@ -203,6 +203,13 @@ namespace CppSharp.Generators.CSharp return IsConstCharString(pointer); } + public static bool IsConstCharString(QualifiedType qualType) + { + return IsConstCharString(qualType.Type); + } + + bool AllowStrings = true; + public CSharpTypePrinterResult VisitPointerType(PointerType pointer, TypeQualifiers quals) { @@ -216,7 +223,7 @@ namespace CppSharp.Generators.CSharp var isManagedContext = ContextKind == CSharpTypePrinterContextKind.Managed; - if (IsConstCharString(pointer)) + if (AllowStrings && IsConstCharString(pointer)) return isManagedContext ? "string" : "global::System.IntPtr"; var desugared = pointee.Desugar(); @@ -240,7 +247,12 @@ namespace CppSharp.Generators.CSharp pointee.IsPrimitiveType(PrimitiveType.Void)) return "global::System.IntPtr"; + // Do not allow strings inside primitive arrays case, else we'll get invalid types + // like string* for const char **. + AllowStrings = isRefParam; var result = pointee.Visit(this, quals); + AllowStrings = true; + return !isRefParam && result.Type == "global::System.IntPtr" ? "void**" : result + "*"; } diff --git a/tests/CSharpTemp/CSharpTemp.h b/tests/CSharpTemp/CSharpTemp.h index 4f884763..0d5a2409 100644 --- a/tests/CSharpTemp/CSharpTemp.h +++ b/tests/CSharpTemp/CSharpTemp.h @@ -403,3 +403,21 @@ template <> struct QIntegerForSize<4> { typedef uint32_t Unsigned; typedef int32 template <> struct QIntegerForSize<8> { typedef uint64_t Unsigned; typedef int64_t Signed; }; typedef QIntegerForSize::Signed qregisterint; typedef QIntegerForSize::Unsigned qregisteruint; + +struct DLL_API TestPointers +{ + void TestDoubleCharPointers(const char** names); + void TestTripleCharPointers(const char*** names); + + const char** Names; +}; + +void TestPointers::TestDoubleCharPointers(const char** names) +{ + +} + +void TestPointers::TestTripleCharPointers(const char*** names) +{ + +}