diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 32989bd81..ae1b89dbd 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -362,6 +362,11 @@ namespace Flax.Build.Bindings contents.Append("out "); else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller)) contents.Append("ref "); + + // Out parameters that need additional converting will be converted at the native side (eg. object reference) + if (parameterInfo.IsOut && !string.IsNullOrEmpty(GenerateCSharpManagedToNativeConverter(buildData, parameterInfo.Type, caller))) + nativeType = parameterInfo.Type.Type; + contents.Append(nativeType); contents.Append(' '); contents.Append(parameterInfo.Name); @@ -399,16 +404,15 @@ namespace Flax.Build.Bindings { contents.Append("return "); } - contents.Append("Internal_").Append(functionInfo.UniqueName).Append('('); + // Pass parameters var separator = false; if (!functionInfo.IsStatic) { contents.Append("__unmanagedPtr"); separator = true; } - for (var i = 0; i < functionInfo.Parameters.Count; i++) { var parameterInfo = functionInfo.Parameters[i]; @@ -416,29 +420,29 @@ namespace Flax.Build.Bindings contents.Append(", "); separator = true; - var convertFunc = GenerateCSharpManagedToNativeConverter(buildData, parameterInfo.Type, caller); - if (string.IsNullOrWhiteSpace(convertFunc)) - { - if (parameterInfo.IsOut) - contents.Append("out "); - else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller)) - contents.Append("ref "); + if (parameterInfo.IsOut) + contents.Append("out "); + else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller)) + contents.Append("ref "); + var convertFunc = CppParamsWrappersCache[i]; + var paramName = isSetter ? "value" : parameterInfo.Name; + if (string.IsNullOrWhiteSpace(convertFunc) || parameterInfo.IsOut) + { // Pass value - contents.Append(isSetter ? "value" : parameterInfo.Name); + contents.Append(paramName); } else { - if (parameterInfo.IsOut) - throw new Exception($"Cannot use Out meta on parameter {parameterInfo} in function {functionInfo.Name} in {caller}."); if (parameterInfo.IsRef) - throw new Exception($"Cannot use Ref meta on parameter {parameterInfo} in function {functionInfo.Name} in {caller}."); + throw new Exception($"Cannot use Ref meta on parameter {parameterInfo} in function {functionInfo.Name} in {caller}. Use API_PARAM(Out) if you want to pass it as a result reference."); // Convert value - contents.Append(string.Format(convertFunc, isSetter ? "value" : parameterInfo.Name)); + contents.Append(string.Format(convertFunc, paramName)); } } + // Pass additional parameters var customParametersCount = functionInfo.Glue.CustomParameters?.Count ?? 0; for (var i = 0; i < customParametersCount; i++) { @@ -448,7 +452,7 @@ namespace Flax.Build.Bindings separator = true; var convertFunc = GenerateCSharpManagedToNativeConverter(buildData, parameterInfo.Type, caller); - if (string.IsNullOrWhiteSpace(convertFunc)) + if (string.IsNullOrWhiteSpace(convertFunc) || parameterInfo.IsOut) { if (parameterInfo.IsOut) contents.Append("out "); @@ -466,6 +470,8 @@ namespace Flax.Build.Bindings } contents.Append(");"); + + // Return result if (functionInfo.Glue.UseReferenceForResult) { contents.Append(" return resultAsRef;"); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 60194ab36..71d02eb6b 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -358,10 +358,8 @@ namespace Flax.Build.Bindings // Use dynamic array as wrapper container for fixed-size native arrays if (typeInfo.IsArray) { - typeInfo.IsArray = false; - var arrayType = new TypeInfo { Type = "Array", GenericArgs = new List { typeInfo, }, }; + var arrayType = new TypeInfo { Type = "Array", GenericArgs = new List { new TypeInfo(typeInfo) { IsArray = false } } }; var result = GenerateCppWrapperNativeToManaged(buildData, arrayType, caller, out type, functionInfo); - typeInfo.IsArray = true; return result.Replace("{0}", $"Span<{typeInfo.Type}>({{0}}, {typeInfo.ArraySize})"); } @@ -533,10 +531,8 @@ namespace Flax.Build.Bindings // Use dynamic array as wrapper container for fixed-size native arrays if (typeInfo.IsArray) { - typeInfo.IsArray = false; - var arrayType = new TypeInfo { Type = "Array", GenericArgs = new List { typeInfo, }, }; + var arrayType = new TypeInfo { Type = "Array", GenericArgs = new List { new TypeInfo(typeInfo) { IsArray = false } } }; var result = GenerateCppWrapperManagedToNative(buildData, arrayType, caller, out type, functionInfo, out needLocalVariable); - typeInfo.IsArray = true; return result + ".Get()"; } @@ -776,6 +772,12 @@ namespace Flax.Build.Bindings CppParamsThatNeedConversion[i] = false; CppParamsWrappersCache[i] = GenerateCppWrapperManagedToNative(buildData, parameterInfo.Type, caller, out var managedType, functionInfo, out CppParamsThatNeedLocalVariable[i]); + + // Out parameters that need additional converting will be converted at the native side (eg. object reference) + var isOutWithManagedConverter = parameterInfo.IsOut && !string.IsNullOrEmpty(GenerateCSharpManagedToNativeConverter(buildData, parameterInfo.Type, caller)); + if (isOutWithManagedConverter) + managedType = "MonoObject*"; + contents.Append(managedType); if (parameterInfo.IsRef || parameterInfo.IsOut || UsePassByReference(buildData, parameterInfo.Type, caller)) contents.Append('*'); @@ -894,9 +896,11 @@ namespace Flax.Build.Bindings var apiType = FindApiTypeInfo(buildData, parameterInfo.Type, caller); if (apiType != null) { - // TODO: maybe for Out-only params we could skip copying from managed data? see RayCastHit usage converted here from RayCastHitManaged - contents.AppendFormat(" auto {0}Temp = {1};", parameterInfo.Name, param).AppendLine(); - if (parameterInfo.Type.IsPtr) + if (parameterInfo.IsOut) + contents.AppendFormat(" {1} {0}Temp;", parameterInfo.Name, new TypeInfo(parameterInfo.Type) { IsRef = false }.GetFullNameNative(buildData, caller)).AppendLine(); + else + contents.AppendFormat(" auto {0}Temp = {1};", parameterInfo.Name, param).AppendLine(); + if (parameterInfo.Type.IsPtr && !parameterInfo.Type.IsRef) callParams += "&"; callParams += parameterInfo.Name; callParams += "Temp"; diff --git a/Source/Tools/Flax.Build/Bindings/TypeInfo.cs b/Source/Tools/Flax.Build/Bindings/TypeInfo.cs index 350015507..609e6af58 100644 --- a/Source/Tools/Flax.Build/Bindings/TypeInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/TypeInfo.cs @@ -11,7 +11,7 @@ namespace Flax.Build.Bindings /// /// The native type information for bindings generator. /// - public class TypeInfo : IEquatable, IBindingsCache + public class TypeInfo : IEquatable, IBindingsCache, ICloneable { public string Type; public bool IsConst; @@ -33,6 +33,23 @@ namespace Flax.Build.Bindings /// public bool IsConstRef => IsRef && IsConst; + public TypeInfo() + { + } + + public TypeInfo(TypeInfo other) + { + Type = other.Type; + IsConst = other.IsConst; + IsRef = other.IsRef; + IsPtr = other.IsPtr; + IsArray = other.IsArray; + IsBitField = other.IsBitField; + ArraySize = other.ArraySize; + BitSize = other.BitSize; + GenericArgs = other.GenericArgs != null ? new List(other.GenericArgs) : null; + } + /// /// Gets a value indicating whether this type is POD (plain old data). /// @@ -183,5 +200,11 @@ namespace Flax.Build.Bindings return hashCode; } } + + /// + public object Clone() + { + return new TypeInfo(this); + } } }