using System.Linq; using CppSharp.AST; using CppSharp.AST.Extensions; namespace CppSharp.Passes { /// /// Checks for classes that should be bound as static classes. /// public class CheckStaticClass : TranslationUnitPass { public CheckStaticClass() { VisitOptions.VisitClassBases = false; } public override bool VisitDeclaration(Declaration decl) { if (!base.VisitDeclaration(decl)) return false; if (Driver.Options.IsCSharpGenerator) { // C# cannot have protected members in static classes. var @class = decl.Namespace as Class; if ( decl.Access == AccessSpecifier.Protected && decl.GenerationKind == GenerationKind.Generate && @class != null && @class.IsStatic) { // By setting it to private it will appear // as an internal in the final C# wrapper. decl.Access = AccessSpecifier.Private; // We need to explicity set the generation else the // now private declaration will get filtered out later. decl.GenerationKind = GenerationKind.Generate; } } return true; } static bool ReturnsClassInstance(Function function) { var returnType = function.ReturnType.Type.Desugar(); var tag = returnType as TagType; if (tag == null) returnType.IsPointerTo(out tag); if (tag == null) 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 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) || @class.Methods.Any(ReturnsClassInstance)) return false; // If the class is to be used as an opaque type, then it cannot be // bound as static. if (@class.IsOpaque) 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; // Ignore the special methods for static classes. foreach (var ctor in @class.Constructors) ctor.GenerationKind = GenerationKind.Internal; foreach (var dtor in @class.Destructors) dtor.GenerationKind = GenerationKind.Internal; return base.VisitClassDecl(@class); } } }