diff --git a/src/Generator/Generators/CLI/CLIMarshal.cs b/src/Generator/Generators/CLI/CLIMarshal.cs index d9b6dd7d..eee6af54 100644 --- a/src/Generator/Generators/CLI/CLIMarshal.cs +++ b/src/Generator/Generators/CLI/CLIMarshal.cs @@ -93,11 +93,31 @@ namespace CppSharp.Generators.CLI { var returnVarName = Context.ReturnVarName; if (quals.IsConst != Context.ReturnType.Qualifiers.IsConst) - returnVarName = string.Format("const_cast<{0}>({1})", - Context.ReturnType, Context.ReturnVarName); - if (pointer.Pointee is TypedefType) - Context.Return.Write("reinterpret_cast<{0}>({1})", pointer, - returnVarName); + { + var nativeTypePrinter = new CppTypePrinter(Context.Driver.TypeDatabase, false); + var constlessPointer = new PointerType() + { + IsDependent = pointer.IsDependent, + Modifier = pointer.Modifier, + QualifiedPointee = new QualifiedType(Context.ReturnType.Type.GetPointee()) + }; + var nativeConstlessTypeName = constlessPointer.Visit(nativeTypePrinter, new TypeQualifiers()); + returnVarName = string.Format("const_cast<{0}>({1})", + nativeConstlessTypeName, Context.ReturnVarName); + } + if (pointer.Pointee is TypedefType) + { + var desugaredPointer = new PointerType() + { + IsDependent = pointer.IsDependent, + Modifier = pointer.Modifier, + QualifiedPointee = new QualifiedType(pointee) + }; + var nativeTypePrinter = new CppTypePrinter(Context.Driver.TypeDatabase); + var nativeTypeName = desugaredPointer.Visit(nativeTypePrinter, quals); + Context.Return.Write("reinterpret_cast<{0}>({1})", nativeTypeName, + returnVarName); + } else Context.Return.Write(returnVarName); return true; diff --git a/src/Generator/Generators/CLI/CLISourcesTemplate.cs b/src/Generator/Generators/CLI/CLISourcesTemplate.cs index 6215dfdd..3ad35f77 100644 --- a/src/Generator/Generators/CLI/CLISourcesTemplate.cs +++ b/src/Generator/Generators/CLI/CLISourcesTemplate.cs @@ -372,16 +372,16 @@ namespace CppSharp.Generators.CLI return; } var param = new Parameter - { - Name = "value", - QualifiedType = decl.QualifiedType - }; + { + Name = "value", + QualifiedType = new QualifiedType(type) + }; var ctx = new MarshalContext(Driver) - { - Parameter = param, - ArgName = param.Name, - }; + { + Parameter = param, + ArgName = param.Name, + }; var marshal = new CLIMarshalManagedToNativePrinter(ctx); param.Visit(marshal); @@ -400,7 +400,10 @@ namespace CppSharp.Generators.CLI if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) Write(marshal.Context.SupportBefore); - WriteLine("{0} = {1};", variable, marshal.Context.Return); + if (isIndexer && decl.Type.IsPointer()) + WriteLine("*({0}) = {1};", variable, marshal.Context.Return); + else + WriteLine("{0} = {1};", variable, marshal.Context.Return); } WriteCloseBraceIndent(); @@ -432,7 +435,10 @@ namespace CppSharp.Generators.CLI if (decl is Function) { var func = decl as Function; - GenerateFunctionCall(func, @class); + if (isIndexer && func.Type.IsAddress()) + GenerateFunctionCall(func, @class, type); + else + GenerateFunctionCall(func, @class); } else { @@ -832,11 +838,13 @@ namespace CppSharp.Generators.CLI WriteCloseBraceIndent(); } - public void GenerateFunctionCall(Function function, Class @class = null) + public void GenerateFunctionCall(Function function, Class @class = null, Type publicRetType = null) { CheckArgumentRange(function); var retType = function.ReturnType; + if (publicRetType == null) + publicRetType = retType.Type; var needsReturn = !retType.Type.IsPrimitiveType(PrimitiveType.Void); const string valueMarshalName = "_this0"; @@ -861,9 +869,12 @@ namespace CppSharp.Generators.CLI var @params = GenerateFunctionParamsMarshal(function.Parameters, function); + var returnIdentifier = Generator.GeneratedIdentifier("ret"); if (needsReturn) - Write("auto {0}{1} = ",(function.ReturnType.Type.IsReference())? "&": string.Empty, - Generator.GeneratedIdentifier("ret")); + if (retType.Type.IsReference()) + Write("auto &{0} = ", returnIdentifier); + else + Write("auto {0} = ", returnIdentifier); if (function.OperatorKind == CXXOperatorKind.Conversion || function.OperatorKind == CXXOperatorKind.ExplicitConversion) @@ -944,24 +955,33 @@ namespace CppSharp.Generators.CLI if (retType.Type.IsPointer() && (isIntPtr || retTypeName.EndsWith("^"))) { WriteLine("if ({0} == nullptr) return {1};", - Generator.GeneratedIdentifier("ret"), + returnIdentifier, isIntPtr ? "System::IntPtr()" : "nullptr"); } var ctx = new MarshalContext(Driver) - { - ArgName = Generator.GeneratedIdentifier("ret"), - ReturnVarName = Generator.GeneratedIdentifier("ret"), - ReturnType = retType - }; + { + ArgName = returnIdentifier, + ReturnVarName = returnIdentifier, + ReturnType = retType + }; var marshal = new CLIMarshalNativeToManagedPrinter(ctx); - function.ReturnType.Visit(marshal); + retType.Visit(marshal); if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) Write(marshal.Context.SupportBefore); - WriteLine("return {0};", marshal.Context.Return); + // Special case for indexer - needs to dereference if the internal + // function is a pointer type and the property is not. + if (retType.Type.IsPointer() && + retType.Type.GetPointee().Equals(publicRetType) && + publicRetType.IsPrimitiveType()) + WriteLine("return *({0});", marshal.Context.Return); + else if (retType.Type.IsReference() && publicRetType.IsReference()) + WriteLine("return ({0})({1});", publicRetType, marshal.Context.Return); + else + WriteLine("return {0};", marshal.Context.Return); } } diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index 0a7ea727..03d9e5a3 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -967,8 +967,8 @@ namespace CppSharp.Generators.CSharp private void GenerateIndexerSetter(QualifiedType returnType, Function function) { - Type type; - returnType.Type.IsPointerTo(out type); + Type type; + function.Type.IsPointerTo(out type); PrimitiveType primitiveType; string internalFunction = GetFunctionNativeIdentifier(function); if (type.IsPrimitiveType(out primitiveType)) @@ -984,7 +984,7 @@ namespace CppSharp.Generators.CSharp } } - private void GeneratePropertyGetter(T decl, Class @class) + private void GeneratePropertyGetter(QualifiedType returnType, T decl, Class @class) where T : Declaration, ITypedDecl { PushBlock(CSharpBlockKind.Method); @@ -1004,19 +1004,9 @@ namespace CppSharp.Generators.CSharp WriteStartBraceIndent(); var method = function as Method; if (method != null && method.IsOverride && method.IsSynthetized) - { GenerateVirtualTableFunctionCall(method, @class); - } else - { - bool isPrimitiveIndexer = function.OperatorKind == CXXOperatorKind.Subscript && - function.ReturnType.Type.IsPointerToPrimitiveType(); - if (isPrimitiveIndexer) - TypePrinter.PushContext(CSharpTypePrinterContextKind.PrimitiveIndexer); - GenerateInternalFunctionCall(function); - if (isPrimitiveIndexer) - TypePrinter.PopContext(); - } + GenerateInternalFunctionCall(function, function.Parameters, returnType.Type); } else if (decl is Field) { @@ -1185,12 +1175,6 @@ namespace CppSharp.Generators.CSharp PushBlock(CSharpBlockKind.Property); - // If this is an indexer that returns an address use the real type - // because there is a setter anyway. - var type = prop.Type; - if (prop.Parameters.Count > 0 && prop.Type.IsPointerToPrimitiveType()) - type = ((PointerType) prop.Type).Pointee; - ArrayType arrayType = prop.Type as ArrayType; if (arrayType != null && arrayType.Type.IsPointerToPrimitiveType() && prop.Field != null) { @@ -1214,11 +1198,11 @@ namespace CppSharp.Generators.CSharp else if (prop.IsVirtual) Write("virtual "); - WriteLine("{0} {1}", type, GetPropertyName(prop)); + WriteLine("{0} {1}", prop.Type, GetPropertyName(prop)); } else { - WriteLine("{0} {1}.{2}", type, prop.ExplicitInterfaceImpl.Name, + WriteLine("{0} {1}.{2}", prop.Type, prop.ExplicitInterfaceImpl.Name, GetPropertyName(prop)); } WriteStartBraceIndent(); @@ -1226,7 +1210,7 @@ namespace CppSharp.Generators.CSharp if (prop.Field != null) { if (prop.HasGetter) - GeneratePropertyGetter(prop.Field, @class); + GeneratePropertyGetter(prop.QualifiedType, prop.Field, @class); if (prop.HasSetter) GeneratePropertySetter(prop.Field.QualifiedType, prop.Field, @@ -1234,13 +1218,11 @@ namespace CppSharp.Generators.CSharp } else { - if (prop.HasGetter) - GeneratePropertyGetter(prop.GetMethod, @class); + if (prop.HasGetter) + GeneratePropertyGetter(prop.QualifiedType, prop.GetMethod, @class); if (prop.HasSetter) - GeneratePropertySetter(prop.GetMethod != null ? - prop.GetMethod.ReturnType : prop.SetMethod.Parameters[0].QualifiedType, - prop.SetMethod, @class); + GeneratePropertySetter(prop.QualifiedType, prop.SetMethod, @class); } WriteCloseBraceIndent(); @@ -1260,7 +1242,7 @@ namespace CppSharp.Generators.CSharp WriteLine("public static {0} {1}", type, variable.Name); WriteStartBraceIndent(); - GeneratePropertyGetter(variable, @class); + GeneratePropertyGetter(variable.QualifiedType, variable, @class); if (!variable.QualifiedType.Qualifiers.IsConst) GeneratePropertySetter(variable.QualifiedType, variable, @class); @@ -2185,8 +2167,8 @@ namespace CppSharp.Generators.CSharp GenerateVTableClassSetupCall(@class); } - public void GenerateInternalFunctionCall(Function function, - List parameters = null) + public void GenerateInternalFunctionCall(Function function, + List parameters = null, Type returnType = null) { if (parameters == null) parameters = function.Parameters; @@ -2194,11 +2176,11 @@ namespace CppSharp.Generators.CSharp CheckArgumentRange(function); var functionName = string.Format("Internal.{0}", GetFunctionNativeIdentifier(function)); - GenerateFunctionCall(functionName, parameters, function); + GenerateFunctionCall(functionName, parameters, function, returnType); } - public void GenerateFunctionCall(string functionName, List parameters, - Function function) + public void GenerateFunctionCall(string functionName, List parameters, + Function function, Type returnType = null) { if (function.IsPure) { @@ -2206,7 +2188,9 @@ namespace CppSharp.Generators.CSharp return; } - var retType = function.OriginalReturnType; + var retType = function.OriginalReturnType; + if (returnType == null) + returnType = retType.Type; var needsReturn = !retType.Type.IsPrimitiveType(PrimitiveType.Void); var isValueType = false; @@ -2365,10 +2349,14 @@ namespace CppSharp.Generators.CSharp if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) Write(marshal.Context.SupportBefore); - WriteLine( - TypePrinter.ContextKind == CSharpTypePrinterContextKind.PrimitiveIndexer - ? "return *{0};" - : "return {0};", marshal.Context.Return); + // Special case for indexer - needs to dereference if the internal + // function is a pointer type and the property is not. + if (retType.Type.IsAddress() && + retType.Type.GetPointee().Equals(returnType) && + returnType.IsPrimitiveType()) + WriteLine("return *{0};", marshal.Context.Return); + else + WriteLine("return {0};", marshal.Context.Return); } } diff --git a/src/Generator/Passes/CheckOperatorsOverloads.cs b/src/Generator/Passes/CheckOperatorsOverloads.cs index 980671ab..166a8cef 100644 --- a/src/Generator/Passes/CheckOperatorsOverloads.cs +++ b/src/Generator/Passes/CheckOperatorsOverloads.cs @@ -97,21 +97,24 @@ namespace CppSharp.Passes GetMethod = @operator }; - if (!@operator.ReturnType.Qualifiers.IsConst && @operator.ReturnType.Type.IsAddress()) - property.SetMethod = @operator; - + var returnType = @operator.Type; + if (returnType.IsAddress()) + { + var pointer = returnType as PointerType; + var qualifiedPointee = pointer.QualifiedPointee; + if (!qualifiedPointee.Qualifiers.IsConst) + property.SetMethod = @operator; + } + + // If we've a setter use the pointee as the type of the property. + var pointerType = property.Type as PointerType; + if (pointerType != null && property.HasSetter) + property.QualifiedType = new QualifiedType( + pointerType.Pointee, property.QualifiedType.Qualifiers); + if (Driver.Options.IsCLIGenerator) - { - // If we've a setter use the pointee as the type of the property. - var pointerType = property.Type as PointerType; - if (pointerType != null && property.HasSetter) - { - property.QualifiedType = new QualifiedType(pointerType.Pointee, property.QualifiedType.Qualifiers); - property.GetMethod.ReturnType = property.QualifiedType; - } // C++/CLI uses "default" as the indexer property name. property.Name = "default"; - } property.Parameters.AddRange(@operator.Parameters); diff --git a/src/Generator/Types/CppTypePrinter.cs b/src/Generator/Types/CppTypePrinter.cs index 9e084752..d2d2ad5d 100644 --- a/src/Generator/Types/CppTypePrinter.cs +++ b/src/Generator/Types/CppTypePrinter.cs @@ -17,10 +17,12 @@ namespace CppSharp.Types { public CppTypePrintScopeKind PrintScopeKind; public bool PrintLogicalNames; + public bool PrintTypeQualifiers; - public CppTypePrinter(ITypeMapDatabase database) + public CppTypePrinter(ITypeMapDatabase database, bool printTypeQualifiers = true) { PrintScopeKind = CppTypePrintScopeKind.GlobalQualified; + PrintTypeQualifiers = printTypeQualifiers; } public string VisitTagType(TagType tag, TypeQualifiers quals) @@ -78,7 +80,7 @@ namespace CppSharp.Types var pointeeType = pointer.Pointee.Visit(this, quals); var mod = ConvertModifierToString(pointer.Modifier); - var s = quals.IsConst ? "const " : string.Empty; + var s = PrintTypeQualifiers && quals.IsConst ? "const " : string.Empty; s += string.Format("{0}{1}", pointeeType, mod); return s; diff --git a/tests/Basic/Basic.Tests.cs b/tests/Basic/Basic.Tests.cs index cdec7817..8cbb0520 100644 --- a/tests/Basic/Basic.Tests.cs +++ b/tests/Basic/Basic.Tests.cs @@ -238,11 +238,23 @@ public class BasicTests : GeneratorTestFixture [Test] public unsafe void TestIndexers() { - var properties = new TestIndexedProperties(); - Assert.AreEqual(1, properties[0]); - Assert.AreEqual(1, properties["foo"]); - properties[0] = 2; - Assert.AreEqual(2, properties[0]); + var indexedProperties = new TestIndexedProperties(); + Assert.AreEqual(1, indexedProperties[0]); + Assert.AreEqual(1, indexedProperties["foo"]); + indexedProperties[0] = 2; + Assert.AreEqual(2, indexedProperties[0]); + indexedProperties[0f] = 3; + Assert.AreEqual(3, indexedProperties[0f]); + var properties = indexedProperties[(byte)0]; + Assert.AreEqual(0, properties.Field); + var newProperties = new TestProperties(); + newProperties.Field = 4; + indexedProperties[(byte)0] = newProperties; + Assert.AreEqual(4, indexedProperties[(byte)0].Field); + newProperties = indexedProperties[(short)0]; + Assert.AreEqual(4, newProperties.Field); + newProperties.Field = 5; + Assert.AreEqual(5, indexedProperties[(byte)0].Field); } [Test] diff --git a/tests/Basic/Basic.h b/tests/Basic/Basic.h index 574dd5a7..c3715f40 100644 --- a/tests/Basic/Basic.h +++ b/tests/Basic/Basic.h @@ -393,17 +393,30 @@ void TestProperties::setFieldValue(int Value) { Field = Value; } class DLL_API TestIndexedProperties { foo_t p; + TestProperties f; public: TestIndexedProperties(); // Should lead to a read/write indexer with return type uint foo_t& operator[](int i); + // Should lead to a read/write indexer with return type uint + foo_t* operator[](float f); // Should lead to a read-only indexer with return type uint foo_t operator[](const char* name); + // Should lead to a read-only indexer with return type uint* + const foo_t& operator[](double d); + // Should lead to a read/write indexer with return type TestProperties + TestProperties* operator[](unsigned char b); + // Should lead to a read-only indexer with return type TestProperties + const TestProperties& operator[](short b); }; -TestIndexedProperties::TestIndexedProperties() : p(1) {} +TestIndexedProperties::TestIndexedProperties() : p(1), f() {} foo_t& TestIndexedProperties::operator[](int i) { return p; } foo_t TestIndexedProperties::operator[](const char* name) { return p; } +foo_t* TestIndexedProperties::operator[](float f) { return &p; } +const foo_t& TestIndexedProperties::operator[](double f) { return p; } +TestProperties* TestIndexedProperties::operator[](unsigned char b) { return &f; } +const TestProperties& TestIndexedProperties::operator[](short b) { return f; } enum struct MyEnum { A, B, C };