diff --git a/src/Generator/Generators/CLI/CLISourcesTemplate.cs b/src/Generator/Generators/CLI/CLISourcesTemplate.cs index 122fdd80..e257f4e6 100644 --- a/src/Generator/Generators/CLI/CLISourcesTemplate.cs +++ b/src/Generator/Generators/CLI/CLISourcesTemplate.cs @@ -816,6 +816,8 @@ namespace CppSharp.Generators.CLI public void GenerateFunctionCall(Function function, Class @class = null) { + CheckArgumentRange(function); + var retType = function.ReturnType; var needsReturn = !retType.Type.IsPrimitiveType(PrimitiveType.Void); @@ -944,6 +946,21 @@ namespace CppSharp.Generators.CLI } } + private void CheckArgumentRange(Function method) + { + if (Driver.Options.MarshalCharAsManagedChar) + { + foreach (var param in method.Parameters.Where( + p => p.Type.Desugar().IsPrimitiveType(PrimitiveType.Int8))) + { + WriteLine("if ({0} < System::Char::MinValue || {0} > System::SByte::MaxValue)", param.Name); + WriteLineIndent( + "throw gcnew System::ArgumentException(\"{0} must be in the range {1} - {2}.\");", + param.Name, (int) char.MinValue, sbyte.MaxValue); + } + } + } + private static bool IsNativeMethod(Function function) { var method = function as Method; diff --git a/src/Generator/Generators/CLI/CLITypePrinter.cs b/src/Generator/Generators/CLI/CLITypePrinter.cs index 1dcefe2e..00a9bb83 100644 --- a/src/Generator/Generators/CLI/CLITypePrinter.cs +++ b/src/Generator/Generators/CLI/CLITypePrinter.cs @@ -172,8 +172,8 @@ namespace CppSharp.Generators.CLI case PrimitiveType.Bool: return "bool"; case PrimitiveType.Void: return "void"; case PrimitiveType.Char16: - case PrimitiveType.WideChar: return "char"; - case PrimitiveType.Int8: return "char"; + case PrimitiveType.WideChar: return "System::Char"; + case PrimitiveType.Int8: return Options.MarshalCharAsManagedChar ? "System::Char" : "char"; case PrimitiveType.UInt8: return "unsigned char"; case PrimitiveType.Int16: return "short"; case PrimitiveType.UInt16: return "unsigned short"; diff --git a/src/Generator/Generators/CSharp/CSharpGenerator.cs b/src/Generator/Generators/CSharp/CSharpGenerator.cs index 949568c0..ca335f14 100644 --- a/src/Generator/Generators/CSharp/CSharpGenerator.cs +++ b/src/Generator/Generators/CSharp/CSharpGenerator.cs @@ -11,7 +11,7 @@ namespace CppSharp.Generators.CSharp public CSharpGenerator(Driver driver) : base(driver) { - typePrinter = new CSharpTypePrinter(driver.TypeDatabase, driver.ASTContext); + typePrinter = new CSharpTypePrinter(driver.TypeDatabase, driver.Options, driver.ASTContext); expressionPrinter = new CSharpExpressionPrinter(); CppSharp.AST.Type.TypePrinterDelegate += type => type.Visit(typePrinter).Type; } diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index 68524b07..0f428367 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -2097,8 +2097,23 @@ namespace CppSharp.Generators.CSharp WriteCloseBraceIndent(); PopBlock(NewLineKind.BeforeNextBlock); - } - + } + + private void CheckArgumentRange(Function method) + { + if (Driver.Options.MarshalCharAsManagedChar) + { + foreach (var param in method.Parameters.Where( + p => p.Type.Desugar().IsPrimitiveType(PrimitiveType.Int8))) + { + 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) { switch (method.Access) @@ -2179,6 +2194,7 @@ namespace CppSharp.Generators.CSharp if (parameters == null) parameters = function.Parameters; + CheckArgumentRange(function); var functionName = string.Format("Internal.{0}", GetFunctionNativeIdentifier(function)); GenerateFunctionCall(functionName, parameters, function); diff --git a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs index 3f4d586d..81937138 100644 --- a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs +++ b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs @@ -54,6 +54,7 @@ namespace CppSharp.Generators.CSharp { public ASTContext AstContext { get; set; } private readonly ITypeMapDatabase TypeMapDatabase; + private readonly DriverOptions driverOptions; private readonly Stack contexts; @@ -64,9 +65,10 @@ namespace CppSharp.Generators.CSharp public CSharpTypePrinterContext Context; - public CSharpTypePrinter(ITypeMapDatabase database, ASTContext context) + public CSharpTypePrinter(ITypeMapDatabase database, DriverOptions driverOptions, ASTContext context) { TypeMapDatabase = database; + this.driverOptions = driverOptions; AstContext = context; contexts = new Stack(); @@ -367,7 +369,7 @@ namespace CppSharp.Generators.CSharp case PrimitiveType.Void: return "void"; case PrimitiveType.Char16: case PrimitiveType.WideChar: return "char"; - case PrimitiveType.Int8: return "sbyte"; + case PrimitiveType.Int8: return driverOptions.MarshalCharAsManagedChar ? "char" : "sbyte"; case PrimitiveType.UInt8: return "byte"; case PrimitiveType.Int16: return "short"; case PrimitiveType.UInt16: return "ushort"; diff --git a/src/Generator/Options.cs b/src/Generator/Options.cs index 80620517..0c6e8ddc 100644 --- a/src/Generator/Options.cs +++ b/src/Generator/Options.cs @@ -139,6 +139,7 @@ namespace CppSharp public List CodeFiles { get; private set; } public readonly List DependentNameSpaces = new List(); + public bool MarshalCharAsManagedChar { get; set; } } public class InvalidOptionException : Exception diff --git a/tests/Basic/Basic.Tests.cs b/tests/Basic/Basic.Tests.cs index f55d0d90..ba79944c 100644 --- a/tests/Basic/Basic.Tests.cs +++ b/tests/Basic/Basic.Tests.cs @@ -1,6 +1,8 @@ -using CppSharp.Utils; +using System; +using CppSharp.Utils; using NUnit.Framework; using Basic; +using Enum = Basic.Enum; public class BasicTests : GeneratorTestFixture { @@ -207,5 +209,14 @@ public class BasicTests : GeneratorTestFixture Assert.That(testCopyConstructorRef.A, Is.EqualTo(copyBar.A)); Assert.That(testCopyConstructorRef.B, Is.EqualTo(copyBar.B)); } + + [Test] + public void TestCharMarshalling() + { + Foo2 foo2 = new Foo2(); + for (char c = char.MinValue; c <= sbyte.MaxValue; c++) + Assert.That(foo2.testCharMarshalling(c), Is.EqualTo(c)); + Assert.Catch(() => foo2.testCharMarshalling('ж')); + } } \ No newline at end of file diff --git a/tests/Basic/Basic.cpp b/tests/Basic/Basic.cpp index 2e266f8f..54237caf 100644 --- a/tests/Basic/Basic.cpp +++ b/tests/Basic/Basic.cpp @@ -23,6 +23,11 @@ Foo2 Foo2::operator<<(signed long l) return foo; } +char Foo2::testCharMarshalling(char c) +{ + return c; +} + Bar::Bar() { } diff --git a/tests/Basic/Basic.cs b/tests/Basic/Basic.cs index 7ff0c33d..88ff092d 100644 --- a/tests/Basic/Basic.cs +++ b/tests/Basic/Basic.cs @@ -17,6 +17,7 @@ namespace CppSharp.Tests if (driver.Options.IsCSharpGenerator) driver.Options.GenerateAbstractImpls = true; driver.Options.GenerateVirtualTables = true; + driver.Options.MarshalCharAsManagedChar = true; } public override void Preprocess(Driver driver, ASTContext ctx) diff --git a/tests/Basic/Basic.h b/tests/Basic/Basic.h index cdcad981..9f7ad357 100644 --- a/tests/Basic/Basic.h +++ b/tests/Basic/Basic.h @@ -48,6 +48,7 @@ public: Foo2 operator<<(signed int i); Foo2 operator<<(signed long l); Bar valueTypeField; + char testCharMarshalling(char c); }; DLL_API Bar::Item operator |(Bar::Item left, Bar::Item right);