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