Browse Source

Improved support for indexed properties.

More types and types with different qualifiers are supported now - in both backends. See test cases for details.
pull/233/head
Elias Holzer 11 years ago
parent
commit
0c260bd223
  1. 30
      src/Generator/Generators/CLI/CLIMarshal.cs
  2. 62
      src/Generator/Generators/CLI/CLISourcesTemplate.cs
  3. 66
      src/Generator/Generators/CSharp/CSharpTextTemplate.cs
  4. 27
      src/Generator/Passes/CheckOperatorsOverloads.cs
  5. 6
      src/Generator/Types/CppTypePrinter.cs
  6. 22
      tests/Basic/Basic.Tests.cs
  7. 15
      tests/Basic/Basic.h

30
src/Generator/Generators/CLI/CLIMarshal.cs

@ -93,11 +93,31 @@ namespace CppSharp.Generators.CLI
{ {
var returnVarName = Context.ReturnVarName; var returnVarName = Context.ReturnVarName;
if (quals.IsConst != Context.ReturnType.Qualifiers.IsConst) if (quals.IsConst != Context.ReturnType.Qualifiers.IsConst)
returnVarName = string.Format("const_cast<{0}>({1})", {
Context.ReturnType, Context.ReturnVarName); var nativeTypePrinter = new CppTypePrinter(Context.Driver.TypeDatabase, false);
if (pointer.Pointee is TypedefType) var constlessPointer = new PointerType()
Context.Return.Write("reinterpret_cast<{0}>({1})", pointer, {
returnVarName); 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 else
Context.Return.Write(returnVarName); Context.Return.Write(returnVarName);
return true; return true;

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

@ -372,16 +372,16 @@ namespace CppSharp.Generators.CLI
return; return;
} }
var param = new Parameter var param = new Parameter
{ {
Name = "value", Name = "value",
QualifiedType = decl.QualifiedType QualifiedType = new QualifiedType(type)
}; };
var ctx = new MarshalContext(Driver) var ctx = new MarshalContext(Driver)
{ {
Parameter = param, Parameter = param,
ArgName = param.Name, ArgName = param.Name,
}; };
var marshal = new CLIMarshalManagedToNativePrinter(ctx); var marshal = new CLIMarshalManagedToNativePrinter(ctx);
param.Visit(marshal); param.Visit(marshal);
@ -400,7 +400,10 @@ namespace CppSharp.Generators.CLI
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore))
Write(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(); WriteCloseBraceIndent();
@ -432,7 +435,10 @@ namespace CppSharp.Generators.CLI
if (decl is Function) if (decl is Function)
{ {
var func = decl as Function; var func = decl as Function;
GenerateFunctionCall(func, @class); if (isIndexer && func.Type.IsAddress())
GenerateFunctionCall(func, @class, type);
else
GenerateFunctionCall(func, @class);
} }
else else
{ {
@ -832,11 +838,13 @@ namespace CppSharp.Generators.CLI
WriteCloseBraceIndent(); WriteCloseBraceIndent();
} }
public void GenerateFunctionCall(Function function, Class @class = null) public void GenerateFunctionCall(Function function, Class @class = null, Type publicRetType = null)
{ {
CheckArgumentRange(function); CheckArgumentRange(function);
var retType = function.ReturnType; var retType = function.ReturnType;
if (publicRetType == null)
publicRetType = retType.Type;
var needsReturn = !retType.Type.IsPrimitiveType(PrimitiveType.Void); var needsReturn = !retType.Type.IsPrimitiveType(PrimitiveType.Void);
const string valueMarshalName = "_this0"; const string valueMarshalName = "_this0";
@ -861,9 +869,12 @@ namespace CppSharp.Generators.CLI
var @params = GenerateFunctionParamsMarshal(function.Parameters, function); var @params = GenerateFunctionParamsMarshal(function.Parameters, function);
var returnIdentifier = Generator.GeneratedIdentifier("ret");
if (needsReturn) if (needsReturn)
Write("auto {0}{1} = ",(function.ReturnType.Type.IsReference())? "&": string.Empty, if (retType.Type.IsReference())
Generator.GeneratedIdentifier("ret")); Write("auto &{0} = ", returnIdentifier);
else
Write("auto {0} = ", returnIdentifier);
if (function.OperatorKind == CXXOperatorKind.Conversion || if (function.OperatorKind == CXXOperatorKind.Conversion ||
function.OperatorKind == CXXOperatorKind.ExplicitConversion) function.OperatorKind == CXXOperatorKind.ExplicitConversion)
@ -944,24 +955,33 @@ namespace CppSharp.Generators.CLI
if (retType.Type.IsPointer() && (isIntPtr || retTypeName.EndsWith("^"))) if (retType.Type.IsPointer() && (isIntPtr || retTypeName.EndsWith("^")))
{ {
WriteLine("if ({0} == nullptr) return {1};", WriteLine("if ({0} == nullptr) return {1};",
Generator.GeneratedIdentifier("ret"), returnIdentifier,
isIntPtr ? "System::IntPtr()" : "nullptr"); isIntPtr ? "System::IntPtr()" : "nullptr");
} }
var ctx = new MarshalContext(Driver) var ctx = new MarshalContext(Driver)
{ {
ArgName = Generator.GeneratedIdentifier("ret"), ArgName = returnIdentifier,
ReturnVarName = Generator.GeneratedIdentifier("ret"), ReturnVarName = returnIdentifier,
ReturnType = retType ReturnType = retType
}; };
var marshal = new CLIMarshalNativeToManagedPrinter(ctx); var marshal = new CLIMarshalNativeToManagedPrinter(ctx);
function.ReturnType.Visit(marshal); retType.Visit(marshal);
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore))
Write(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);
} }
} }

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

