From 0a102d8fbc91cbeefcfe15a946fc9eae8f49aff1 Mon Sep 17 00:00:00 2001 From: triton Date: Wed, 26 Feb 2014 18:43:49 +0000 Subject: [PATCH] Added better wrapping for static classes. --- src/AST/Class.cs | 3 + src/Generator/Driver.cs | 1 + .../Generators/CLI/CLIHeadersTemplate.cs | 14 ++-- .../Generators/CLI/CLISourcesTemplate.cs | 26 +++++--- src/Generator/Passes/CheckStaticClass.cs | 65 +++++++++++++++++++ tests/Basic/Basic.Tests.cs | 6 ++ tests/Basic/Basic.h | 8 +++ 7 files changed, 110 insertions(+), 13 deletions(-) create mode 100644 src/Generator/Passes/CheckStaticClass.cs diff --git a/src/AST/Class.cs b/src/AST/Class.cs index bd743004..5336a28f 100644 --- a/src/AST/Class.cs +++ b/src/AST/Class.cs @@ -114,6 +114,9 @@ namespace CppSharp.AST // True if the class has a non trivial destructor. public bool HasNonTrivialDestructor; + // True if the class represents a static class. + public bool IsStatic; + public Class() { Bases = new List(); diff --git a/src/Generator/Driver.cs b/src/Generator/Driver.cs index 328e3822..9d4e7605 100644 --- a/src/Generator/Driver.cs +++ b/src/Generator/Driver.cs @@ -256,6 +256,7 @@ namespace CppSharp library.SetupPasses(this); TranslationUnitPasses.AddPass(new FindSymbolsPass()); + TranslationUnitPasses.AddPass(new CheckStaticClass()); TranslationUnitPasses.AddPass(new MoveOperatorToClassPass()); TranslationUnitPasses.AddPass(new MoveFunctionToClassPass()); TranslationUnitPasses.AddPass(new CheckAmbiguousFunctions()); diff --git a/src/Generator/Generators/CLI/CLIHeadersTemplate.cs b/src/Generator/Generators/CLI/CLIHeadersTemplate.cs index d56facfe..c9c3e2e6 100644 --- a/src/Generator/Generators/CLI/CLIHeadersTemplate.cs +++ b/src/Generator/Generators/CLI/CLIHeadersTemplate.cs @@ -360,6 +360,9 @@ namespace CppSharp.Generators.CLI public void GenerateClassConstructors(Class @class, string nativeType) { + if (@class.IsStatic) + return; + PushIndent(); // Output a default constructor that takes the native pointer. @@ -566,10 +569,13 @@ namespace CppSharp.Generators.CLI return true; } - if (CSharpTextTemplate.HasRefBase(@class)) - Write(" : {0}", QualifiedIdentifier(@class.Bases[0].Class)); - else if (@class.IsRefType) - Write(" : ICppInstance"); + if (!@class.IsStatic) + { + if (CSharpTextTemplate.HasRefBase(@class)) + Write(" : {0}", QualifiedIdentifier(@class.Bases[0].Class)); + else if (@class.IsRefType) + Write(" : ICppInstance"); + } NewLine(); WriteLine("{"); diff --git a/src/Generator/Generators/CLI/CLISourcesTemplate.cs b/src/Generator/Generators/CLI/CLISourcesTemplate.cs index dff450c2..978fc56e 100644 --- a/src/Generator/Generators/CLI/CLISourcesTemplate.cs +++ b/src/Generator/Generators/CLI/CLISourcesTemplate.cs @@ -135,15 +135,7 @@ namespace CppSharp.Generators.CLI GenerateDeclContext(@class); - // Output a default constructor that takes the native pointer. - GenerateClassConstructor(@class, isIntPtr: false); - GenerateClassConstructor(@class, isIntPtr: true); - - if (@class.IsRefType) - { - GenerateClassDestructor(@class); - GenerateClassFinalizer(@class); - } + GenerateClassConstructors(@class); foreach (var method in @class.Methods) { @@ -199,6 +191,22 @@ namespace CppSharp.Generators.CLI PopBlock(); } + private void GenerateClassConstructors(Class @class) + { + if (@class.IsStatic) + return; + + // Output a default constructor that takes the native pointer. + GenerateClassConstructor(@class, isIntPtr: false); + GenerateClassConstructor(@class, isIntPtr: true); + + if (@class.IsRefType) + { + GenerateClassDestructor(@class); + GenerateClassFinalizer(@class); + } + } + private void GenerateClassProperties(Class @class, Class realOwner) { if (@class.IsValueType) diff --git a/src/Generator/Passes/CheckStaticClass.cs b/src/Generator/Passes/CheckStaticClass.cs new file mode 100644 index 00000000..762950fc --- /dev/null +++ b/src/Generator/Passes/CheckStaticClass.cs @@ -0,0 +1,65 @@ +using System.Linq; +using CppSharp.AST; + +namespace CppSharp.Passes +{ + /// + /// Checks for classes that should be bound as static classes. + /// + public class CheckStaticClass : TranslationUnitPass + { + static bool ReturnsClassInstance(Function function) + { + var returnType = function.ReturnType.Type.Desugar(); + + TagType tag; + if (!returnType.IsPointerTo(out tag)) + return false; + + var @class = (Class) function.Namespace; + var decl = tag.Declaration; + + if (decl is Class) + return false; + + return @class.QualifiedOriginalName == decl.QualifiedOriginalName; + } + + public override bool VisitClassDecl(Class @class) + { + if (!VisitDeclaration(@class)) + return false; + + // If the class has any non-private constructors then it cannot + // be bound as a static class and we bail out early. + if (@class.Constructors.Any(m => + !(m.IsCopyConstructor || m.IsMoveConstructor) + && m.Access != AccessSpecifier.Private)) + return false; + + // Check for any non-static fields or methods, in which case we + // assume the class is not meant to be static. + // Note: Static fields are represented as variables in the AST. + if (@class.Fields.Any() || + @class.Methods.Any(m => m.Kind == CXXMethodKind.Normal + && !m.IsStatic)) + return false; + + // Check for any static function that return a pointer to the class. + // If one exists, we assume it's a factory function and the class is + // not meant to be static. It's a simple heuristic but it should be + // good enough for the time being. + if (@class.Functions.Any(ReturnsClassInstance)) + return false; + + // TODO: We should take C++ friends into account here, they might allow + // a class to be instantiated even it if's not possible to instantiate + // it using just its regular members. + + // If all the above constraints hold, then we assume the class can be + // static. + @class.IsStatic = true; + return true; + } + } +} diff --git a/tests/Basic/Basic.Tests.cs b/tests/Basic/Basic.Tests.cs index 25c19391..adab646d 100644 --- a/tests/Basic/Basic.Tests.cs +++ b/tests/Basic/Basic.Tests.cs @@ -181,5 +181,11 @@ public class BasicTests : GeneratorTestFixture bar2.pointerToStruct.A = 15; Assert.That(bar2.pointerToStruct.A, Is.EqualTo(15)); } + + [Test] + public void TestStaticClasses() + { + Assert.That(TestStaticClass.Add(1, 2), Is.EqualTo(3)); + } } \ No newline at end of file diff --git a/tests/Basic/Basic.h b/tests/Basic/Basic.h index 987ffd63..60010545 100644 --- a/tests/Basic/Basic.h +++ b/tests/Basic/Basic.h @@ -253,3 +253,11 @@ struct DLL_API TestFinalizers { }; +// Tests static classes +struct DLL_API TestStaticClass +{ + static int Add(int a, int b) { return a + b; } + +private: + TestStaticClass(); +}; \ No newline at end of file