Browse Source

Added marshalling of fixed char arrays.

Signed-off-by: Dimitar Dobrev <dpldobrev@protonmail.com>
pull/681/head
Dimitar Dobrev 9 years ago
parent
commit
c1be105d46
  1. 3
      src/Generator/Generators/CLI/CLISourcesTemplate.cs
  2. 65
      src/Generator/Generators/CSharp/CSharpMarshal.cs
  3. 24
      src/Generator/Generators/CSharp/CSharpTextTemplate.cs
  4. 14
      tests/Common/Common.Tests.cs
  5. 1
      tests/Common/Common.h

3
src/Generator/Generators/CLI/CLISourcesTemplate.cs

@ -1101,8 +1101,9 @@ namespace CppSharp.Generators.CLI
p => p.Type.IsPrimitiveType(PrimitiveType.Char))) p => p.Type.IsPrimitiveType(PrimitiveType.Char)))
{ {
WriteLine("if ({0} < System::Char::MinValue || {0} > System::SByte::MaxValue)", param.Name); WriteLine("if ({0} < System::Char::MinValue || {0} > System::SByte::MaxValue)", param.Name);
// C++/CLI can actually handle char -> sbyte in all case, this is for compatibility with the C# generator
WriteLineIndent( WriteLineIndent(
"throw gcnew System::ArgumentException(\"{0} must be in the range {1} - {2}.\");", "throw gcnew System::OverflowException(\"{0} must be in the range {1} - {2}.\");",
param.Name, (int) char.MinValue, sbyte.MaxValue); param.Name, (int) char.MinValue, sbyte.MaxValue);
} }
} }

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

