diff --git a/src/Generator/Generators/CLI/CLIMarshal.cs b/src/Generator/Generators/CLI/CLIMarshal.cs index 551b4095..99d920b7 100644 --- a/src/Generator/Generators/CLI/CLIMarshal.cs +++ b/src/Generator/Generators/CLI/CLIMarshal.cs @@ -45,6 +45,13 @@ namespace CppSharp.Generators.CLI { var pointee = pointer.Pointee; + var param = Context.Parameter; + if (param != null && (param.IsOut || param.IsInOut)) + { + Context.Return.Write(Context.ReturnVarName); + return true; + } + if (pointee.Desugar().IsPrimitiveType(PrimitiveType.Void)) { Context.Return.Write("IntPtr({0})", Context.ReturnVarName); @@ -234,7 +241,11 @@ namespace CppSharp.Generators.CLI public override bool VisitParameterDecl(Parameter parameter) { - return parameter.Type.Visit(this, parameter.QualifiedType.Qualifiers); + Context.Parameter = parameter; + var ret = parameter.Type.Visit(this, parameter.QualifiedType.Qualifiers); + Context.Parameter = null; + + return ret; } public override bool VisitTypedefDecl(TypedefDecl typedef) @@ -527,7 +538,8 @@ namespace CppSharp.Generators.CLI public void MarshalValueClass(Class @class) { - var marshalVar = "_marshal" + Context.ParameterIndex++; + var marshalVar = Context.MarshalVarPrefix + "_marshal" + + Context.ParameterIndex++; Context.SupportBefore.WriteLine("auto {0} = ::{1}();", marshalVar, @class.QualifiedOriginalName); @@ -572,7 +584,8 @@ namespace CppSharp.Generators.CLI var marshalCtx = new MarshalContext(Context.Driver) { ArgName = fieldRef, - ParameterIndex = Context.ParameterIndex++ + ParameterIndex = Context.ParameterIndex++, + MarshalVarPrefix = Context.MarshalVarPrefix }; var marshal = new CLIMarshalManagedToNativePrinter(marshalCtx); diff --git a/src/Generator/Generators/CLI/CLISourcesTemplate.cs b/src/Generator/Generators/CLI/CLISourcesTemplate.cs index 895523b9..d651edf2 100644 --- a/src/Generator/Generators/CLI/CLISourcesTemplate.cs +++ b/src/Generator/Generators/CLI/CLISourcesTemplate.cs @@ -632,12 +632,14 @@ namespace CppSharp.Generators.CLI { var names = new List(); + var paramIndex = 0; foreach (var param in method.Parameters) { var ctx = new MarshalContext(Driver) { Parameter = param, ArgName = param.Name, + ParameterIndex = paramIndex++ }; var marshal = new CLIMarshalManagedToNativePrinter(ctx); @@ -729,7 +731,11 @@ namespace CppSharp.Generators.CLI WriteLine("auto {0} = ::{1}();", valueMarshalName, @class.QualifiedOriginalName); var param = new Parameter() { Name = "(*this)" }; - var ctx = new MarshalContext(Driver) { Parameter = param }; + var ctx = new MarshalContext(Driver) + { + MarshalVarPrefix = valueMarshalName, + Parameter = param + }; var marshal = new CLIMarshalManagedToNativePrinter(ctx); marshal.MarshalValueClassFields(@class, valueMarshalName); @@ -860,6 +866,8 @@ namespace CppSharp.Generators.CLI public struct ParamMarshal { public string Name; + public string Prefix; + public Parameter Param; } @@ -881,18 +889,21 @@ namespace CppSharp.Generators.CLI private ParamMarshal GenerateFunctionParamMarshal(Parameter param, int paramIndex, Function function = null) { + var paramMarshal = new ParamMarshal { Name = param.Name, Param = param }; + if (param.Type is BuiltinType) - { - return new ParamMarshal {Name = param.Name, Param = param}; - } + return paramMarshal; var argName = "arg" + paramIndex.ToString(CultureInfo.InvariantCulture); - if (param.Usage == ParameterUsage.Out) + if (param.IsOut || param.IsInOut) { var paramType = param.Type; - if (paramType.IsReference()) + if (paramType is PointerType) + { paramType = (paramType as PointerType).Pointee; + paramMarshal.Prefix = "&"; + } var typePrinter = new CppTypePrinter(Driver.TypeDatabase); var type = paramType.Visit(typePrinter); @@ -923,12 +934,18 @@ namespace CppSharp.Generators.CLI argName = marshal.ArgumentPrefix + argName; } - return new ParamMarshal {Name = argName, Param = param}; + paramMarshal.Name = argName; + return paramMarshal; } public void GenerateFunctionParams(List @params) { - var names = @params.Select(param => param.Name).ToList(); + var names = @params.Select(param => + { + if (!string.IsNullOrWhiteSpace(param.Prefix)) + return param.Prefix + param.Name; + return param.Name; + }).ToList(); Write(string.Join(", ", names)); } diff --git a/src/Generator/Generators/CLI/CLITypePrinter.cs b/src/Generator/Generators/CLI/CLITypePrinter.cs index 47751341..978afc94 100644 --- a/src/Generator/Generators/CLI/CLITypePrinter.cs +++ b/src/Generator/Generators/CLI/CLITypePrinter.cs @@ -91,10 +91,11 @@ namespace CppSharp.Generators.CLI public string VisitParameter(Parameter param, bool hasName = true) { + Context.Parameter = param; var type = param.Type.Visit(this, param.QualifiedType.Qualifiers); - var name = param.Name; - var str = ""; + Context.Parameter = null; + var str = string.Empty; if(param.Usage == ParameterUsage.Out) str += "[System::Runtime::InteropServices::Out] "; @@ -103,9 +104,9 @@ namespace CppSharp.Generators.CLI if(param.Usage == ParameterUsage.Out || param.Usage == ParameterUsage.InOut) str += "%"; - - if (hasName && !string.IsNullOrEmpty(name)) - str += " " + name; + + if (hasName && !string.IsNullOrEmpty(param.Name)) + str += " " + param.Name; return str; } @@ -135,6 +136,10 @@ namespace CppSharp.Generators.CLI PrimitiveType primitive; if (pointee.Desugar().IsPrimitiveType(out primitive)) { + var param = Context.Parameter; + if (param != null && (param.IsOut || param.IsInOut)) + return VisitPrimitiveType(primitive); + return "System::IntPtr"; } diff --git a/src/Generator/Generators/CSharp/CSharpMarshal.cs b/src/Generator/Generators/CSharp/CSharpMarshal.cs index e882a36c..82fa525c 100644 --- a/src/Generator/Generators/CSharp/CSharpMarshal.cs +++ b/src/Generator/Generators/CSharp/CSharpMarshal.cs @@ -141,6 +141,13 @@ namespace CppSharp.Generators.CSharp PrimitiveType primitive; if (pointee.Desugar().IsPrimitiveType(out primitive)) { + var param = Context.Parameter; + if (param != null && (param.IsOut || param.IsInOut)) + { + Context.Return.Write("_{0}", param.Name); + return true; + } + Context.Return.Write(Context.ReturnVarName); return true; } @@ -410,7 +417,24 @@ namespace CppSharp.Generators.CSharp PrimitiveType primitive; if (type.IsPrimitiveType(out primitive)) { - Context.Return.Write(Context.Parameter.Name); + 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) + { + var typeName = Type.TypePrinterDelegate(type); + + Context.SupportBefore.WriteLine("{0} _{1};", typeName, + Helpers.SafeIdentifier(param.Name)); + Context.Return.Write("new global::System.IntPtr(&_{0})", param.Name); + + } + else + Context.Return.Write(Context.Parameter.Name); + return true; } diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index 096c4d95..7a2588e0 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -1813,13 +1813,13 @@ namespace CppSharp.Generators.CSharp foreach (var paramInfo in @params) { var param = paramInfo.Param; - if (param.Usage != ParameterUsage.Out && param.Usage != ParameterUsage.InOut) - continue; + if (!(param.IsOut || param.IsInOut)) continue; var nativeVarName = paramInfo.Name; var ctx = new CSharpMarshalContext(Driver) { + Parameter = param, ArgName = nativeVarName, ReturnVarName = nativeVarName, ReturnType = param.QualifiedType @@ -1890,7 +1890,7 @@ namespace CppSharp.Generators.CSharp var argName = "arg" + paramIndex.ToString(CultureInfo.InvariantCulture); var paramMarshal = new ParamMarshal { Name = argName, Param = param }; - if (param.Usage == ParameterUsage.Out) + if (param.IsOut || param.IsInOut) { var paramType = param.Type; @@ -1949,8 +1949,9 @@ namespace CppSharp.Generators.CSharp if (param.Kind == ParameterKind.IndirectReturnType) continue; + var typeName = param.CSharpType(TypePrinter); @params.Add(string.Format("{0}{1} {2}", GetParameterUsage(param.Usage), - param.Type, SafeIdentifier(param.Name))); + typeName, SafeIdentifier(param.Name))); } Write(string.Join(", ", @params)); diff --git a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs index 9c43d37b..811a828d 100644 --- a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs +++ b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs @@ -172,10 +172,16 @@ namespace CppSharp.Generators.CSharp if (IsConstCharString(pointer)) return isManagedContext ? "string" : "global::System.IntPtr"; - PrimitiveType primitive; - if (pointee.Desugar().IsPrimitiveType(out primitive)) - return "global::System.IntPtr"; - + PrimitiveType primitive; + if (pointee.Desugar().IsPrimitiveType(out primitive)) + { + if (isManagedContext && Context.Parameter != null && + (Context.Parameter.IsOut || Context.Parameter.IsInOut)) + return VisitPrimitiveType(primitive, quals); + + return "global::System.IntPtr"; + } + Class @class; if (pointee.IsTagDecl(out @class) && ContextKind == CSharpTypePrinterContextKind.Native) @@ -363,9 +369,13 @@ namespace CppSharp.Generators.CSharp var paramType = parameter.Type; if (parameter.Kind == ParameterKind.IndirectReturnType) - return "global::System.IntPtr"; - - return paramType.Visit(this); + return "global::System.IntPtr"; + + Context.Parameter = parameter; + var ret = paramType.Visit(this); + Context.Parameter = null; + + return ret; } public CSharpTypePrinterResult VisitTypedefDecl(TypedefDecl typedef) @@ -436,11 +446,15 @@ namespace CppSharp.Generators.CSharp public CSharpTypePrinterResult VisitParameters(IEnumerable @params, bool hasNames) { - var args = new List(); - - foreach (var param in @params) - args.Add(VisitParameter(param, hasNames).Type); - + var args = new List(); + + foreach (var param in @params) + { + Context.Parameter = param; + args.Add(VisitParameter(param, hasNames).Type); + } + + Context.Parameter = null; return string.Join(", ", args); } diff --git a/src/Generator/Generators/Marshal.cs b/src/Generator/Generators/Marshal.cs index 8129b2be..b743209d 100644 --- a/src/Generator/Generators/Marshal.cs +++ b/src/Generator/Generators/Marshal.cs @@ -9,6 +9,7 @@ namespace CppSharp.Generators Driver = driver; SupportBefore = new TextGenerator(); Return = new TextGenerator(); + MarshalVarPrefix = string.Empty; } public Driver Driver { get; private set; } @@ -28,6 +29,8 @@ namespace CppSharp.Generators public Parameter Parameter { get; set; } public int ParameterIndex { get; set; } public Function Function { get; set; } + + public string MarshalVarPrefix { get; set; } } public abstract class MarshalPrinter : AstVisitor diff --git a/src/Generator/Types/ITypePrinter.cs b/src/Generator/Types/ITypePrinter.cs index dd6c3fb2..873f01eb 100644 --- a/src/Generator/Types/ITypePrinter.cs +++ b/src/Generator/Types/ITypePrinter.cs @@ -47,6 +47,7 @@ namespace CppSharp.Types public TypePrinterContextKind Kind; public Declaration Declaration; + public Parameter Parameter; public Type Type; } diff --git a/tests/Basic/Basic.Tests.cs b/tests/Basic/Basic.Tests.cs index 60510542..284a7480 100644 --- a/tests/Basic/Basic.Tests.cs +++ b/tests/Basic/Basic.Tests.cs @@ -44,6 +44,16 @@ public class BasicTests Assert.That(hello.RetEnum(Enum.F), Is.EqualTo(-9)); } + [Test] + public void TestPrimitiveOutParameters() + { + var hello = new Hello(); + + float f; + Assert.That(hello.TestPrimitiveOut(out f), Is.True); + Assert.That(f, Is.EqualTo(10.0f)); + } + [Test] public void TestNullRef() { diff --git a/tests/Basic/Basic.cpp b/tests/Basic/Basic.cpp index 4d146747..9bcb3dfb 100644 --- a/tests/Basic/Basic.cpp +++ b/tests/Basic/Basic.cpp @@ -95,6 +95,12 @@ Hello* Hello::RetNull() return 0; } +bool Hello::TestPrimitiveOut(CS_OUT float* f) +{ + *f = 10; + return true; +} + int unsafeFunction(const Bar& ret, char* testForString, void (*foo)(int)) { return ret.A; diff --git a/tests/Basic/Basic.cs b/tests/Basic/Basic.cs index 543bacff..f01f4b2d 100644 --- a/tests/Basic/Basic.cs +++ b/tests/Basic/Basic.cs @@ -15,6 +15,7 @@ namespace CppSharp.Tests { lib.SetClassAsValueType("Bar"); lib.SetClassAsValueType("Bar2"); + lib.SetMethodParameterUsage("Hello", "TestPrimitiveOut", 1, ParameterUsage.Out); } static class Program diff --git a/tests/Basic/Basic.h b/tests/Basic/Basic.h index b63b74cd..ead072dd 100644 --- a/tests/Basic/Basic.h +++ b/tests/Basic/Basic.h @@ -4,6 +4,8 @@ #define DLL_API #endif +#define CS_OUT + class DLL_API Foo { public: @@ -84,6 +86,9 @@ public: int RetEnum(Enum); Hello* RetNull(); + + bool TestPrimitiveOut(CS_OUT float* f); + }; class DLL_API AbstractFoo