Tools and libraries to glue C/C++ APIs to high-level languages
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

112 lines
4.0 KiB

using System.Linq;
using CppSharp.AST;
using CppSharp.AST.Extensions;
namespace CppSharp.Passes
{
/// <summary>
/// Checks for classes that should be bound as static classes.
/// </summary>
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);
}
}
}