@ -106,12 +106,27 @@ namespace CppSharp.Generators.CSharp
value, Context.ReturnVarName); value, Context.ReturnVarName);
else else
{ {
var arrayType = array.Type.Desugar();
Class @class; Class @class;
if (array.Type.Desugar().TryGetClass(out @class) && @class.IsRefType) if (arrayType.TryGetClass(out @class) && @class.IsRefType)
supportBefore.WriteLineIndent("{0}[i] = {1}.{2}(*(({1}.Internal*)&({3}[i * sizeof({1}.Internal)])));", supportBefore.WriteLineIndent(
"{0}[i] = {1}.{2}(*(({1}.Internal*)&({3}[i * sizeof({1}.Internal)])));",
value, array.Type, Helpers.CreateInstanceIdentifier, Context.ReturnVarName); value, array.Type, Helpers.CreateInstanceIdentifier, Context.ReturnVarName);
else else
supportBefore.WriteLineIndent("{0}[i] = {1}[i];", value, Context.ReturnVarName); {
if (arrayType.IsPrimitiveType(PrimitiveType.Char) &&
Context.Driver.Options.MarshalCharAsManagedChar)
{
supportBefore.WriteLineIndent(
"{0}[i] = global::System.Convert.ToChar({1}[i]);",
value, Context.ReturnVarName);
}
else
{
supportBefore.WriteLineIndent("{0}[i] = {1}[i];",
value, Context.ReturnVarName);
}
}
} }
supportBefore.WriteCloseBraceIndent(); supportBefore.WriteCloseBraceIndent();
Context.Return.Write(value); Context.Return.Write(value);
@ -203,7 +218,11 @@ namespace CppSharp.Generators.CSharp
case PrimitiveType.Char: case PrimitiveType.Char:
// returned structs must be blittable and char isn't // returned structs must be blittable and char isn't
if (Context.Driver.Options.MarshalCharAsManagedChar) if (Context.Driver.Options.MarshalCharAsManagedChar)
Context.Return.Write("(char) "); {
Context.Return.Write("global::System.Convert.ToChar({0})",
Context.ReturnVarName);
return true;
}
goto default; goto default;
case PrimitiveType.Char16: case PrimitiveType.Char16:
return false; return false;
@ -393,10 +412,12 @@ namespace CppSharp.Generators.CSharp
case ArrayType.ArraySize.Constant: case ArrayType.ArraySize.Constant:
if (string.IsNullOrEmpty(Context.ReturnVarName)) if (string.IsNullOrEmpty(Context.ReturnVarName))
{ {
Context.SupportBefore.WriteLine("if ({0} == null || {0}.Length != {1})", Context.Parameter.Name, array.Size); Context.SupportBefore.WriteLine("if ({0} == null || {0}.Length != {1})",
Context.Parameter.Name, array.Size);
ThrowArgumentOutOfRangeException(); ThrowArgumentOutOfRangeException();
const string ptr = "__ptr"; const string ptr = "__ptr";
Context.SupportBefore.WriteLine("fixed ({0}* {1} = {2})", array.Type, ptr, Context.Parameter.Name); Context.SupportBefore.WriteLine("fixed ({0}* {1} = {2})",
array.Type, ptr, Context.Parameter.Name);
Context.SupportBefore.WriteStartBraceIndent(); Context.SupportBefore.WriteStartBraceIndent();
Context.Return.Write("new global::System.IntPtr({0})", ptr); Context.Return.Write("new global::System.IntPtr({0})", ptr);
Context.HasFixedBlock = true; Context.HasFixedBlock = true;
@ -407,6 +428,7 @@ namespace CppSharp.Generators.CSharp
supportBefore.WriteLine("if ({0} != null)", Context.ArgName); supportBefore.WriteLine("if ({0} != null)", Context.ArgName);
supportBefore.WriteStartBraceIndent(); supportBefore.WriteStartBraceIndent();
Class @class; Class @class;
var arrayType = array.Type.Desugar();
if (array.Type.Desugar().TryGetClass(out @class) && @class.IsRefType) if (array.Type.Desugar().TryGetClass(out @class) && @class.IsRefType)
{ {
supportBefore.WriteLine("if (value.Length != {0})", array.Size); supportBefore.WriteLine("if (value.Length != {0})", array.Size);
@ -414,15 +436,30 @@ namespace CppSharp.Generators.CSharp
} }
supportBefore.WriteLine("for (int i = 0; i < {0}; i++)", array.Size); supportBefore.WriteLine("for (int i = 0; i < {0}; i++)", array.Size);
if (@class != null && @class.IsRefType) if (@class != null && @class.IsRefType)
{
supportBefore.WriteLineIndent( supportBefore.WriteLineIndent(
"{0}[i * sizeof({2}.Internal)] = *((byte*)({2}.Internal*){1}[i].__Instance);", "{0}[i * sizeof({2}.Internal)] = *((byte*)({2}.Internal*){1}[i].__Instance);",
Context.ReturnVarName, Context.ArgName, array.Type); Context.ReturnVarName, Context.ArgName, array.Type);
}
else else
supportBefore.WriteLineIndent("{0}[i] = {1}[i]{2};", {
Context.ReturnVarName, Context.ArgName, if (arrayType.IsPrimitiveType(PrimitiveType.Char) &&
array.Type.IsPointerToPrimitiveType(PrimitiveType.Void) Context.Driver.Options.MarshalCharAsManagedChar)
? ".ToPointer()" {
: string.Empty); supportBefore.WriteLineIndent(
"{0}[i] = global::System.Convert.ToSByte({1}[i]);",
Context.ReturnVarName, Context.ArgName);
}
else
{
supportBefore.WriteLineIndent("{0}[i] = {1}[i]{2};",
Context.ReturnVarName,
Context.ArgName,
array.Type.IsPointerToPrimitiveType(PrimitiveType.Void)
? ".ToPointer()"
: string.Empty);
}
}
supportBefore.WriteCloseBraceIndent(); supportBefore.WriteCloseBraceIndent();
} }
break; break;
@ -578,7 +615,11 @@ namespace CppSharp.Generators.CSharp
case PrimitiveType.Char: case PrimitiveType.Char:
// returned structs must be blittable and char isn't // returned structs must be blittable and char isn't
if (Context.Driver.Options.MarshalCharAsManagedChar) if (Context.Driver.Options.MarshalCharAsManagedChar)
Context.Return.Write("(sbyte) "); {
Context.Return.Write("global::System.Convert.ToSByte({0})",
Context.Parameter.Name);
return true;
}
goto default; goto default;
case PrimitiveType.Char16: case PrimitiveType.Char16:
return false; return false;

24
src/Generator/Generators/CSharp/CSharpTextTemplate.cs