@ -967,8 +967,8 @@ namespace CppSharp.Generators.CSharp
private void GenerateIndexerSetter(QualifiedType returnType, Function function) private void GenerateIndexerSetter(QualifiedType returnType, Function function)
{ {
Type type; Type type;
returnType.Type.IsPointerTo(out type); function.Type.IsPointerTo(out type);
PrimitiveType primitiveType; PrimitiveType primitiveType;
string internalFunction = GetFunctionNativeIdentifier(function); string internalFunction = GetFunctionNativeIdentifier(function);
if (type.IsPrimitiveType(out primitiveType)) if (type.IsPrimitiveType(out primitiveType))
@ -984,7 +984,7 @@ namespace CppSharp.Generators.CSharp
} }
} }
private void GeneratePropertyGetter<T>(T decl, Class @class) private void GeneratePropertyGetter<T>(QualifiedType returnType, T decl, Class @class)
where T : Declaration, ITypedDecl where T : Declaration, ITypedDecl
{ {
PushBlock(CSharpBlockKind.Method); PushBlock(CSharpBlockKind.Method);
@ -1004,19 +1004,9 @@ namespace CppSharp.Generators.CSharp
WriteStartBraceIndent(); WriteStartBraceIndent();
var method = function as Method; var method = function as Method;
if (method != null && method.IsOverride && method.IsSynthetized) if (method != null && method.IsOverride && method.IsSynthetized)
{
GenerateVirtualTableFunctionCall(method, @class); GenerateVirtualTableFunctionCall(method, @class);
}
else else
{ GenerateInternalFunctionCall(function, function.Parameters, returnType.Type);
bool isPrimitiveIndexer = function.OperatorKind == CXXOperatorKind.Subscript &&
function.ReturnType.Type.IsPointerToPrimitiveType();
if (isPrimitiveIndexer)
TypePrinter.PushContext(CSharpTypePrinterContextKind.PrimitiveIndexer);
GenerateInternalFunctionCall(function);
if (isPrimitiveIndexer)
TypePrinter.PopContext();
}
} }
else if (decl is Field) else if (decl is Field)
{ {
@ -1185,12 +1175,6 @@ namespace CppSharp.Generators.CSharp
PushBlock(CSharpBlockKind.Property); 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; ArrayType arrayType = prop.Type as ArrayType;
if (arrayType != null && arrayType.Type.IsPointerToPrimitiveType() && prop.Field != null) if (arrayType != null && arrayType.Type.IsPointerToPrimitiveType() && prop.Field != null)
{ {
@ -1214,11 +1198,11 @@ namespace CppSharp.Generators.CSharp
else if (prop.IsVirtual) else if (prop.IsVirtual)
Write("virtual "); Write("virtual ");
WriteLine("{0} {1}", type, GetPropertyName(prop)); WriteLine("{0} {1}", prop.Type, GetPropertyName(prop));
} }
else else
{ {
WriteLine("{0} {1}.{2}", type, prop.ExplicitInterfaceImpl.Name, WriteLine("{0} {1}.{2}", prop.Type, prop.ExplicitInterfaceImpl.Name,
GetPropertyName(prop)); GetPropertyName(prop));
} }
WriteStartBraceIndent(); WriteStartBraceIndent();
@ -1226,7 +1210,7 @@ namespace CppSharp.Generators.CSharp
if (prop.Field != null) if (prop.Field != null)
{ {
if (prop.HasGetter) if (prop.HasGetter)
GeneratePropertyGetter(prop.Field, @class); GeneratePropertyGetter(prop.QualifiedType, prop.Field, @class);
if (prop.HasSetter) if (prop.HasSetter)
GeneratePropertySetter(prop.Field.QualifiedType, prop.Field, GeneratePropertySetter(prop.Field.QualifiedType, prop.Field,
@ -1234,13 +1218,11 @@ namespace CppSharp.Generators.CSharp
} }
else else
{ {
if (prop.HasGetter) if (prop.HasGetter)
GeneratePropertyGetter(prop.GetMethod, @class); GeneratePropertyGetter(prop.QualifiedType, prop.GetMethod, @class);
if (prop.HasSetter) if (prop.HasSetter)
GeneratePropertySetter(prop.GetMethod != null ? GeneratePropertySetter(prop.QualifiedType, prop.SetMethod, @class);
prop.GetMethod.ReturnType : prop.SetMethod.Parameters[0].QualifiedType,
prop.SetMethod, @class);
} }
WriteCloseBraceIndent(); WriteCloseBraceIndent();
@ -1260,7 +1242,7 @@ namespace CppSharp.Generators.CSharp
WriteLine("public static {0} {1}", type, variable.Name); WriteLine("public static {0} {1}", type, variable.Name);
WriteStartBraceIndent(); WriteStartBraceIndent();
GeneratePropertyGetter(variable, @class); GeneratePropertyGetter(variable.QualifiedType, variable, @class);
if (!variable.QualifiedType.Qualifiers.IsConst) if (!variable.QualifiedType.Qualifiers.IsConst)
GeneratePropertySetter(variable.QualifiedType, variable, @class); GeneratePropertySetter(variable.QualifiedType, variable, @class);
@ -2185,8 +2167,8 @@ namespace CppSharp.Generators.CSharp
GenerateVTableClassSetupCall(@class); GenerateVTableClassSetupCall(@class);
} }
public void GenerateInternalFunctionCall(Function function, public void GenerateInternalFunctionCall(Function function,
List<Parameter> parameters = null) List<Parameter> parameters = null, Type returnType = null)
{ {
if (parameters == null) if (parameters == null)
parameters = function.Parameters; parameters = function.Parameters;
@ -2194,11 +2176,11 @@ namespace CppSharp.Generators.CSharp
CheckArgumentRange(function); CheckArgumentRange(function);
var functionName = string.Format("Internal.{0}", var functionName = string.Format("Internal.{0}",
GetFunctionNativeIdentifier(function)); GetFunctionNativeIdentifier(function));
GenerateFunctionCall(functionName, parameters, function); GenerateFunctionCall(functionName, parameters, function, returnType);
} }
public void GenerateFunctionCall(string functionName, List<Parameter> parameters, public void GenerateFunctionCall(string functionName, List<Parameter> parameters,
Function function) Function function, Type returnType = null)
{ {
if (function.IsPure) if (function.IsPure)
{ {
@ -2206,7 +2188,9 @@ namespace CppSharp.Generators.CSharp
return; return;
} }
var retType = function.OriginalReturnType; var retType = function.OriginalReturnType;
if (returnType == null)
returnType = retType.Type;
var needsReturn = !retType.Type.IsPrimitiveType(PrimitiveType.Void); var needsReturn = !retType.Type.IsPrimitiveType(PrimitiveType.Void);
var isValueType = false; var isValueType = false;
@ -2365,10 +2349,14 @@ namespace CppSharp.Generators.CSharp
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore))
Write(marshal.Context.SupportBefore); Write(marshal.Context.SupportBefore);
WriteLine( // Special case for indexer - needs to dereference if the internal
TypePrinter.ContextKind == CSharpTypePrinterContextKind.PrimitiveIndexer // function is a pointer type and the property is not.
? "return *{0};" if (retType.Type.IsAddress() &&
: "return {0};", marshal.Context.Return); retType.Type.GetPointee().Equals(returnType) &&
returnType.IsPrimitiveType())
WriteLine("return *{0};", marshal.Context.Return);
else
WriteLine("return {0};", marshal.Context.Return);
} }
} }

