From 37f09c04efb6707db1b8af90eb9a5838f961ea3b Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Wed, 18 Sep 2013 16:39:07 +0300 Subject: [PATCH 01/17] Added a pass to generate interfaces with the purpose of simulating multiple inheritance. Signed-off-by: Dimitar Dobrev --- src/Generator/Driver.cs | 1 + .../Passes/MultipleInheritancePass.cs | 42 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 src/Generator/Passes/MultipleInheritancePass.cs diff --git a/src/Generator/Driver.cs b/src/Generator/Driver.cs index 92e3b6a7..afe02b60 100644 --- a/src/Generator/Driver.cs +++ b/src/Generator/Driver.cs @@ -144,6 +144,7 @@ namespace CppSharp TranslationUnitPasses.AddPass(new CheckAmbiguousFunctions()); TranslationUnitPasses.AddPass(new CheckOperatorsOverloadsPass()); TranslationUnitPasses.AddPass(new CheckVirtualOverrideReturnCovariance()); + TranslationUnitPasses.AddPass(new MultipleInheritancePass()); Generator.SetupPasses(); TranslationUnitPasses.AddPass(new FieldToPropertyPass()); diff --git a/src/Generator/Passes/MultipleInheritancePass.cs b/src/Generator/Passes/MultipleInheritancePass.cs new file mode 100644 index 00000000..07982006 --- /dev/null +++ b/src/Generator/Passes/MultipleInheritancePass.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Linq; +using CppSharp.AST; + +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. + /// + 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) + { + for (int i = 1; i < @class.Bases.Count; i++) + { + var @base = @class.Bases[i].Class; + var name = "I" + @base.Name; + if (!interfaces.ContainsKey(@base) && @base.Namespace.Classes.All(c => c.Name != name)) + { + Class @interface = new Class { Name = name, Namespace = @class.Namespace }; + @interface.IsAbstract = true; + @interface.Methods.AddRange(@class.Methods); + @interface.Properties.AddRange(@class.Properties); + @interface.Events.AddRange(@class.Events); + @interfaces.Add(@class, @interface); + } + } + return base.VisitClassDecl(@class); + } + } +} From f5304e69bb144498fb28a9f6528130980f2fd2f9 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Wed, 18 Sep 2013 21:50:57 +0300 Subject: [PATCH 02/17] Completed the support for multiple inheritance with the exception of base interfaces and the cast operator in the derived class to the second or later base. Signed-off-by: Dimitar Dobrev --- src/AST/Class.cs | 2 + src/AST/Property.cs | 24 ++++ .../Generators/CSharp/CSharpTextTemplate.cs | 116 +++++++++++++++--- .../Passes/MultipleInheritancePass.cs | 53 ++++++-- tests/CSharpTemp/CSharpTemp.Tests.cs | 8 ++ tests/CSharpTemp/CSharpTemp.cpp | 10 ++ tests/CSharpTemp/CSharpTemp.h | 7 ++ 7 files changed, 195 insertions(+), 25 deletions(-) diff --git a/src/AST/Class.cs b/src/AST/Class.cs index b1893f71..692c3037 100644 --- a/src/AST/Class.cs +++ b/src/AST/Class.cs @@ -82,6 +82,8 @@ namespace CppSharp.AST // True if class provides pure virtual methods. public bool IsAbstract; + public bool IsInterface { get; set; } + // True if the type is to be treated as a union. public bool IsUnion; diff --git a/src/AST/Property.cs b/src/AST/Property.cs index b3394aa8..cf2ebb5b 100644 --- a/src/AST/Property.cs +++ b/src/AST/Property.cs @@ -7,6 +7,30 @@ namespace CppSharp.AST /// public class Property : Declaration, ITypedDecl { + public Property() + { + } + + public Property(Property property) + : base(property) + { + QualifiedType = property.QualifiedType; + if (property.GetMethod != null) + GetMethod = new Method(property.GetMethod); + if (property.GetMethod == property.SetMethod) + { + SetMethod = GetMethod; + } + else + { + if (property.SetMethod != null) + SetMethod = new Method(property.SetMethod); + } + if (property.Field != null) + Field = property.Field; + parameters.AddRange(property.Parameters); + } + public Type Type { get { return QualifiedType.Type; } diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index d97fd17b..899feb64 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; } @@ -220,7 +219,10 @@ namespace CppSharp.Generators.CSharp if (@class.IsDependent) continue; - GenerateClass(@class); + if (@class.IsInterface) + GenerateInterface(@class); + else + GenerateClass(@class); } if (context.HasFunctions) @@ -356,6 +358,80 @@ namespace CppSharp.Generators.CSharp PopBlock(NewLineKind.BeforeNextBlock); } + private void GenerateInterface(Class @class) + { + if (@class.Ignore || @class.IsIncomplete) + return; + + PushBlock(CSharpBlockKind.Class); + GenerateDeclarationCommon(@class); + + Write(Helpers.GetAccess(@class)); + Write("unsafe "); + + if (Options.GeneratePartialClasses) + Write("partial "); + + Write("interface "); + Write("{0}", SafeIdentifier(@class.Name)); + + //var needsBase = @class.HasBaseClass && !@class.IsValueType + // && !@class.Bases[0].Class.IsValueType + // && !@class.Bases[0].Class.Ignore; + + //if (needsBase || @class.IsRefType) + // Write(" : "); + + //if (needsBase) + //{ + // var qualifiedBase = QualifiedIdentifier(@class.Bases[0].Class); + // Write("{0}", qualifiedBase); + + // if (@class.IsRefType) + // Write(", "); + //} + + NewLine(); + WriteStartBraceIndent(); + + GenerateDeclContext(@class); + + foreach (var method in @class.Methods.Where(m => !ASTUtils.CheckIgnoreMethod(m))) + { + PushBlock(CSharpBlockKind.Method); + GenerateDeclarationCommon(method); + + var functionName = GetFunctionIdentifier(method); + + Write("{0} {1}(", method.OriginalReturnType, functionName); + + Write(FormatMethodParameters(method.Parameters)); + + Write(");"); + NewLine(); + + 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 => @@ -610,7 +686,7 @@ namespace CppSharp.Generators.CSharp baseClass = @class.Bases[0].Class; var hasRefBase = baseClass != null && baseClass.IsRefType - && !baseClass.Ignore; + && !baseClass.Ignore && !baseClass.IsInterface; var hasIgnoredBase = baseClass != null && baseClass.Ignore; @@ -643,8 +719,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(", "); @@ -1360,7 +1437,8 @@ namespace CppSharp.Generators.CSharp private void GenerateDisposeMethods(Class @class) { - var hasBaseClass = @class.HasBaseClass && @class.BaseClass.IsRefType; + var hasBaseClass = @class.HasBaseClass && @class.BaseClass.IsRefType && + !@class.BaseClass.IsInterface; // Generate the IDispose Dispose() method. if (!hasBaseClass) @@ -1429,7 +1507,8 @@ namespace CppSharp.Generators.CSharp WriteLine("internal {0}(global::System.IntPtr native){1}", safeIdentifier, @class.IsValueType ? " : this()" : string.Empty); - var hasBaseClass = @class.HasBaseClass && @class.BaseClass.IsRefType; + var hasBaseClass = @class.HasBaseClass && @class.BaseClass.IsRefType && + !@class.BaseClass.IsInterface; if (hasBaseClass) WriteLineIndent(": base(native)"); @@ -1523,14 +1602,18 @@ namespace CppSharp.Generators.CSharp PushBlock(CSharpBlockKind.Method); GenerateDeclarationCommon(method); - switch (GetValidMethodAccess(method, @class)) + // check if this is an explicit interface implementation + if (!method.Name.Contains('.')) { - case AccessSpecifier.Public: - Write("public "); - break; - case AccessSpecifier.Protected: - Write("protected "); - break; + switch (GetValidMethodAccess(method, @class)) + { + case AccessSpecifier.Public: + Write("public "); + break; + case AccessSpecifier.Protected: + Write("protected "); + break; + } } if (method.IsVirtual && !method.IsOverride && @@ -1549,7 +1632,8 @@ namespace CppSharp.Generators.CSharp if (Driver.Options.GenerateAbstractImpls && method.IsPure) Write("abstract "); - var functionName = GetFunctionIdentifier(method); + var functionName = method.IsOperator ? + Operators.GetOperatorIdentifier(method.OperatorKind) : method.Name; if (method.IsConstructor || method.IsDestructor) Write("{0}(", functionName); diff --git a/src/Generator/Passes/MultipleInheritancePass.cs b/src/Generator/Passes/MultipleInheritancePass.cs index 07982006..ebc13bb4 100644 --- a/src/Generator/Passes/MultipleInheritancePass.cs +++ b/src/Generator/Passes/MultipleInheritancePass.cs @@ -25,18 +25,53 @@ namespace CppSharp.Passes for (int i = 1; i < @class.Bases.Count; i++) { var @base = @class.Bases[i].Class; + if (@base.IsInterface) continue; + + if (@base.CompleteDeclaration != null) + @base = (Class) @base.CompleteDeclaration; var name = "I" + @base.Name; - if (!interfaces.ContainsKey(@base) && @base.Namespace.Classes.All(c => c.Name != name)) - { - Class @interface = new Class { Name = name, Namespace = @class.Namespace }; - @interface.IsAbstract = true; - @interface.Methods.AddRange(@class.Methods); - @interface.Properties.AddRange(@class.Properties); - @interface.Events.AddRange(@class.Events); - @interfaces.Add(@class, @interface); - } + var @interface = (interfaces.ContainsKey(@base) + ? interfaces[@base] + : @base.Namespace.Classes.FirstOrDefault(c => c.Name == name)) ?? + GetNewInterface(@class, name, @base); + @class.Bases[i] = new BaseClassSpecifier { Type = new TagType(@interface) }; } return base.VisitClassDecl(@class); } + + private Class GetNewInterface(Class @class, string name, Class @base) + { + var @interface = new Class + { + Name = name, + Namespace = @base.Namespace, + Access = @base.Access, + IsInterface = true + }; + @interface.Methods.AddRange(@base.Methods.Where( + m => !m.IsConstructor && !m.IsDestructor && !m.IsStatic && !m.Ignore)); + foreach (var method in @interface.Methods) + { + var impl = new Method(method) + { + Namespace = @class, + IsVirtual = false, + IsOverride = false + }; + var rootBaseMethod = @class.GetRootBaseMethod(method); + if (rootBaseMethod != null && !rootBaseMethod.Ignore) + impl.Name = @interface.Name + "." + impl.Name; + @class.Methods.Add(impl); + } + @interface.Properties.AddRange(@base.Properties.Where(p => !p.Ignore)); + @class.Properties.AddRange( + from property in @interface.Properties + select new Property(property) { Namespace = @class }); + @interface.Events.AddRange(@base.Events); + if (@base.Bases.All(b => b.Class != @interface)) + @base.Bases.Add(new BaseClassSpecifier { Type = new TagType(@interface) }); + interfaces.Add(@base, @interface); + return @interface; + } } } diff --git a/tests/CSharpTemp/CSharpTemp.Tests.cs b/tests/CSharpTemp/CSharpTemp.Tests.cs index 47a60df1..18973418 100644 --- a/tests/CSharpTemp/CSharpTemp.Tests.cs +++ b/tests/CSharpTemp/CSharpTemp.Tests.cs @@ -30,4 +30,12 @@ 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)); + Assert.That(((IBar) baz).method(), Is.EqualTo(2)); + } } \ No newline at end of file diff --git a/tests/CSharpTemp/CSharpTemp.cpp b/tests/CSharpTemp/CSharpTemp.cpp index 8c59df01..b0882e34 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,6 +31,11 @@ const Foo& Bar::operator[](int i) const return m_foo; } +int Bar::method() +{ + return 2; +} + Foo& Bar::operator[](int i) { return m_foo; diff --git a/tests/CSharpTemp/CSharpTemp.h b/tests/CSharpTemp/CSharpTemp.h index 647fff34..001c658c 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); @@ -20,9 +21,15 @@ protected: class DLL_API Bar { 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 +{ + +}; From 068cf51165685122db3baac1d81fddbd019d30b6 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Wed, 18 Sep 2013 23:32:35 +0300 Subject: [PATCH 03/17] Added explicit implementation of interface properties (when necessary). Signed-off-by: Dimitar Dobrev --- src/AST/Class.cs | 15 +++++++++++ .../Generators/CSharp/CSharpTextTemplate.cs | 25 +++++++++++++------ .../Passes/MultipleInheritancePass.cs | 14 ++++++++--- tests/CSharpTemp/CSharpTemp.Tests.cs | 6 ++++- 4 files changed, 48 insertions(+), 12 deletions(-) diff --git a/src/AST/Class.cs b/src/AST/Class.cs index 692c3037..09491034 100644 --- a/src/AST/Class.cs +++ b/src/AST/Class.cs @@ -219,6 +219,21 @@ namespace CppSharp.AST select rootBaseMethod ?? baseMethod).FirstOrDefault(); } + public Property GetRootBaseProperty(Property @override) + { + return (from @base in Bases + let baseMethod = ( + 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 rootBaseMethod = @base.Class.GetRootBaseProperty(@override) + select rootBaseMethod ?? baseMethod).FirstOrDefault(); + } + public override T Visit(IDeclVisitor visitor) { return visitor.VisitClassDecl(this); diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index 899feb64..a7588177 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -869,16 +869,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); } } @@ -1008,9 +1009,13 @@ namespace CppSharp.Generators.CSharp var type = prop.Type; 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)); + // explicit impl + if (prop.Name.Contains('.')) + WriteLine("{0} {1}", type, GetPropertyName(prop)); + else + WriteLine("{0} {1} {2}", + prop.Access == AccessSpecifier.Public ? "public" : "protected", + type, GetPropertyName(prop)); WriteStartBraceIndent(); if (prop.Field != null) @@ -1037,8 +1042,12 @@ namespace CppSharp.Generators.CSharp private string GetPropertyName(Property prop) { - return prop.Parameters.Count == 0 ? SafeIdentifier(prop.Name) - : string.Format("this[{0}]", FormatMethodParameters(prop.Parameters)); + // check for explicit interface implementations + var indexOfDot = prop.Name.IndexOf('.'); + string name = prop.Name.Substring(prop.Name.IndexOf('.') + 1); + return prop.Name.Substring(0, indexOfDot + 1) + + (prop.Parameters.Count == 0 ? SafeIdentifier(name) + : string.Format("this[{0}]", FormatMethodParameters(prop.Parameters))); } private void GenerateVariable(Class @class, Type type, Variable variable) diff --git a/src/Generator/Passes/MultipleInheritancePass.cs b/src/Generator/Passes/MultipleInheritancePass.cs index ebc13bb4..d6ca1166 100644 --- a/src/Generator/Passes/MultipleInheritancePass.cs +++ b/src/Generator/Passes/MultipleInheritancePass.cs @@ -64,9 +64,17 @@ namespace CppSharp.Passes @class.Methods.Add(impl); } @interface.Properties.AddRange(@base.Properties.Where(p => !p.Ignore)); - @class.Properties.AddRange( - from property in @interface.Properties - select new Property(property) { Namespace = @class }); + foreach (var property in @interface.Properties) + { + var impl = new Property(property) + { + Namespace = @class + }; + var rootBaseProperty = @class.GetRootBaseProperty(property); + if (rootBaseProperty != null && !rootBaseProperty.Ignore) + impl.Name = @interface.Name + "." + impl.Name; + @class.Properties.Add(impl); + } @interface.Events.AddRange(@base.Events); if (@base.Bases.All(b => b.Class != @interface)) @base.Bases.Add(new BaseClassSpecifier { Type = new TagType(@interface) }); diff --git a/tests/CSharpTemp/CSharpTemp.Tests.cs b/tests/CSharpTemp/CSharpTemp.Tests.cs index 18973418..a31dbde8 100644 --- a/tests/CSharpTemp/CSharpTemp.Tests.cs +++ b/tests/CSharpTemp/CSharpTemp.Tests.cs @@ -36,6 +36,10 @@ public class CSharpTempTests { Baz baz = new Baz(); Assert.That(baz.method(), Is.EqualTo(1)); - Assert.That(((IBar) baz).method(), Is.EqualTo(2)); + 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)); } } \ No newline at end of file From 5e9de92b7827e4236eec97597e653907048b44ed Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Thu, 19 Sep 2013 15:29:50 +0300 Subject: [PATCH 04/17] Fixed a bug in the getting of the root base method: the bases after the first one were ignored. Signed-off-by: Dimitar Dobrev --- src/AST/Class.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/AST/Class.cs b/src/AST/Class.cs index 09491034..4b040991 100644 --- a/src/AST/Class.cs +++ b/src/AST/Class.cs @@ -215,14 +215,15 @@ 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 + select rootBaseMethod).FirstOrDefault(); } public Property GetRootBaseProperty(Property @override) { return (from @base in Bases - let baseMethod = ( + let baseProperty = ( from property in @base.Class.Properties where property.Name == @override.Name && @@ -230,8 +231,9 @@ namespace CppSharp.AST property.Parameters.SequenceEqual(@override.Parameters, new ParameterTypeComparer()) select property).FirstOrDefault() - let rootBaseMethod = @base.Class.GetRootBaseProperty(@override) - select rootBaseMethod ?? baseMethod).FirstOrDefault(); + let rootBaseProperty = @base.Class.GetRootBaseProperty(@override) ?? baseProperty + where rootBaseProperty != null + select rootBaseProperty).FirstOrDefault(); } public override T Visit(IDeclVisitor visitor) From c9e5c0d505d710267e83b4afd6324ff4af10d706 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Thu, 19 Sep 2013 15:59:24 +0300 Subject: [PATCH 05/17] Completed the parallel hierarchy of interfaces so that a derived class can access the members of all of its bases. Signed-off-by: Dimitar Dobrev --- src/AST/Class.cs | 10 +-- .../Generators/CSharp/CSharpTextTemplate.cs | 21 ++---- .../Passes/MultipleInheritancePass.cs | 64 +++++++++++++------ tests/CSharpTemp/CSharpTemp.cpp | 5 ++ tests/CSharpTemp/CSharpTemp.h | 8 ++- 5 files changed, 68 insertions(+), 40 deletions(-) diff --git a/src/AST/Class.cs b/src/AST/Class.cs index 4b040991..42a1aafa 100644 --- a/src/AST/Class.cs +++ b/src/AST/Class.cs @@ -203,9 +203,10 @@ namespace CppSharp.AST } } - public Method GetRootBaseMethod(Method @override) + public Method GetRootBaseMethod(Method @override, bool onlyFirstBase = false) { return (from @base in Bases + where !@base.Class.IsInterface let baseMethod = ( from method in @base.Class.Methods where @@ -216,13 +217,14 @@ namespace CppSharp.AST new ParameterTypeComparer()) select method).FirstOrDefault() let rootBaseMethod = @base.Class.GetRootBaseMethod(@override) ?? baseMethod - where rootBaseMethod != null + where rootBaseMethod != null || onlyFirstBase select rootBaseMethod).FirstOrDefault(); } - public Property GetRootBaseProperty(Property @override) + public Property GetRootBaseProperty(Property @override, bool onlyFirstBase = false) { return (from @base in Bases + where !@base.Class.IsInterface let baseProperty = ( from property in @base.Class.Properties where @@ -232,7 +234,7 @@ namespace CppSharp.AST new ParameterTypeComparer()) select property).FirstOrDefault() let rootBaseProperty = @base.Class.GetRootBaseProperty(@override) ?? baseProperty - where rootBaseProperty != null + where rootBaseProperty != null || onlyFirstBase select rootBaseProperty).FirstOrDefault(); } diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index a7588177..3a9c234e 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -375,21 +375,12 @@ namespace CppSharp.Generators.CSharp Write("interface "); Write("{0}", SafeIdentifier(@class.Name)); - //var needsBase = @class.HasBaseClass && !@class.IsValueType - // && !@class.Bases[0].Class.IsValueType - // && !@class.Bases[0].Class.Ignore; - - //if (needsBase || @class.IsRefType) - // Write(" : "); - - //if (needsBase) - //{ - // var qualifiedBase = QualifiedIdentifier(@class.Bases[0].Class); - // Write("{0}", qualifiedBase); - - // if (@class.IsRefType) - // Write(", "); - //} + if (@class.HasBase) + { + Write(" : {0}", string.Join(", ", + from @base in @class.Bases + select QualifiedIdentifier(@base.Class))); + } NewLine(); WriteStartBraceIndent(); diff --git a/src/Generator/Passes/MultipleInheritancePass.cs b/src/Generator/Passes/MultipleInheritancePass.cs index d6ca1166..0ffe9570 100644 --- a/src/Generator/Passes/MultipleInheritancePass.cs +++ b/src/Generator/Passes/MultipleInheritancePass.cs @@ -27,19 +27,25 @@ namespace CppSharp.Passes var @base = @class.Bases[i].Class; if (@base.IsInterface) continue; - if (@base.CompleteDeclaration != null) - @base = (Class) @base.CompleteDeclaration; - var name = "I" + @base.Name; - var @interface = (interfaces.ContainsKey(@base) - ? interfaces[@base] - : @base.Namespace.Classes.FirstOrDefault(c => c.Name == name)) ?? - GetNewInterface(@class, name, @base); + var @interface = GetInterface(@class, @base, true); @class.Bases[i] = new BaseClassSpecifier { Type = new TagType(@interface) }; } return base.VisitClassDecl(@class); } - private Class GetNewInterface(Class @class, string name, Class @base) + private Class GetInterface(Class @class, Class @base, bool addMembers = false) + { + if (@base.CompleteDeclaration != null) + @base = (Class) @base.CompleteDeclaration; + var name = "I" + @base.Name; + var @interface = (this.interfaces.ContainsKey(@base) + ? this.interfaces[@base] + : @base.Namespace.Classes.FirstOrDefault(c => c.Name == name)) ?? + GetNewInterface(@class, name, @base, addMembers); + return @interface; + } + + private Class GetNewInterface(Class @class, string name, Class @base, bool addMembers = false) { var @interface = new Class { @@ -48,8 +54,27 @@ namespace CppSharp.Passes Access = @base.Access, IsInterface = true }; + @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)); + @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) @@ -58,28 +83,27 @@ namespace CppSharp.Passes IsVirtual = false, IsOverride = false }; - var rootBaseMethod = @class.GetRootBaseMethod(method); + var rootBaseMethod = @class.GetRootBaseMethod(method, true); if (rootBaseMethod != null && !rootBaseMethod.Ignore) impl.Name = @interface.Name + "." + impl.Name; @class.Methods.Add(impl); } - @interface.Properties.AddRange(@base.Properties.Where(p => !p.Ignore)); + foreach (var @base in @interface.Bases) + ImplementInterfaceMethods(@class, @base.Class); + } + + private static void ImplementInterfaceProperties(Class @class, Class @interface) + { foreach (var property in @interface.Properties) { - var impl = new Property(property) - { - Namespace = @class - }; - var rootBaseProperty = @class.GetRootBaseProperty(property); + var impl = new Property(property) { Namespace = @class }; + var rootBaseProperty = @class.GetRootBaseProperty(property, true); if (rootBaseProperty != null && !rootBaseProperty.Ignore) impl.Name = @interface.Name + "." + impl.Name; @class.Properties.Add(impl); } - @interface.Events.AddRange(@base.Events); - if (@base.Bases.All(b => b.Class != @interface)) - @base.Bases.Add(new BaseClassSpecifier { Type = new TagType(@interface) }); - interfaces.Add(@base, @interface); - return @interface; + foreach (var @base in @interface.Bases) + ImplementInterfaceProperties(@class, @base.Class); } } } diff --git a/tests/CSharpTemp/CSharpTemp.cpp b/tests/CSharpTemp/CSharpTemp.cpp index b0882e34..250ba462 100644 --- a/tests/CSharpTemp/CSharpTemp.cpp +++ b/tests/CSharpTemp/CSharpTemp.cpp @@ -31,6 +31,11 @@ const Foo& Bar::operator[](int i) const return m_foo; } +int Qux::farAwayFunc() +{ + return 20; +} + int Bar::method() { return 2; diff --git a/tests/CSharpTemp/CSharpTemp.h b/tests/CSharpTemp/CSharpTemp.h index 001c658c..f23fd473 100644 --- a/tests/CSharpTemp/CSharpTemp.h +++ b/tests/CSharpTemp/CSharpTemp.h @@ -18,7 +18,13 @@ protected: int P; }; -class DLL_API Bar +class DLL_API Qux +{ +public: + int farAwayFunc(); +}; + +class DLL_API Bar : public Qux { public: int method(); From f07ddf79fc0a31cab151ff032b7af9466a803c76 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Thu, 19 Sep 2013 16:12:45 +0300 Subject: [PATCH 06/17] Replaced the vague checks for explicit interface impls with a specific property. Signed-off-by: Dimitar Dobrev --- src/AST/Method.cs | 2 ++ src/AST/Property.cs | 2 ++ .../Generators/CSharp/CSharpTextTemplate.cs | 24 +++++++++---------- .../Passes/MultipleInheritancePass.cs | 4 ++-- tests/CSharpTemp/CSharpTemp.Tests.cs | 1 + 5 files changed, 18 insertions(+), 15 deletions(-) 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 cf2ebb5b..9b0bdb90 100644 --- a/src/AST/Property.cs +++ b/src/AST/Property.cs @@ -62,6 +62,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/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index 3a9c234e..b12af4bd 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -1000,13 +1000,13 @@ namespace CppSharp.Generators.CSharp var type = prop.Type; if (prop.Parameters.Count > 0 && prop.Type.IsPointerToPrimitiveType()) type = ((PointerType) prop.Type).Pointee; - // explicit impl - if (prop.Name.Contains('.')) - WriteLine("{0} {1}", type, GetPropertyName(prop)); - else + + if (prop.ExplicitInterfaceImpl == null) WriteLine("{0} {1} {2}", prop.Access == AccessSpecifier.Public ? "public" : "protected", type, GetPropertyName(prop)); + else + WriteLine("{0} {1}.{2}", type, prop.ExplicitInterfaceImpl.Name, GetPropertyName(prop)); WriteStartBraceIndent(); if (prop.Field != null) @@ -1033,12 +1033,8 @@ namespace CppSharp.Generators.CSharp private string GetPropertyName(Property prop) { - // check for explicit interface implementations - var indexOfDot = prop.Name.IndexOf('.'); - string name = prop.Name.Substring(prop.Name.IndexOf('.') + 1); - return prop.Name.Substring(0, indexOfDot + 1) + - (prop.Parameters.Count == 0 ? SafeIdentifier(name) - : string.Format("this[{0}]", FormatMethodParameters(prop.Parameters))); + return prop.Parameters.Count == 0 ? SafeIdentifier(prop.Name) + : string.Format("this[{0}]", this.FormatMethodParameters(prop.Parameters)); } private void GenerateVariable(Class @class, Type type, Variable variable) @@ -1602,8 +1598,7 @@ namespace CppSharp.Generators.CSharp PushBlock(CSharpBlockKind.Method); GenerateDeclarationCommon(method); - // check if this is an explicit interface implementation - if (!method.Name.Contains('.')) + if (method.ExplicitInterfaceImpl == null) { switch (GetValidMethodAccess(method, @class)) { @@ -1637,8 +1632,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)); diff --git a/src/Generator/Passes/MultipleInheritancePass.cs b/src/Generator/Passes/MultipleInheritancePass.cs index 0ffe9570..082bf41d 100644 --- a/src/Generator/Passes/MultipleInheritancePass.cs +++ b/src/Generator/Passes/MultipleInheritancePass.cs @@ -85,7 +85,7 @@ namespace CppSharp.Passes }; var rootBaseMethod = @class.GetRootBaseMethod(method, true); if (rootBaseMethod != null && !rootBaseMethod.Ignore) - impl.Name = @interface.Name + "." + impl.Name; + impl.ExplicitInterfaceImpl = @interface; @class.Methods.Add(impl); } foreach (var @base in @interface.Bases) @@ -99,7 +99,7 @@ namespace CppSharp.Passes var impl = new Property(property) { Namespace = @class }; var rootBaseProperty = @class.GetRootBaseProperty(property, true); if (rootBaseProperty != null && !rootBaseProperty.Ignore) - impl.Name = @interface.Name + "." + impl.Name; + impl.ExplicitInterfaceImpl = @interface; @class.Properties.Add(impl); } foreach (var @base in @interface.Bases) diff --git a/tests/CSharpTemp/CSharpTemp.Tests.cs b/tests/CSharpTemp/CSharpTemp.Tests.cs index a31dbde8..b3a1c7cd 100644 --- a/tests/CSharpTemp/CSharpTemp.Tests.cs +++ b/tests/CSharpTemp/CSharpTemp.Tests.cs @@ -41,5 +41,6 @@ public class CSharpTempTests 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)); } } \ No newline at end of file From ffbc5fbf156771967a6896140399fd8811035e6a Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Thu, 19 Sep 2013 18:35:31 +0300 Subject: [PATCH 07/17] Fixed the check for duplicates to account for explicit impls. Fixed the getting of a root base method and property to consider the parameter for all bases. Signed-off-by: Dimitar Dobrev --- src/AST/Class.cs | 4 ++-- src/Generator/Passes/CheckDuplicatedNamesPass.cs | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/AST/Class.cs b/src/AST/Class.cs index 42a1aafa..645b1363 100644 --- a/src/AST/Class.cs +++ b/src/AST/Class.cs @@ -206,7 +206,7 @@ namespace CppSharp.AST public Method GetRootBaseMethod(Method @override, bool onlyFirstBase = false) { return (from @base in Bases - where !@base.Class.IsInterface + where !onlyFirstBase || !@base.Class.IsInterface let baseMethod = ( from method in @base.Class.Methods where @@ -224,7 +224,7 @@ namespace CppSharp.AST public Property GetRootBaseProperty(Property @override, bool onlyFirstBase = false) { return (from @base in Bases - where !@base.Class.IsInterface + where !onlyFirstBase || !@base.Class.IsInterface let baseProperty = ( from property in @base.Class.Properties where diff --git a/src/Generator/Passes/CheckDuplicatedNamesPass.cs b/src/Generator/Passes/CheckDuplicatedNamesPass.cs index 1241a988..fcbf9fac 100644 --- a/src/Generator/Passes/CheckDuplicatedNamesPass.cs +++ b/src/Generator/Passes/CheckDuplicatedNamesPass.cs @@ -31,6 +31,12 @@ namespace CppSharp.Passes return UpdateName(method); } + var property = decl as Property; + if (property != null && property.Parameters.Count > 0) + { + return false; + } + var count = Count++; if (count == 0) return false; @@ -93,7 +99,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 +110,7 @@ namespace CppSharp.Passes if (ASTUtils.CheckIgnoreMethod(decl)) return false; - if(!AlreadyVisited(decl)) + if (!AlreadyVisited(decl) && decl.ExplicitInterfaceImpl == null) CheckDuplicate(decl); return false; From 6ef578cd6a961fb75c3f461c12244b92480928b7 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Thu, 19 Sep 2013 19:39:36 +0300 Subject: [PATCH 08/17] Removed the generation of protected members of interfaces. Signed-off-by: Dimitar Dobrev --- src/Generator/Driver.cs | 2 +- src/Generator/Generators/CSharp/CSharpTextTemplate.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Generator/Driver.cs b/src/Generator/Driver.cs index afe02b60..c16252d1 100644 --- a/src/Generator/Driver.cs +++ b/src/Generator/Driver.cs @@ -144,7 +144,6 @@ namespace CppSharp TranslationUnitPasses.AddPass(new CheckAmbiguousFunctions()); TranslationUnitPasses.AddPass(new CheckOperatorsOverloadsPass()); TranslationUnitPasses.AddPass(new CheckVirtualOverrideReturnCovariance()); - TranslationUnitPasses.AddPass(new MultipleInheritancePass()); Generator.SetupPasses(); TranslationUnitPasses.AddPass(new FieldToPropertyPass()); @@ -154,6 +153,7 @@ namespace CppSharp TranslationUnitPasses.AddPass(new CheckDuplicatedNamesPass()); if (Options.GenerateAbstractImpls) TranslationUnitPasses.AddPass(new GenerateAbstractImplementationsPass()); + TranslationUnitPasses.AddPass(new MultipleInheritancePass()); } public void ProcessCode() diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index b12af4bd..57c6bb7a 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -387,7 +387,8 @@ namespace CppSharp.Generators.CSharp GenerateDeclContext(@class); - foreach (var method in @class.Methods.Where(m => !ASTUtils.CheckIgnoreMethod(m))) + foreach (var method in @class.Methods.Where(m => !ASTUtils.CheckIgnoreMethod(m) && + m.Access == AccessSpecifier.Public)) { PushBlock(CSharpBlockKind.Method); GenerateDeclarationCommon(method); @@ -1034,7 +1035,7 @@ namespace CppSharp.Generators.CSharp private string GetPropertyName(Property prop) { return prop.Parameters.Count == 0 ? SafeIdentifier(prop.Name) - : string.Format("this[{0}]", this.FormatMethodParameters(prop.Parameters)); + : string.Format("this[{0}]", FormatMethodParameters(prop.Parameters)); } private void GenerateVariable(Class @class, Type type, Variable variable) @@ -1627,8 +1628,7 @@ namespace CppSharp.Generators.CSharp if (Driver.Options.GenerateAbstractImpls && method.IsPure) Write("abstract "); - var functionName = method.IsOperator ? - Operators.GetOperatorIdentifier(method.OperatorKind) : method.Name; + var functionName = GetFunctionIdentifier(method); if (method.IsConstructor || method.IsDestructor) Write("{0}(", functionName); From d7a91d1719a6fef265074c96a0d43d71e715cd5a Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Thu, 19 Sep 2013 20:14:05 +0300 Subject: [PATCH 09/17] Added a fake option for multiple inheritance so that the build stays healthy. Signed-off-by: Dimitar Dobrev --- src/Generator/Driver.cs | 4 +++- tests/CSharpTemp/CSharpTemp.cs | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Generator/Driver.cs b/src/Generator/Driver.cs index c16252d1..6490f98f 100644 --- a/src/Generator/Driver.cs +++ b/src/Generator/Driver.cs @@ -153,7 +153,8 @@ namespace CppSharp TranslationUnitPasses.AddPass(new CheckDuplicatedNamesPass()); if (Options.GenerateAbstractImpls) TranslationUnitPasses.AddPass(new GenerateAbstractImplementationsPass()); - TranslationUnitPasses.AddPass(new MultipleInheritancePass()); + if (Options.GenerateInterfacesForMultipleInheritance) + TranslationUnitPasses.AddPass(new MultipleInheritancePass()); } public void ProcessCode() @@ -272,6 +273,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/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) From 3a9eef1e80550ca8a0e14829c5868f137fa3a963 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Fri, 20 Sep 2013 17:00:58 +0300 Subject: [PATCH 10/17] Added a new pass converting all parameter types to their respective interfaces, if any. Signed-off-by: Dimitar Dobrev --- src/Generator/Driver.cs | 3 ++ .../Generators/CSharp/CSharpMarshal.cs | 6 ++-- .../Generators/CSharp/CSharpTextTemplate.cs | 2 +- .../Generators/CSharp/CSharpTypePrinter.cs | 12 +++++--- .../Passes/MultipleInheritancePass.cs | 11 ++++++- .../Passes/ParamTypeToInterfacePass.cs | 29 +++++++++++++++++++ tests/CSharpTemp/CSharpTemp.Tests.cs | 1 + tests/CSharpTemp/CSharpTemp.cpp | 7 ++++- tests/CSharpTemp/CSharpTemp.h | 5 ++-- 9 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 src/Generator/Passes/ParamTypeToInterfacePass.cs diff --git a/src/Generator/Driver.cs b/src/Generator/Driver.cs index 6490f98f..e9bb04f3 100644 --- a/src/Generator/Driver.cs +++ b/src/Generator/Driver.cs @@ -154,7 +154,10 @@ namespace CppSharp if (Options.GenerateAbstractImpls) TranslationUnitPasses.AddPass(new GenerateAbstractImplementationsPass()); if (Options.GenerateInterfacesForMultipleInheritance) + { TranslationUnitPasses.AddPass(new MultipleInheritancePass()); + TranslationUnitPasses.AddPass(new ParamTypeToInterfacePass()); + } } public void ProcessCode() diff --git a/src/Generator/Generators/CSharp/CSharpMarshal.cs b/src/Generator/Generators/CSharp/CSharpMarshal.cs index 397d6791..6f766a58 100644 --- a/src/Generator/Generators/CSharp/CSharpMarshal.cs +++ b/src/Generator/Generators/CSharp/CSharpMarshal.cs @@ -541,9 +541,11 @@ namespace CppSharp.Generators.CSharp return; } - Context.Return.Write("*({0}.Internal*){1}.{2}", CSharpMarshalNativeToManagedPrinter.QualifiedIdentifier(@class), + var qualifiedIdentifier = CSharpMarshalNativeToManagedPrinter.QualifiedIdentifier(@class); + if (@class.IsInterface) + qualifiedIdentifier = qualifiedIdentifier.Substring(1); + 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 57c6bb7a..c60bcbdc 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -2067,7 +2067,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))); } diff --git a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs index e0aa9103..4dda213d 100644 --- a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs +++ b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs @@ -349,10 +349,14 @@ namespace CppSharp.Generators.CSharp public CSharpTypePrinterResult VisitClassDecl(Class @class) { - var nestedName = GetNestedQualifiedName(@class); - - if (ContextKind == CSharpTypePrinterContextKind.Native) - return string.Format("{0}.Internal", nestedName); + var nestedName = GetNestedQualifiedName(@class); + + if (ContextKind == CSharpTypePrinterContextKind.Native) + { + if (@class.IsInterface) + nestedName = nestedName.Substring(1); + return string.Format("{0}.Internal", nestedName); + } return nestedName; } diff --git a/src/Generator/Passes/MultipleInheritancePass.cs b/src/Generator/Passes/MultipleInheritancePass.cs index 082bf41d..aedab7a2 100644 --- a/src/Generator/Passes/MultipleInheritancePass.cs +++ b/src/Generator/Passes/MultipleInheritancePass.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using CppSharp.AST; +using CppSharp.Generators.CSharp; namespace CppSharp.Passes { @@ -61,6 +62,14 @@ namespace CppSharp.Passes @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) { @@ -94,7 +103,7 @@ namespace CppSharp.Passes private static void ImplementInterfaceProperties(Class @class, Class @interface) { - foreach (var property in @interface.Properties) + 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); diff --git a/src/Generator/Passes/ParamTypeToInterfacePass.cs b/src/Generator/Passes/ParamTypeToInterfacePass.cs new file mode 100644 index 00000000..b8fd0e79 --- /dev/null +++ b/src/Generator/Passes/ParamTypeToInterfacePass.cs @@ -0,0 +1,29 @@ +using CppSharp.AST; + +namespace CppSharp.Passes +{ + public class ParamTypeToInterfacePass : TranslationUnitPass + { + public override bool VisitParameterDecl(Parameter parameter) + { + var tagType = parameter.QualifiedType.Type as TagType; + if (tagType == null) + { + var pointerType = parameter.QualifiedType.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.FindClass("I" + @class.Name); + if (@interface != null) + parameter.QualifiedType = new QualifiedType(new TagType(@interface)); + } + } + return base.VisitParameterDecl(parameter); + } + } +} diff --git a/tests/CSharpTemp/CSharpTemp.Tests.cs b/tests/CSharpTemp/CSharpTemp.Tests.cs index b3a1c7cd..8e05e774 100644 --- a/tests/CSharpTemp/CSharpTemp.Tests.cs +++ b/tests/CSharpTemp/CSharpTemp.Tests.cs @@ -42,5 +42,6 @@ public class CSharpTempTests 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)); } } \ No newline at end of file diff --git a/tests/CSharpTemp/CSharpTemp.cpp b/tests/CSharpTemp/CSharpTemp.cpp index 250ba462..01826516 100644 --- a/tests/CSharpTemp/CSharpTemp.cpp +++ b/tests/CSharpTemp/CSharpTemp.cpp @@ -31,7 +31,7 @@ const Foo& Bar::operator[](int i) const return m_foo; } -int Qux::farAwayFunc() +int Qux::farAwayFunc() const { return 20; } @@ -45,3 +45,8 @@ Foo& Bar::operator[](int i) { return m_foo; } + +int Baz::takesQux(const Qux& qux) +{ + return qux.farAwayFunc(); +} diff --git a/tests/CSharpTemp/CSharpTemp.h b/tests/CSharpTemp/CSharpTemp.h index f23fd473..608e7899 100644 --- a/tests/CSharpTemp/CSharpTemp.h +++ b/tests/CSharpTemp/CSharpTemp.h @@ -21,7 +21,7 @@ protected: class DLL_API Qux { public: - int farAwayFunc(); + int farAwayFunc() const; }; class DLL_API Bar : public Qux @@ -37,5 +37,6 @@ private: class DLL_API Baz : public Foo, public Bar { - +public: + int takesQux(const Qux& qux); }; From c5382ee66697a2df4385f8ce710621a321ed365b Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Fri, 20 Sep 2013 18:21:33 +0300 Subject: [PATCH 11/17] Changed a bit the method for a class definition to be able to reuse it for interfaces. Signed-off-by: Dimitar Dobrev --- .../Generators/CSharp/CSharpTextTemplate.cs | 26 +++++-------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index c60bcbdc..486994b5 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -366,21 +366,7 @@ namespace CppSharp.Generators.CSharp PushBlock(CSharpBlockKind.Class); GenerateDeclarationCommon(@class); - Write(Helpers.GetAccess(@class)); - Write("unsafe "); - - if (Options.GeneratePartialClasses) - Write("partial "); - - Write("interface "); - Write("{0}", SafeIdentifier(@class.Name)); - - if (@class.HasBase) - { - Write(" : {0}", string.Join(", ", - from @base in @class.Bases - select QualifiedIdentifier(@base.Class))); - } + GenerateClassProlog(@class); NewLine(); WriteStartBraceIndent(); @@ -699,14 +685,16 @@ 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 && !@class.Bases[0].Class.IsValueType && !@class.Bases[0].Class.Ignore; - if (needsBase || @class.IsRefType) + var isRefClass = @class.IsRefType && !@class.IsInterface; + + if (needsBase || isRefClass) Write(" : "); if (needsBase) @@ -715,11 +703,11 @@ namespace CppSharp.Generators.CSharp from @base in @class.Bases select QualifiedIdentifier(@base.Class))); - if (@class.IsRefType) + if (isRefClass) Write(", "); } - if (@class.IsRefType) + if (isRefClass) Write("IDisposable"); } From de9f1056bcd038865e3beb8ec70555b047c15a9b Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Fri, 20 Sep 2013 18:30:15 +0300 Subject: [PATCH 12/17] Changed the copy constructor of properties to make a shallow copy. Signed-off-by: Dimitar Dobrev --- src/AST/Property.cs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/AST/Property.cs b/src/AST/Property.cs index 9b0bdb90..50a34440 100644 --- a/src/AST/Property.cs +++ b/src/AST/Property.cs @@ -15,19 +15,9 @@ namespace CppSharp.AST : base(property) { QualifiedType = property.QualifiedType; - if (property.GetMethod != null) - GetMethod = new Method(property.GetMethod); - if (property.GetMethod == property.SetMethod) - { - SetMethod = GetMethod; - } - else - { - if (property.SetMethod != null) - SetMethod = new Method(property.SetMethod); - } - if (property.Field != null) - Field = property.Field; + GetMethod = property.GetMethod; + SetMethod = property.SetMethod; + Field = property.Field; parameters.AddRange(property.Parameters); } From ef53ca17d33e803e4b6b301f86b2988b57c41f20 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Fri, 20 Sep 2013 18:40:29 +0300 Subject: [PATCH 13/17] Added a block kind for an interface. Signed-off-by: Dimitar Dobrev --- src/Generator/Generators/CSharp/CSharpTextTemplate.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index 486994b5..4f0c7b3c 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -92,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 @@ -363,7 +364,7 @@ namespace CppSharp.Generators.CSharp if (@class.Ignore || @class.IsIncomplete) return; - PushBlock(CSharpBlockKind.Class); + PushBlock(CSharpBlockKind.Interface); GenerateDeclarationCommon(@class); GenerateClassProlog(@class); @@ -385,8 +386,7 @@ namespace CppSharp.Generators.CSharp Write(FormatMethodParameters(method.Parameters)); - Write(");"); - NewLine(); + WriteLine(");"); PopBlock(NewLineKind.BeforeNextBlock); } @@ -987,6 +987,7 @@ 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; From c45c015a757b0c7935aba513ce216e1d62ee11ce Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Fri, 20 Sep 2013 19:15:10 +0300 Subject: [PATCH 14/17] Reused the method for generating a string representation of an access qualifier. Signed-off-by: Dimitar Dobrev --- .../Generators/CSharp/CSharpTextTemplate.cs | 27 +++++++------------ .../GenerateAbstractImplementationsPass.cs | 5 ++-- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index 4f0c7b3c..4665a61c 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -58,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 "; @@ -676,7 +676,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) @@ -992,8 +992,7 @@ namespace CppSharp.Generators.CSharp type = ((PointerType) prop.Type).Pointee; if (prop.ExplicitInterfaceImpl == null) - WriteLine("{0} {1} {2}", - prop.Access == AccessSpecifier.Public ? "public" : "protected", + WriteLine("{0}{1} {2}", Helpers.GetAccess(prop.Access), type, GetPropertyName(prop)); else WriteLine("{0} {1}.{2}", type, prop.ExplicitInterfaceImpl.Name, GetPropertyName(prop)); @@ -1590,15 +1589,7 @@ namespace CppSharp.Generators.CSharp if (method.ExplicitInterfaceImpl == null) { - switch (GetValidMethodAccess(method, @class)) - { - case AccessSpecifier.Public: - Write("public "); - break; - case AccessSpecifier.Protected: - Write("protected "); - break; - } + Write(Helpers.GetAccess(GetValidMethodAccess(method))); } if (method.IsVirtual && !method.IsOverride && @@ -1688,7 +1679,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) { @@ -1696,7 +1687,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; } } @@ -2086,8 +2077,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/Passes/GenerateAbstractImplementationsPass.cs b/src/Generator/Passes/GenerateAbstractImplementationsPass.cs index 6f17906a..c85de2c4 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,9 @@ namespace CppSharp.Passes foreach (var abstractMethod in abstractMethods) { - internalImpl.Methods.Add(new Method(abstractMethod)); + var method = new Method(abstractMethod); + method.Namespace = internalImpl; + internalImpl.Methods.Add(method); var @delegate = new TypedefDecl { Name = abstractMethod.Name + "Delegate", From 1962751cb808163b37a76dd84e2bfc4460dbd2c1 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Fri, 20 Sep 2013 19:35:21 +0300 Subject: [PATCH 15/17] Stored the original class in its interface replacement used with multiple inheritance. Signed-off-by: Dimitar Dobrev --- src/AST/Class.cs | 2 ++ src/Generator/Generators/CSharp/CSharpMarshal.cs | 5 ++--- .../Generators/CSharp/CSharpTypePrinter.cs | 13 ++++--------- src/Generator/Passes/MultipleInheritancePass.cs | 6 +++--- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/AST/Class.cs b/src/AST/Class.cs index 645b1363..7ebdfc00 100644 --- a/src/AST/Class.cs +++ b/src/AST/Class.cs @@ -141,6 +141,8 @@ namespace CppSharp.AST } } + public Class OriginalClass { get; set; } + public bool IsValueType { get { return Type == ClassType.ValueType || IsUnion; } diff --git a/src/Generator/Generators/CSharp/CSharpMarshal.cs b/src/Generator/Generators/CSharp/CSharpMarshal.cs index 6f766a58..c7bf8353 100644 --- a/src/Generator/Generators/CSharp/CSharpMarshal.cs +++ b/src/Generator/Generators/CSharp/CSharpMarshal.cs @@ -541,9 +541,8 @@ namespace CppSharp.Generators.CSharp return; } - var qualifiedIdentifier = CSharpMarshalNativeToManagedPrinter.QualifiedIdentifier(@class); - if (@class.IsInterface) - qualifiedIdentifier = qualifiedIdentifier.Substring(1); + var qualifiedIdentifier = CSharpMarshalNativeToManagedPrinter.QualifiedIdentifier( + @class.OriginalClass ?? @class); Context.Return.Write("*({0}.Internal*){1}.{2}", qualifiedIdentifier, Helpers.SafeIdentifier(Context.Parameter.Name), Helpers.InstanceIdentifier); } diff --git a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs index 4dda213d..78a6b6ea 100644 --- a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs +++ b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs @@ -349,16 +349,11 @@ namespace CppSharp.Generators.CSharp public CSharpTypePrinterResult VisitClassDecl(Class @class) { - var nestedName = GetNestedQualifiedName(@class); - if (ContextKind == CSharpTypePrinterContextKind.Native) - { - if (@class.IsInterface) - nestedName = nestedName.Substring(1); - return string.Format("{0}.Internal", nestedName); - } - - return nestedName; + return string.Format("{0}.Internal", + GetNestedQualifiedName(@class.OriginalClass ?? @class)); + + return GetNestedQualifiedName(@class); } public CSharpTypePrinterResult VisitFieldDecl(Field field) diff --git a/src/Generator/Passes/MultipleInheritancePass.cs b/src/Generator/Passes/MultipleInheritancePass.cs index aedab7a2..dc014fa5 100644 --- a/src/Generator/Passes/MultipleInheritancePass.cs +++ b/src/Generator/Passes/MultipleInheritancePass.cs @@ -39,8 +39,7 @@ namespace CppSharp.Passes if (@base.CompleteDeclaration != null) @base = (Class) @base.CompleteDeclaration; var name = "I" + @base.Name; - var @interface = (this.interfaces.ContainsKey(@base) - ? this.interfaces[@base] + var @interface = (interfaces.ContainsKey(@base) ? interfaces[@base] : @base.Namespace.Classes.FirstOrDefault(c => c.Name == name)) ?? GetNewInterface(@class, name, @base, addMembers); return @interface; @@ -53,7 +52,8 @@ namespace CppSharp.Passes Name = name, Namespace = @base.Namespace, Access = @base.Access, - IsInterface = true + IsInterface = true, + OriginalClass = @base }; @interface.Bases.AddRange( from b in @base.Bases From 593aac162ea1a80ce7758adbe8aee07a569d65e6 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Fri, 20 Sep 2013 21:10:58 +0300 Subject: [PATCH 16/17] Added a new class type "Interface". Signed-off-by: Dimitar Dobrev --- src/AST/Class.cs | 8 +++++-- .../Generators/CSharp/CSharpTextTemplate.cs | 17 +++++--------- .../Passes/CheckDuplicatedNamesPass.cs | 3 ++- .../GenerateAbstractImplementationsPass.cs | 3 +-- .../Passes/MultipleInheritancePass.cs | 23 ++++++++++++++----- .../Passes/ParamTypeToInterfacePass.cs | 2 +- 6 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/AST/Class.cs b/src/AST/Class.cs index 7ebdfc00..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. @@ -82,8 +83,6 @@ namespace CppSharp.AST // True if class provides pure virtual methods. public bool IsAbstract; - public bool IsInterface { get; set; } - // True if the type is to be treated as a union. public bool IsUnion; @@ -153,6 +152,11 @@ namespace CppSharp.AST get { return Type == ClassType.RefType && !IsUnion; } } + public bool IsInterface + { + get { return Type == ClassType.Interface; } + } + public IEnumerable Constructors { get diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index 4665a61c..0ad88e82 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -663,8 +663,7 @@ namespace CppSharp.Generators.CSharp if (@class.HasBaseClass) baseClass = @class.Bases[0].Class; - var hasRefBase = baseClass != null && baseClass.IsRefType - && !baseClass.Ignore && !baseClass.IsInterface; + var hasRefBase = baseClass != null && baseClass.IsRefType && !baseClass.Ignore; var hasIgnoredBase = baseClass != null && baseClass.Ignore; @@ -692,9 +691,7 @@ namespace CppSharp.Generators.CSharp && !@class.Bases[0].Class.IsValueType && !@class.Bases[0].Class.Ignore; - var isRefClass = @class.IsRefType && !@class.IsInterface; - - if (needsBase || isRefClass) + if (needsBase || @class.IsRefType) Write(" : "); if (needsBase) @@ -703,11 +700,11 @@ namespace CppSharp.Generators.CSharp from @base in @class.Bases select QualifiedIdentifier(@base.Class))); - if (isRefClass) + if (@class.IsRefType) Write(", "); } - if (isRefClass) + if (@class.IsRefType) Write("IDisposable"); } @@ -1422,8 +1419,7 @@ namespace CppSharp.Generators.CSharp private void GenerateDisposeMethods(Class @class) { - var hasBaseClass = @class.HasBaseClass && @class.BaseClass.IsRefType && - !@class.BaseClass.IsInterface; + var hasBaseClass = @class.HasBaseClass && @class.BaseClass.IsRefType; // Generate the IDispose Dispose() method. if (!hasBaseClass) @@ -1492,8 +1488,7 @@ namespace CppSharp.Generators.CSharp WriteLine("internal {0}(global::System.IntPtr native){1}", safeIdentifier, @class.IsValueType ? " : this()" : string.Empty); - var hasBaseClass = @class.HasBaseClass && @class.BaseClass.IsRefType && - !@class.BaseClass.IsInterface; + var hasBaseClass = @class.HasBaseClass && @class.BaseClass.IsRefType; if (hasBaseClass) WriteLineIndent(": base(native)"); diff --git a/src/Generator/Passes/CheckDuplicatedNamesPass.cs b/src/Generator/Passes/CheckDuplicatedNamesPass.cs index fcbf9fac..9a758100 100644 --- a/src/Generator/Passes/CheckDuplicatedNamesPass.cs +++ b/src/Generator/Passes/CheckDuplicatedNamesPass.cs @@ -32,7 +32,8 @@ namespace CppSharp.Passes } var property = decl as Property; - if (property != null && property.Parameters.Count > 0) + var isIndexer = property != null && property.Parameters.Count > 0; + if (isIndexer) { return false; } diff --git a/src/Generator/Passes/GenerateAbstractImplementationsPass.cs b/src/Generator/Passes/GenerateAbstractImplementationsPass.cs index c85de2c4..a15d2c3d 100644 --- a/src/Generator/Passes/GenerateAbstractImplementationsPass.cs +++ b/src/Generator/Passes/GenerateAbstractImplementationsPass.cs @@ -51,8 +51,7 @@ namespace CppSharp.Passes foreach (var abstractMethod in abstractMethods) { - var method = new Method(abstractMethod); - method.Namespace = internalImpl; + var method = new Method(abstractMethod) { Namespace = internalImpl }; internalImpl.Methods.Add(method); var @delegate = new TypedefDecl { diff --git a/src/Generator/Passes/MultipleInheritancePass.cs b/src/Generator/Passes/MultipleInheritancePass.cs index dc014fa5..d9c7b8e3 100644 --- a/src/Generator/Passes/MultipleInheritancePass.cs +++ b/src/Generator/Passes/MultipleInheritancePass.cs @@ -8,7 +8,10 @@ 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. + /// 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(); @@ -23,6 +26,7 @@ namespace CppSharp.Passes 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; @@ -39,10 +43,11 @@ namespace CppSharp.Passes if (@base.CompleteDeclaration != null) @base = (Class) @base.CompleteDeclaration; var name = "I" + @base.Name; - var @interface = (interfaces.ContainsKey(@base) ? interfaces[@base] - : @base.Namespace.Classes.FirstOrDefault(c => c.Name == name)) ?? - GetNewInterface(@class, name, @base, addMembers); - return @interface; + 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) @@ -52,16 +57,19 @@ namespace CppSharp.Passes Name = name, Namespace = @base.Namespace, Access = @base.Access, - IsInterface = true, + 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(); @@ -70,7 +78,9 @@ namespace CppSharp.Passes instance.GetMethod = new Method(); @interface.Properties.Add(instance); } + @interface.Events.AddRange(@base.Events); + if (addMembers) { ImplementInterfaceMethods(@class, @interface); @@ -78,6 +88,7 @@ namespace CppSharp.Passes if (@base.Bases.All(b => b.Class != @interface)) @base.Bases.Add(new BaseClassSpecifier { Type = new TagType(@interface) }); } + interfaces.Add(@base, @interface); return @interface; } diff --git a/src/Generator/Passes/ParamTypeToInterfacePass.cs b/src/Generator/Passes/ParamTypeToInterfacePass.cs index b8fd0e79..c561d066 100644 --- a/src/Generator/Passes/ParamTypeToInterfacePass.cs +++ b/src/Generator/Passes/ParamTypeToInterfacePass.cs @@ -18,7 +18,7 @@ namespace CppSharp.Passes var @class = tagType.Declaration as Class; if (@class != null) { - var @interface = @class.Namespace.FindClass("I" + @class.Name); + var @interface = @class.Namespace.Classes.Find(c => c.OriginalClass == @class); if (@interface != null) parameter.QualifiedType = new QualifiedType(new TagType(@interface)); } From 07beb470481c6bcd4b71e278b694d8d4a9864cf5 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Fri, 20 Sep 2013 22:15:26 +0300 Subject: [PATCH 17/17] Converted return types to their complementary interfaces, if any. Signed-off-by: Dimitar Dobrev --- .../Generators/CSharp/CSharpMarshal.cs | 2 +- .../Passes/MultipleInheritancePass.cs | 4 +-- .../Passes/ParamTypeToInterfacePass.cs | 28 ++++++++++++++++--- tests/CSharpTemp/CSharpTemp.Tests.cs | 1 + tests/CSharpTemp/CSharpTemp.cpp | 5 ++++ tests/CSharpTemp/CSharpTemp.h | 1 + 6 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/Generator/Generators/CSharp/CSharpMarshal.cs b/src/Generator/Generators/CSharp/CSharpMarshal.cs index c7bf8353..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); diff --git a/src/Generator/Passes/MultipleInheritancePass.cs b/src/Generator/Passes/MultipleInheritancePass.cs index d9c7b8e3..4651ab83 100644 --- a/src/Generator/Passes/MultipleInheritancePass.cs +++ b/src/Generator/Passes/MultipleInheritancePass.cs @@ -85,9 +85,9 @@ namespace CppSharp.Passes { ImplementInterfaceMethods(@class, @interface); ImplementInterfaceProperties(@class, @interface); - if (@base.Bases.All(b => b.Class != @interface)) - @base.Bases.Add(new BaseClassSpecifier { Type = new TagType(@interface) }); } + if (@base.Bases.All(b => b.Class != @interface)) + @base.Bases.Add(new BaseClassSpecifier { Type = new TagType(@interface) }); interfaces.Add(@base, @interface); return @interface; diff --git a/src/Generator/Passes/ParamTypeToInterfacePass.cs b/src/Generator/Passes/ParamTypeToInterfacePass.cs index c561d066..5e0e12d0 100644 --- a/src/Generator/Passes/ParamTypeToInterfacePass.cs +++ b/src/Generator/Passes/ParamTypeToInterfacePass.cs @@ -4,12 +4,32 @@ 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) { - var tagType = parameter.QualifiedType.Type as TagType; + 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 = parameter.QualifiedType.Type as PointerType; + var pointerType = type.Type as PointerType; if (pointerType != null) tagType = pointerType.Pointee as TagType; } @@ -20,10 +40,10 @@ namespace CppSharp.Passes { var @interface = @class.Namespace.Classes.Find(c => c.OriginalClass == @class); if (@interface != null) - parameter.QualifiedType = new QualifiedType(new TagType(@interface)); + return new QualifiedType(new TagType(@interface)); } } - return base.VisitParameterDecl(parameter); + return type; } } } diff --git a/tests/CSharpTemp/CSharpTemp.Tests.cs b/tests/CSharpTemp/CSharpTemp.Tests.cs index 8e05e774..449ede61 100644 --- a/tests/CSharpTemp/CSharpTemp.Tests.cs +++ b/tests/CSharpTemp/CSharpTemp.Tests.cs @@ -43,5 +43,6 @@ public class CSharpTempTests 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 01826516..3a48155e 100644 --- a/tests/CSharpTemp/CSharpTemp.cpp +++ b/tests/CSharpTemp/CSharpTemp.cpp @@ -50,3 +50,8 @@ int Baz::takesQux(const Qux& qux) { return qux.farAwayFunc(); } + +Qux Baz::returnQux() +{ + return Qux(); +} diff --git a/tests/CSharpTemp/CSharpTemp.h b/tests/CSharpTemp/CSharpTemp.h index 608e7899..6453d89f 100644 --- a/tests/CSharpTemp/CSharpTemp.h +++ b/tests/CSharpTemp/CSharpTemp.h @@ -39,4 +39,5 @@ class DLL_API Baz : public Foo, public Bar { public: int takesQux(const Qux& qux); + Qux returnQux(); };