@ -993,8 +993,7 @@ namespace CppSharp.Generators.CSharp
private string HandleValueArray(ArrayType arrayType, Field field) private string HandleValueArray(ArrayType arrayType, Field field)
{ {
var originalType = new PointerType(new QualifiedType(arrayType.Type)); var originalType = new PointerType(new QualifiedType(arrayType.Type));
var arrPtrIden = Generator.GeneratedIdentifier("arrPtr"); var arrPtr = Generator.GeneratedIdentifier("arrPtr");
var arrPtr = arrPtrIden;
var finalElementType = (arrayType.Type.GetFinalPointee() ?? arrayType.Type); var finalElementType = (arrayType.Type.GetFinalPointee() ?? arrayType.Type);
var isChar = finalElementType.IsPrimitiveType(PrimitiveType.Char); var isChar = finalElementType.IsPrimitiveType(PrimitiveType.Char);
@ -1004,7 +1003,6 @@ namespace CppSharp.Generators.CSharp
var typePrinter = new CSharpTypePrinter(Driver); var typePrinter = new CSharpTypePrinter(Driver);
typePrinter.PushContext(CSharpTypePrinterContextKind.Native); typePrinter.PushContext(CSharpTypePrinterContextKind.Native);
type = originalType.Visit(typePrinter).Type; type = originalType.Visit(typePrinter).Type;
arrPtrIden = Generator.GeneratedIdentifier(arrPtrIden);
} }
else else
{ {
@ -1014,10 +1012,8 @@ namespace CppSharp.Generators.CSharp
var name = ((Class) field.Namespace).Layout.Fields.First( var name = ((Class) field.Namespace).Layout.Fields.First(
f => f.FieldPtr == field.OriginalPtr).Name; f => f.FieldPtr == field.OriginalPtr).Name;
WriteLine(string.Format("fixed ({0} {1} = {2}.{3})", WriteLine(string.Format("fixed ({0} {1} = {2}.{3})",
type, arrPtrIden, Helpers.InstanceField, Helpers.SafeIdentifier(name))); type, arrPtr, Helpers.InstanceField, Helpers.SafeIdentifier(name)));
WriteStartBraceIndent(); WriteStartBraceIndent();
if (Driver.Options.MarshalCharAsManagedChar && isChar)
WriteLine("var {0} = ({1}) {2};", arrPtr, originalType, arrPtrIden);
return arrPtr; return arrPtr;
} }
@ -2414,21 +2410,6 @@ namespace CppSharp.Generators.CSharp
} }
} }
private void CheckArgumentRange(Function method)
{
if (Driver.Options.MarshalCharAsManagedChar)
{
foreach (var param in method.Parameters.Where(
p => p.Type.IsPrimitiveType(PrimitiveType.Char)))
{
WriteLine("if ({0} < char.MinValue || {0} > sbyte.MaxValue)", param.Name);
WriteLineIndent(
"throw new global::System.ArgumentException(\"{0} must be in the range {1} - {2}.\");",
param.Name, (int) char.MinValue, sbyte.MaxValue);
}
}
}
private static AccessSpecifier GetValidMethodAccess(Method method) private static AccessSpecifier GetValidMethodAccess(Method method)
{ {
if (!method.IsOverride) if (!method.IsOverride)
@ -2601,7 +2582,6 @@ namespace CppSharp.Generators.CSharp
!templateSpecialization.IsSupportedStdType() ? !templateSpecialization.IsSupportedStdType() ?
(templateSpecialization.Namespace.OriginalName + '.') : string.Empty; (templateSpecialization.Namespace.OriginalName + '.') : string.Empty;
CheckArgumentRange(function);
var functionName = string.Format("{0}Internal.{1}", @namespace, var functionName = string.Format("{0}Internal.{1}", @namespace,
GetFunctionNativeIdentifier(function.OriginalFunction ?? function)); GetFunctionNativeIdentifier(function.OriginalFunction ?? function));
GenerateFunctionCall(functionName, parameters, function, returnType); GenerateFunctionCall(functionName, parameters, function, returnType);

14
tests/Common/Common.Tests.cs

@ -337,7 +337,7 @@ public class CommonTests : GeneratorTestFixture
Foo2 foo2 = new Foo2(); Foo2 foo2 = new Foo2();
for (char c = char.MinValue; c <= sbyte.MaxValue; c++) for (char c = char.MinValue; c <= sbyte.MaxValue; c++)
Assert.That(foo2.testCharMarshalling(c), Is.EqualTo(c)); Assert.That(foo2.testCharMarshalling(c), Is.EqualTo(c));
Assert.Catch<ArgumentException>(() => foo2.testCharMarshalling('ж')); Assert.Catch<OverflowException>(() => foo2.testCharMarshalling('ж'));
} }
[Test] [Test]
@ -623,6 +623,18 @@ public class CommonTests : GeneratorTestFixture
Assert.IsTrue(NonTrivialDtor.DtorCalled); Assert.IsTrue(NonTrivialDtor.DtorCalled);
} }
[Test]
public void TestFixedCharArray()
{
using (var foo = new Foo())
{
foo.fixedCharArray = new char[] { 'a', 'b', 'c' };
Assert.That(foo.fixedCharArray[0], Is.EqualTo('a'));
Assert.That(foo.fixedCharArray[1], Is.EqualTo('b'));
Assert.That(foo.fixedCharArray[2], Is.EqualTo('c'));
}
}
private class CustomDerivedFromVirtual : AbstractWithVirtualDtor private class CustomDerivedFromVirtual : AbstractWithVirtualDtor
{ {
public override void @abstract() public override void @abstract()

1
tests/Common/Common.h

@ -45,6 +45,7 @@ public:
float B; float B;
IgnoredType ignoredType; IgnoredType ignoredType;
int fixedArray[3]; int fixedArray[3];
char fixedCharArray[3];
void* ptr; void* ptr;
static const int unsafe; static const int unsafe;
static const char charArray[]; static const char charArray[];

Loading…
Cancel
Save