27
src/Generator/Passes/CheckOperatorsOverloads.cs

@ -97,21 +97,24 @@ namespace CppSharp.Passes
GetMethod = @operator GetMethod = @operator
}; };
if (!@operator.ReturnType.Qualifiers.IsConst && @operator.ReturnType.Type.IsAddress()) var returnType = @operator.Type;
property.SetMethod = @operator; 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 (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. // C++/CLI uses "default" as the indexer property name.
property.Name = "default"; property.Name = "default";
}
property.Parameters.AddRange(@operator.Parameters); property.Parameters.AddRange(@operator.Parameters);

6
src/Generator/Types/CppTypePrinter.cs

@ -17,10 +17,12 @@ namespace CppSharp.Types
{ {
public CppTypePrintScopeKind PrintScopeKind; public CppTypePrintScopeKind PrintScopeKind;
public bool PrintLogicalNames; public bool PrintLogicalNames;
public bool PrintTypeQualifiers;
public CppTypePrinter(ITypeMapDatabase database) public CppTypePrinter(ITypeMapDatabase database, bool printTypeQualifiers = true)
{ {
PrintScopeKind = CppTypePrintScopeKind.GlobalQualified; PrintScopeKind = CppTypePrintScopeKind.GlobalQualified;
PrintTypeQualifiers = printTypeQualifiers;
} }
public string VisitTagType(TagType tag, TypeQualifiers quals) public string VisitTagType(TagType tag, TypeQualifiers quals)
@ -78,7 +80,7 @@ namespace CppSharp.Types
var pointeeType = pointer.Pointee.Visit(this, quals); var pointeeType = pointer.Pointee.Visit(this, quals);
var mod = ConvertModifierToString(pointer.Modifier); 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); s += string.Format("{0}{1}", pointeeType, mod);
return s; return s;

22
tests/Basic/Basic.Tests.cs

@ -238,11 +238,23 @@ public class BasicTests : GeneratorTestFixture
[Test] [Test]
public unsafe void TestIndexers() public unsafe void TestIndexers()
{ {
var properties = new TestIndexedProperties(); var indexedProperties = new TestIndexedProperties();
Assert.AreEqual(1, properties[0]); Assert.AreEqual(1, indexedProperties[0]);
Assert.AreEqual(1, properties["foo"]); Assert.AreEqual(1, indexedProperties["foo"]);
properties[0] = 2; indexedProperties[0] = 2;
Assert.AreEqual(2, properties[0]); 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] [Test]

15
tests/Basic/Basic.h

@ -393,17 +393,30 @@ void TestProperties::setFieldValue(int Value) { Field = Value; }
class DLL_API TestIndexedProperties class DLL_API TestIndexedProperties
{ {
foo_t p; foo_t p;
TestProperties f;
public: public:
TestIndexedProperties(); TestIndexedProperties();
// Should lead to a read/write indexer with return type uint // Should lead to a read/write indexer with return type uint
foo_t& operator[](int i); 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 // Should lead to a read-only indexer with return type uint
foo_t operator[](const char* name); 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[](int i) { return p; }
foo_t TestIndexedProperties::operator[](const char* name) { 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 }; enum struct MyEnum { A, B, C };

Loading…
Cancel
Save