From 357efec91bb2eeccc013c22ed70ba21a2279ed39 Mon Sep 17 00:00:00 2001 From: Trung Nguyen <57174311+trungnt2910@users.noreply.github.com> Date: Sat, 15 Jul 2023 04:45:45 +1000 Subject: [PATCH] Array marshalling (#1748) * Generator: Customization for const char[] Allow the user to choose whether `const char[]` should be marshalled as `string` or a normal `char` array in C#. A new option `MarshalConstCharArrayAsString` is added, and is `true` by default. This helps in situations where the original C++ API distinguishes between C-strings and char arrays using the two different notations. * CSharpMarshal: Fix unknown length array marshal For unknown length arrays, also run a conversion loop if the primitive type encountered needs conversion (e.g. `char` to `sbyte`). * CSharpTypePrinter: Fix for boolean arrays --- src/Generator/Generators/CLI/CLIMarshal.cs | 3 ++- src/Generator/Generators/CLI/CLITypePrinter.cs | 3 ++- src/Generator/Generators/CSharp/CSharpMarshal.cs | 10 ++++++++-- src/Generator/Generators/CSharp/CSharpSources.cs | 2 +- src/Generator/Generators/CSharp/CSharpTypePrinter.cs | 10 +++++++++- src/Generator/Options.cs | 5 +++-- 6 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/Generator/Generators/CLI/CLIMarshal.cs b/src/Generator/Generators/CLI/CLIMarshal.cs index 1376592c..c6416517 100644 --- a/src/Generator/Generators/CLI/CLIMarshal.cs +++ b/src/Generator/Generators/CLI/CLIMarshal.cs @@ -61,7 +61,8 @@ namespace CppSharp.Generators.CLI break; case ArrayType.ArraySize.Incomplete: // const char* and const char[] are the same so we can use a string - if (array.Type.Desugar().IsPrimitiveType(PrimitiveType.Char) && + if (Context.Context.Options.MarshalConstCharArrayAsString && + array.Type.Desugar().IsPrimitiveType(PrimitiveType.Char) && array.QualifiedType.Qualifiers.IsConst) { var pointer = new PointerType { QualifiedPointee = array.QualifiedType }; diff --git a/src/Generator/Generators/CLI/CLITypePrinter.cs b/src/Generator/Generators/CLI/CLITypePrinter.cs index 50ff8d46..69077aff 100644 --- a/src/Generator/Generators/CLI/CLITypePrinter.cs +++ b/src/Generator/Generators/CLI/CLITypePrinter.cs @@ -35,7 +35,8 @@ namespace CppSharp.Generators.CLI TypeQualifiers quals) { // const char* and const char[] are the same so we can use a string - if (array.SizeType == ArrayType.ArraySize.Incomplete && + if (Context.Options.MarshalConstCharArrayAsString && + array.SizeType == ArrayType.ArraySize.Incomplete && array.Type.Desugar().IsPrimitiveType(PrimitiveType.Char) && array.QualifiedType.Qualifiers.IsConst) return VisitCILType(new CILType(typeof(string)), quals); diff --git a/src/Generator/Generators/CSharp/CSharpMarshal.cs b/src/Generator/Generators/CSharp/CSharpMarshal.cs index 85b6a254..dedd8a9b 100644 --- a/src/Generator/Generators/CSharp/CSharpMarshal.cs +++ b/src/Generator/Generators/CSharp/CSharpMarshal.cs @@ -116,7 +116,8 @@ namespace CppSharp.Generators.CSharp break; case ArrayType.ArraySize.Incomplete: // const char* and const char[] are the same so we can use a string - if (array.Type.Desugar().IsPrimitiveType(PrimitiveType.Char) && + if (Context.Context.Options.MarshalConstCharArrayAsString && + array.Type.Desugar().IsPrimitiveType(PrimitiveType.Char) && array.QualifiedType.Qualifiers.IsConst) { var pointer = new PointerType { QualifiedPointee = array.QualifiedType }; @@ -874,7 +875,8 @@ namespace CppSharp.Generators.CSharp var elementType = arrayType.Type.Desugar(); - if (elementType.IsPrimitiveType() || + if ((elementType.IsPrimitiveType() && + !(elementType.IsPrimitiveType(PrimitiveType.Char) && Context.Context.Options.MarshalCharAsManagedChar)) || elementType.IsPointerToPrimitiveType()) { if (Context.Context.Options.UseSpan && !elementType.IsConstCharString()) @@ -916,6 +918,10 @@ namespace CppSharp.Generators.CSharp Context.Before.WriteLine($@"{intermediateArray}[i] = { element} is null ? {intPtrZero} : {element}.{Helpers.InstanceIdentifier};"); } + else if (elementType.IsPrimitiveType(PrimitiveType.Char) && + Context.Context.Options.MarshalCharAsManagedChar) + Context.Before.WriteLine($@"{intermediateArray}[i] = global::System.Convert.ToSByte({ + element});"); else Context.Before.WriteLine($@"{intermediateArray}[i] = { element} is null ? new {intermediateArrayType}() : *({ diff --git a/src/Generator/Generators/CSharp/CSharpSources.cs b/src/Generator/Generators/CSharp/CSharpSources.cs index 11ff37ca..3a3db80e 100644 --- a/src/Generator/Generators/CSharp/CSharpSources.cs +++ b/src/Generator/Generators/CSharp/CSharpSources.cs @@ -972,7 +972,7 @@ internal static bool {Helpers.TryGetNativeToManagedMappingIdentifier}(IntPtr nat string ptr = Generator.GeneratedIdentifier("ptr"); if (arrayType != null) { - if (arrayType.Type.IsPrimitiveType(PrimitiveType.Char) && arrayType.SizeType != ArrayType.ArraySize.Constant) + if (Context.Options.MarshalConstCharArrayAsString && arrayType.Type.IsPrimitiveType(PrimitiveType.Char) && arrayType.SizeType != ArrayType.ArraySize.Constant) WriteLine($"var {ptr} = {location};"); else WriteLine($"var {ptr} = ({arrayType.Type.Visit(TypePrinter)}*){location};"); diff --git a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs index c31a0df8..72711795 100644 --- a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs +++ b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs @@ -111,7 +111,8 @@ namespace CppSharp.Generators.CSharp } // const char* and const char[] are the same so we can use a string - if (array.SizeType == ArrayType.ArraySize.Incomplete && + if (Context.Options.MarshalConstCharArrayAsString && + array.SizeType == ArrayType.ArraySize.Incomplete && arrayType.IsPrimitiveType(PrimitiveType.Char) && array.QualifiedType.Qualifiers.IsConst) return "string"; @@ -123,6 +124,13 @@ namespace CppSharp.Generators.CSharp return $"{prefix}string[]"; } + if (arrayType.IsPrimitiveType(PrimitiveType.Bool)) + { + var prefix = ContextKind == TypePrinterContextKind.Managed ? string.Empty : + "[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I1)] "; + return $"{prefix}bool[]"; + } + if (Context.Options.UseSpan && !(array.SizeType != ArrayType.ArraySize.Constant && MarshalKind == MarshalKind.ReturnVariableArray)) { diff --git a/src/Generator/Options.cs b/src/Generator/Options.cs index 0f0bc257..df924b86 100644 --- a/src/Generator/Options.cs +++ b/src/Generator/Options.cs @@ -115,7 +115,7 @@ namespace CppSharp /// /// Enable this option to enable generation of finalizers. Works in both CLI and - /// C# backends. + /// C# backends. /// /// /// Use to specify a filter so that @@ -125,7 +125,7 @@ namespace CppSharp /// /// A filter that can restrict the classes for which finalizers are generated when - /// is true. + /// is true. /// /// /// The default filter performs no filtering so that whenever DependentNameSpaces = new List(); public bool MarshalCharAsManagedChar { get; set; } + public bool MarshalConstCharArrayAsString { get; set; } = true; /// /// Use Span Struct instead of Managed Array