diff --git a/src/AST/Class.cs b/src/AST/Class.cs index b1893f71..029d39b5 100644 --- a/src/AST/Class.cs +++ b/src/AST/Class.cs @@ -59,6 +59,7 @@ namespace CppSharp.AST { ValueType, RefType, + Interface } // Represents a C++ record Decl. @@ -139,6 +140,8 @@ namespace CppSharp.AST } } + public Class OriginalClass { get; set; } + public bool IsValueType { get { return Type == ClassType.ValueType || IsUnion; } @@ -149,6 +152,11 @@ namespace CppSharp.AST get { return Type == ClassType.RefType && !IsUnion; } } + public bool IsInterface + { + get { return Type == ClassType.Interface; } + } + public IEnumerable Constructors { get @@ -201,9 +209,10 @@ namespace CppSharp.AST } } - public Method GetRootBaseMethod(Method @override) + public Method GetRootBaseMethod(Method @override, bool onlyFirstBase = false) { return (from @base in Bases + where !onlyFirstBase || !@base.Class.IsInterface let baseMethod = ( from method in @base.Class.Methods where @@ -213,8 +222,26 @@ namespace CppSharp.AST method.Parameters.SequenceEqual(@override.Parameters, new ParameterTypeComparer()) select method).FirstOrDefault() - let rootBaseMethod = @base.Class.GetRootBaseMethod(@override) - select rootBaseMethod ?? baseMethod).FirstOrDefault(); + let rootBaseMethod = @base.Class.GetRootBaseMethod(@override) ?? baseMethod + where rootBaseMethod != null || onlyFirstBase + select rootBaseMethod).FirstOrDefault(); + } + + public Property GetRootBaseProperty(Property @override, bool onlyFirstBase = false) + { + return (from @base in Bases + where !onlyFirstBase || !@base.Class.IsInterface + let baseProperty = ( + from property in @base.Class.Properties + where + property.Name == @override.Name && + property.Parameters.Count == @override.Parameters.Count && + property.Parameters.SequenceEqual(@override.Parameters, + new ParameterTypeComparer()) + select property).FirstOrDefault() + let rootBaseProperty = @base.Class.GetRootBaseProperty(@override) ?? baseProperty + where rootBaseProperty != null || onlyFirstBase + select rootBaseProperty).FirstOrDefault(); } public override T Visit(IDeclVisitor visitor) diff --git a/src/AST/Method.cs b/src/AST/Method.cs index 65bbf895..c51746da 100644 --- a/src/AST/Method.cs +++ b/src/AST/Method.cs @@ -126,6 +126,8 @@ namespace CppSharp.AST public MethodConversionKind Conversion { get; set; } + public Class ExplicitInterfaceImpl { get; set; } + public override QualifiedType GetFunctionType() { var qualifiedType = base.GetFunctionType(); diff --git a/src/AST/Property.cs b/src/AST/Property.cs index b3394aa8..50a34440 100644 --- a/src/AST/Property.cs +++ b/src/AST/Property.cs @@ -7,6 +7,20 @@ namespace CppSharp.AST /// public class Property : Declaration, ITypedDecl { + public Property() + { + } + + public Property(Property property) + : base(property) + { + QualifiedType = property.QualifiedType; + GetMethod = property.GetMethod; + SetMethod = property.SetMethod; + Field = property.Field; + parameters.AddRange(property.Parameters); + } + public Type Type { get { return QualifiedType.Type; } @@ -38,6 +52,8 @@ namespace CppSharp.AST // The field that should be get and set by this property public Field Field { get; set; } + public Class ExplicitInterfaceImpl { get; set; } + private readonly List parameters = new List(); /// diff --git a/src/Generator/Driver.cs b/src/Generator/Driver.cs index 92e3b6a7..e9bb04f3 100644 --- a/src/Generator/Driver.cs +++ b/src/Generator/Driver.cs @@ -153,6 +153,11 @@ namespace CppSharp TranslationUnitPasses.AddPass(new CheckDuplicatedNamesPass()); if (Options.GenerateAbstractImpls) TranslationUnitPasses.AddPass(new GenerateAbstractImplementationsPass()); + if (Options.GenerateInterfacesForMultipleInheritance) + { + TranslationUnitPasses.AddPass(new MultipleInheritancePass()); + TranslationUnitPasses.AddPass(new ParamTypeToInterfacePass()); + } } public void ProcessCode() @@ -271,6 +276,7 @@ namespace CppSharp public bool GeneratePartialClasses; public bool GenerateVirtualTables; public bool GenerateAbstractImpls; + public bool GenerateInterfacesForMultipleInheritance; public bool GenerateInternalImports; public string IncludePrefix; public bool WriteOnlyWhenChanged; diff --git a/src/Generator/Generators/CSharp/CSharpMarshal.cs b/src/Generator/Generators/CSharp/CSharpMarshal.cs index 397d6791..4ede156b 100644 --- a/src/Generator/Generators/CSharp/CSharpMarshal.cs +++ b/src/Generator/Generators/CSharp/CSharpMarshal.cs @@ -277,7 +277,7 @@ namespace CppSharp.Generators.CSharp } Context.Return.Write("new {0}({1})", - QualifiedIdentifier(@class) + + QualifiedIdentifier(@class.OriginalClass ?? @class) + (Context.Driver.Options.GenerateAbstractImpls && @class.IsAbstract ? "Internal" : ""), instance); @@ -541,9 +541,10 @@ namespace CppSharp.Generators.CSharp return; } - Context.Return.Write("*({0}.Internal*){1}.{2}", CSharpMarshalNativeToManagedPrinter.QualifiedIdentifier(@class), + var qualifiedIdentifier = CSharpMarshalNativeToManagedPrinter.QualifiedIdentifier( + @class.OriginalClass ?? @class); + Context.Return.Write("*({0}.Internal*){1}.{2}", qualifiedIdentifier, Helpers.SafeIdentifier(Context.Parameter.Name), Helpers.InstanceIdentifier); - } private void MarshalValueClass(Class @class) diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index d97fd17b..0ad88e82 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -30,8 +30,7 @@ namespace CppSharp.Generators.CSharp public static string SafeIdentifier(string id) { - id = new string(((IEnumerable)id) - .Select(c => char.IsLetterOrDigit(c) ? c : '_').ToArray()); + id = new string(id.Select(c => char.IsLetterOrDigit(c) ? c : '_').ToArray()); return Keywords.Contains(id) ? "@" + id : id; } @@ -59,9 +58,9 @@ namespace CppSharp.Generators.CSharp get { return Generator.GeneratedIdentifier("Instance"); } } - public static string GetAccess(Class @class) + public static string GetAccess(AccessSpecifier accessSpecifier) { - switch (@class.Access) + switch (accessSpecifier) { case AccessSpecifier.Private: return "internal "; @@ -93,6 +92,7 @@ namespace CppSharp.Generators.CSharp public const int Field = FIRST + 14; public const int VTableDelegate = FIRST + 16; public const int Region = FIRST + 17; + public const int Interface = FIRST + 18; } public class CSharpTextTemplate : Template @@ -220,7 +220,10 @@ namespace CppSharp.Generators.CSharp if (@class.IsDependent) continue; - GenerateClass(@class); + if (@class.IsInterface) + GenerateInterface(@class); + else + GenerateClass(@class); } if (context.HasFunctions) @@ -356,6 +359,57 @@ namespace CppSharp.Generators.CSharp PopBlock(NewLineKind.BeforeNextBlock); } + private void GenerateInterface(Class @class) + { + if (@class.Ignore || @class.IsIncomplete) + return; + + PushBlock(CSharpBlockKind.Interface); + GenerateDeclarationCommon(@class); + + GenerateClassProlog(@class); + + NewLine(); + WriteStartBraceIndent(); + + GenerateDeclContext(@class); + + foreach (var method in @class.Methods.Where(m => !ASTUtils.CheckIgnoreMethod(m) && + m.Access == AccessSpecifier.Public)) + { + PushBlock(CSharpBlockKind.Method); + GenerateDeclarationCommon(method); + + var functionName = GetFunctionIdentifier(method); + + Write("{0} {1}(", method.OriginalReturnType, functionName); + + Write(FormatMethodParameters(method.Parameters)); + + WriteLine(");"); + + PopBlock(NewLineKind.BeforeNextBlock); + } + foreach (var prop in @class.Properties.Where(p => !p.Ignore)) + { + PushBlock(CSharpBlockKind.Property); + var type = prop.Type; + if (prop.Parameters.Count > 0 && prop.Type.IsPointerToPrimitiveType()) + type = ((PointerType) prop.Type).Pointee; + Write("{0} {1} {{ ", type, GetPropertyName(prop)); + if (prop.HasGetter) + Write("get; "); + if (prop.HasSetter) + Write("set; "); + + WriteLine("}"); + PopBlock(NewLineKind.BeforeNextBlock); + } + + WriteCloseBraceIndent(); + PopBlock(NewLineKind.BeforeNextBlock); + } + private void GenerateValueClassFields(Class @class) { GenerateClassFields(@class, field => @@ -609,8 +663,7 @@ namespace CppSharp.Generators.CSharp if (@class.HasBaseClass) baseClass = @class.Bases[0].Class; - var hasRefBase = baseClass != null && baseClass.IsRefType - && !baseClass.Ignore; + var hasRefBase = baseClass != null && baseClass.IsRefType && !baseClass.Ignore; var hasIgnoredBase = baseClass != null && baseClass.Ignore; @@ -622,7 +675,7 @@ namespace CppSharp.Generators.CSharp if (@class.IsUnion) WriteLine("[StructLayout(LayoutKind.Explicit)]"); - Write(Helpers.GetAccess(@class)); + Write(Helpers.GetAccess(@class.Access)); Write("unsafe "); if (Driver.Options.GenerateAbstractImpls && @class.IsAbstract) @@ -631,7 +684,7 @@ namespace CppSharp.Generators.CSharp if (Options.GeneratePartialClasses) Write("partial "); - Write(@class.IsValueType ? "struct " : "class "); + Write(@class.IsInterface ? "interface " : (@class.IsValueType ? "struct " : "class ")); Write("{0}", SafeIdentifier(@class.Name)); var needsBase = @class.HasBaseClass && !@class.IsValueType @@ -643,8 +696,9 @@ namespace CppSharp.Generators.CSharp if (needsBase) { - var qualifiedBase = QualifiedIdentifier(@class.Bases[0].Class); - Write("{0}", qualifiedBase); + Write("{0}", string.Join(", ", + from @base in @class.Bases + select QualifiedIdentifier(@base.Class))); if (@class.IsRefType) Write(", "); @@ -792,16 +846,17 @@ namespace CppSharp.Generators.CSharp Type type; returnType.Type.IsPointerTo(out type); PrimitiveType primitiveType; + string internalFunction = GetFunctionNativeIdentifier(function); if (type.IsPrimitiveType(out primitiveType)) { - string internalFunction = GetFunctionNativeIdentifier(function); WriteLine("*Internal.{0}({1}, {2}) = value;", internalFunction, Helpers.InstanceIdentifier, function.Parameters[0].Name); } else { - WriteLine("*({0}.Internal*) this[{1}].{2} = *({0}.Internal*) value.{2};", - type.ToString(), function.Parameters[0].Name, Helpers.InstanceIdentifier); + WriteLine("*({0}.Internal*) Internal.{1}({2}, {3}) = *({0}.Internal*) value.{2};", + type.ToString(), internalFunction, + Helpers.InstanceIdentifier, function.Parameters[0].Name); } } @@ -929,11 +984,15 @@ namespace CppSharp.Generators.CSharp { PushBlock(CSharpBlockKind.Property); var type = prop.Type; + // if it's an indexer that returns an address use the real type because there is a setter anyway if (prop.Parameters.Count > 0 && prop.Type.IsPointerToPrimitiveType()) type = ((PointerType) prop.Type).Pointee; - WriteLine("{0} {1} {2}", - prop.Access == AccessSpecifier.Public ? "public" : "protected", - type, GetPropertyName(prop)); + + if (prop.ExplicitInterfaceImpl == null) + WriteLine("{0}{1} {2}", Helpers.GetAccess(prop.Access), + type, GetPropertyName(prop)); + else + WriteLine("{0} {1}.{2}", type, prop.ExplicitInterfaceImpl.Name, GetPropertyName(prop)); WriteStartBraceIndent(); if (prop.Field != null) @@ -1523,14 +1582,9 @@ namespace CppSharp.Generators.CSharp PushBlock(CSharpBlockKind.Method); GenerateDeclarationCommon(method); - switch (GetValidMethodAccess(method, @class)) + if (method.ExplicitInterfaceImpl == null) { - case AccessSpecifier.Public: - Write("public "); - break; - case AccessSpecifier.Protected: - Write("protected "); - break; + Write(Helpers.GetAccess(GetValidMethodAccess(method))); } if (method.IsVirtual && !method.IsOverride && @@ -1553,8 +1607,11 @@ namespace CppSharp.Generators.CSharp if (method.IsConstructor || method.IsDestructor) Write("{0}(", functionName); - else + else if (method.ExplicitInterfaceImpl == null) Write("{0} {1}(", method.OriginalReturnType, functionName); + else + Write("{0} {1}.{2}(", method.OriginalReturnType, + method.ExplicitInterfaceImpl.Name, functionName); Write(FormatMethodParameters(method.Parameters)); @@ -1617,7 +1674,7 @@ namespace CppSharp.Generators.CSharp PopBlock(NewLineKind.BeforeNextBlock); } - private static AccessSpecifier GetValidMethodAccess(Method method, Class @class) + private static AccessSpecifier GetValidMethodAccess(Method method) { switch (method.Access) { @@ -1625,7 +1682,7 @@ namespace CppSharp.Generators.CSharp return AccessSpecifier.Public; default: return method.IsOverride ? - @class.GetRootBaseMethod(method).Access : method.Access; + ((Class) method.Namespace).GetRootBaseMethod(method).Access : method.Access; } } @@ -1985,7 +2042,7 @@ namespace CppSharp.Generators.CSharp return string.Join(", ", from param in @params where param.Kind != ParameterKind.IndirectReturnType - let typeName = param.CSharpType(this.TypePrinter) + let typeName = param.CSharpType(TypePrinter) select string.Format("{0}{1} {2}", GetParameterUsage(param.Usage), typeName, SafeIdentifier(param.Name))); } @@ -2015,8 +2072,8 @@ namespace CppSharp.Generators.CSharp WriteLine("[UnmanagedFunctionPointerAttribute(CallingConvention.{0})]", Helpers.ToCSharpCallConv(functionType.CallingConvention)); TypePrinter.PushContext(CSharpTypePrinterContextKind.Native); - WriteLine("{0} unsafe {1};", - typedef.Access == AccessSpecifier.Public ? "public" : "protected", + WriteLine("{0}unsafe {1};", + Helpers.GetAccess(typedef.Access), string.Format(TypePrinter.VisitDelegate(functionType).Type, SafeIdentifier(typedef.Name))); TypePrinter.PopContext(); diff --git a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs index e0aa9103..78a6b6ea 100644 --- a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs +++ b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs @@ -349,12 +349,11 @@ namespace CppSharp.Generators.CSharp public CSharpTypePrinterResult VisitClassDecl(Class @class) { - var nestedName = GetNestedQualifiedName(@class); - - if (ContextKind == CSharpTypePrinterContextKind.Native) - return string.Format("{0}.Internal", nestedName); - - return nestedName; + if (ContextKind == CSharpTypePrinterContextKind.Native) + return string.Format("{0}.Internal", + GetNestedQualifiedName(@class.OriginalClass ?? @class)); + + return GetNestedQualifiedName(@class); } public CSharpTypePrinterResult VisitFieldDecl(Field field) diff --git a/src/Generator/Passes/CheckDuplicatedNamesPass.cs b/src/Generator/Passes/CheckDuplicatedNamesPass.cs index 1241a988..9a758100 100644 --- a/src/Generator/Passes/CheckDuplicatedNamesPass.cs +++ b/src/Generator/Passes/CheckDuplicatedNamesPass.cs @@ -31,6 +31,13 @@ namespace CppSharp.Passes return UpdateName(method); } + var property = decl as Property; + var isIndexer = property != null && property.Parameters.Count > 0; + if (isIndexer) + { + return false; + } + var count = Count++; if (count == 0) return false; @@ -93,7 +100,7 @@ namespace CppSharp.Passes public override bool VisitProperty(Property decl) { - if(!AlreadyVisited(decl)) + if(!AlreadyVisited(decl) && decl.ExplicitInterfaceImpl == null) CheckDuplicate(decl); return false; @@ -104,7 +111,7 @@ namespace CppSharp.Passes if (ASTUtils.CheckIgnoreMethod(decl)) return false; - if(!AlreadyVisited(decl)) + if (!AlreadyVisited(decl) && decl.ExplicitInterfaceImpl == null) CheckDuplicate(decl); return false; diff --git a/src/Generator/Passes/GenerateAbstractImplementationsPass.cs b/src/Generator/Passes/GenerateAbstractImplementationsPass.cs index 6f17906a..a15d2c3d 100644 --- a/src/Generator/Passes/GenerateAbstractImplementationsPass.cs +++ b/src/Generator/Passes/GenerateAbstractImplementationsPass.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Linq; using CppSharp.AST; -using CppSharp.Utils; namespace CppSharp.Passes { @@ -52,7 +51,8 @@ namespace CppSharp.Passes foreach (var abstractMethod in abstractMethods) { - internalImpl.Methods.Add(new Method(abstractMethod)); + var method = new Method(abstractMethod) { Namespace = internalImpl }; + internalImpl.Methods.Add(method); var @delegate = new TypedefDecl { Name = abstractMethod.Name + "Delegate", diff --git a/src/Generator/Passes/MultipleInheritancePass.cs b/src/Generator/Passes/MultipleInheritancePass.cs new file mode 100644 index 00000000..4651ab83 --- /dev/null +++ b/src/Generator/Passes/MultipleInheritancePass.cs @@ -0,0 +1,129 @@ +using System.Collections.Generic; +using System.Linq; +using CppSharp.AST; +using CppSharp.Generators.CSharp; + +namespace CppSharp.Passes +{ + public class MultipleInheritancePass : TranslationUnitPass + { + /// + /// Collects all interfaces in a unit to be added at the end + /// because the unit cannot be changed while it's being iterated though. + /// We also need it to check if a class already has a complementary interface + /// because different classes may have the same secondary bases. + /// + private readonly Dictionary interfaces = new Dictionary(); + + public override bool VisitTranslationUnit(TranslationUnit unit) + { + bool result = base.VisitTranslationUnit(unit); + foreach (var @interface in interfaces) + @interface.Key.Namespace.Classes.Add(@interface.Value); + interfaces.Clear(); + return result; + } + + public override bool VisitClassDecl(Class @class) + { + // skip the first base because we can inherit from one class + for (int i = 1; i < @class.Bases.Count; i++) + { + var @base = @class.Bases[i].Class; + if (@base.IsInterface) continue; + + var @interface = GetInterface(@class, @base, true); + @class.Bases[i] = new BaseClassSpecifier { Type = new TagType(@interface) }; + } + return base.VisitClassDecl(@class); + } + + private Class GetInterface(Class @class, Class @base, bool addMembers = false) + { + if (@base.CompleteDeclaration != null) + @base = (Class) @base.CompleteDeclaration; + var name = "I" + @base.Name; + if (interfaces.ContainsKey(@base)) + return interfaces[@base]; + + return @base.Namespace.Classes.FirstOrDefault(c => c.Name == name) ?? + GetNewInterface(@class, name, @base, addMembers); + } + + private Class GetNewInterface(Class @class, string name, Class @base, bool addMembers = false) + { + var @interface = new Class + { + Name = name, + Namespace = @base.Namespace, + Access = @base.Access, + Type = ClassType.Interface, + OriginalClass = @base + }; + + @interface.Bases.AddRange( + from b in @base.Bases + let i = GetInterface(@base, b.Class) + select new BaseClassSpecifier { Type = new TagType(i) }); + + @interface.Methods.AddRange(@base.Methods.Where( + m => !m.IsConstructor && !m.IsDestructor && !m.IsStatic && !m.Ignore)); + @interface.Properties.AddRange(@base.Properties.Where(p => !p.Ignore)); + + if (@interface.Bases.Count == 0) + { + Property instance = new Property(); + instance.Name = Helpers.InstanceIdentifier; + instance.QualifiedType = new QualifiedType(new BuiltinType(PrimitiveType.IntPtr)); + instance.GetMethod = new Method(); + @interface.Properties.Add(instance); + } + + @interface.Events.AddRange(@base.Events); + + if (addMembers) + { + ImplementInterfaceMethods(@class, @interface); + ImplementInterfaceProperties(@class, @interface); + } + if (@base.Bases.All(b => b.Class != @interface)) + @base.Bases.Add(new BaseClassSpecifier { Type = new TagType(@interface) }); + + interfaces.Add(@base, @interface); + return @interface; + } + + private static void ImplementInterfaceMethods(Class @class, Class @interface) + { + foreach (var method in @interface.Methods) + { + var impl = new Method(method) + { + Namespace = @class, + IsVirtual = false, + IsOverride = false + }; + var rootBaseMethod = @class.GetRootBaseMethod(method, true); + if (rootBaseMethod != null && !rootBaseMethod.Ignore) + impl.ExplicitInterfaceImpl = @interface; + @class.Methods.Add(impl); + } + foreach (var @base in @interface.Bases) + ImplementInterfaceMethods(@class, @base.Class); + } + + private static void ImplementInterfaceProperties(Class @class, Class @interface) + { + foreach (var property in @interface.Properties.Where(p => p.Name != Helpers.InstanceIdentifier)) + { + var impl = new Property(property) { Namespace = @class }; + var rootBaseProperty = @class.GetRootBaseProperty(property, true); + if (rootBaseProperty != null && !rootBaseProperty.Ignore) + impl.ExplicitInterfaceImpl = @interface; + @class.Properties.Add(impl); + } + foreach (var @base in @interface.Bases) + ImplementInterfaceProperties(@class, @base.Class); + } + } +} diff --git a/src/Generator/Passes/ParamTypeToInterfacePass.cs b/src/Generator/Passes/ParamTypeToInterfacePass.cs new file mode 100644 index 00000000..5e0e12d0 --- /dev/null +++ b/src/Generator/Passes/ParamTypeToInterfacePass.cs @@ -0,0 +1,49 @@ +using CppSharp.AST; + +namespace CppSharp.Passes +{ + public class ParamTypeToInterfacePass : TranslationUnitPass + { + public override bool VisitFunctionDecl(Function function) + { + if (function.HasIndirectReturnTypeParameter) + { + var parameter = function.Parameters.Find(p => p.Kind == ParameterKind.IndirectReturnType); + parameter.QualifiedType = GetInterfaceType(parameter.QualifiedType); + } + else + { + function.ReturnType = GetInterfaceType(function.ReturnType); + } + return base.VisitFunctionDecl(function); + } + + public override bool VisitParameterDecl(Parameter parameter) + { + parameter.QualifiedType = GetInterfaceType(parameter.QualifiedType); + return base.VisitParameterDecl(parameter); + } + + private static QualifiedType GetInterfaceType(QualifiedType type) + { + var tagType = type.Type as TagType; + if (tagType == null) + { + var pointerType = type.Type as PointerType; + if (pointerType != null) + tagType = pointerType.Pointee as TagType; + } + if (tagType != null) + { + var @class = tagType.Declaration as Class; + if (@class != null) + { + var @interface = @class.Namespace.Classes.Find(c => c.OriginalClass == @class); + if (@interface != null) + return new QualifiedType(new TagType(@interface)); + } + } + return type; + } + } +} diff --git a/tests/CSharpTemp/CSharpTemp.Tests.cs b/tests/CSharpTemp/CSharpTemp.Tests.cs index 47a60df1..449ede61 100644 --- a/tests/CSharpTemp/CSharpTemp.Tests.cs +++ b/tests/CSharpTemp/CSharpTemp.Tests.cs @@ -30,4 +30,19 @@ public class CSharpTempTests Assert.That(typeof(Foo).GetProperty("P", BindingFlags.Instance | BindingFlags.NonPublic), Is.Not.Null); } + + [Test] + public void TestMultipleInheritance() + { + Baz baz = new Baz(); + Assert.That(baz.method(), Is.EqualTo(1)); + var bar = (IBar) baz; + Assert.That(bar.method(), Is.EqualTo(2)); + Assert.That(baz[0], Is.EqualTo(50)); + bar[0] = new Foo { A = 1000 }; + Assert.That(bar[0].A, Is.EqualTo(1000)); + Assert.That(baz.farAwayFunc(), Is.EqualTo(20)); + Assert.That(baz.takesQux(baz), Is.EqualTo(20)); + Assert.That(baz.returnQux().farAwayFunc(), Is.EqualTo(20)); + } } \ No newline at end of file diff --git a/tests/CSharpTemp/CSharpTemp.cpp b/tests/CSharpTemp/CSharpTemp.cpp index 8c59df01..3a48155e 100644 --- a/tests/CSharpTemp/CSharpTemp.cpp +++ b/tests/CSharpTemp/CSharpTemp.cpp @@ -6,6 +6,11 @@ Foo::Foo() P = 50; } +int Foo::method() +{ + return 1; +} + int Foo::operator[](int i) const { return 5; @@ -26,7 +31,27 @@ const Foo& Bar::operator[](int i) const return m_foo; } +int Qux::farAwayFunc() const +{ + return 20; +} + +int Bar::method() +{ + return 2; +} + Foo& Bar::operator[](int i) { return m_foo; } + +int Baz::takesQux(const Qux& qux) +{ + return qux.farAwayFunc(); +} + +Qux Baz::returnQux() +{ + return Qux(); +} diff --git a/tests/CSharpTemp/CSharpTemp.cs b/tests/CSharpTemp/CSharpTemp.cs index 30bc0ef6..19c40511 100644 --- a/tests/CSharpTemp/CSharpTemp.cs +++ b/tests/CSharpTemp/CSharpTemp.cs @@ -10,6 +10,11 @@ namespace CppSharp.Tests { } + public override void SetupPasses(Driver driver) + { + driver.Options.GenerateInterfacesForMultipleInheritance = true; + } + static class Program { public static void Main(string[] args) diff --git a/tests/CSharpTemp/CSharpTemp.h b/tests/CSharpTemp/CSharpTemp.h index 647fff34..6453d89f 100644 --- a/tests/CSharpTemp/CSharpTemp.h +++ b/tests/CSharpTemp/CSharpTemp.h @@ -8,6 +8,7 @@ class DLL_API Foo { public: Foo(); + int method(); int operator[](int i) const; int operator[](unsigned int i); int& operator[](int i); @@ -17,12 +18,26 @@ protected: int P; }; -class DLL_API Bar +class DLL_API Qux { public: + int farAwayFunc() const; +}; + +class DLL_API Bar : public Qux +{ +public: + int method(); const Foo& operator[](int i) const; Foo& operator[](int i); private: Foo m_foo; }; + +class DLL_API Baz : public Foo, public Bar +{ +public: + int takesQux(const Qux& qux); + Qux returnQux(); +};