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.
 
 
 
 
 

636 lines
19 KiB

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using CppSharp.AST;
using CppSharp.Types;
namespace CppSharp.Generators.CLI
{
/// <summary>
/// Generates C++/CLI header files.
/// </summary>
public class CLIHeadersTemplate : CLITextTemplate
{
public override string FileExtension { get { return "h"; } }
public CLIHeadersTemplate(Driver driver, TranslationUnit unit)
: base(driver, unit)
{
}
public override void Process()
{
PushBlock(BlockKind.Header);
PopBlock();
PushBlock(CLIBlockKind.Includes);
WriteLine("#pragma once");
NewLine();
if (Options.OutputInteropIncludes)
WriteLine("#include \"CppSharp.h\"");
PushBlock(CLIBlockKind.IncludesForwardReferences);
WriteLine("#include <{0}>", TranslationUnit.IncludePath);
GenerateIncludeForwardRefs();
PopBlock(NewLineKind.BeforeNextBlock);
PopBlock(NewLineKind.Always);
GenerateNamespace(TranslationUnit);
PushBlock(BlockKind.Footer);
PopBlock();
}
public void GenerateIncludeForwardRefs()
{
var typeReferenceCollector = new CLITypeReferenceCollector(Driver.TypeDatabase);
typeReferenceCollector.Process(TranslationUnit);
var includes = new SortedSet<string>(StringComparer.InvariantCulture);
foreach (var typeRef in typeReferenceCollector.TypeReferences)
{
if (typeRef.Include.File == TranslationUnit.FileName)
continue;
var include = typeRef.Include;
if(!string.IsNullOrEmpty(include.File) && include.InHeader)
includes.Add(include.ToString());
}
foreach (var include in includes)
WriteLine(include);
}
public void GenerateForwardRefs(Namespace @namespace)
{
var typeReferenceCollector = new CLITypeReferenceCollector(Driver.TypeDatabase);
typeReferenceCollector.Process(@namespace);
// Use a set to remove duplicate entries.
var forwardRefs = new SortedSet<string>(StringComparer.InvariantCulture);
foreach (var typeRef in typeReferenceCollector.TypeReferences)
{
var @ref = typeRef.FowardReference;
if(!string.IsNullOrEmpty(@ref) && !typeRef.Include.InHeader)
forwardRefs.Add(@ref);
}
foreach (var forwardRef in forwardRefs)
WriteLine(forwardRef);
}
public void GenerateNamespace(Namespace @namespace)
{
var isTopLevel = @namespace is TranslationUnit;
var generateNamespace = !isTopLevel || Options.GenerateLibraryNamespace;
if (generateNamespace)
{
PushBlock(CLIBlockKind.Namespace, @namespace);
WriteLine("namespace {0}", isTopLevel
? Options.OutputNamespace
: SafeIdentifier(@namespace.Name));
WriteStartBraceIndent();
}
// Generate the forward references.
PushBlock(CLIBlockKind.ForwardReferences);
GenerateForwardRefs(@namespace);
PopBlock(NewLineKind.BeforeNextBlock);
// Generate all the enum declarations for the module.
foreach (var @enum in @namespace.Enums)
{
if (@enum.Ignore || @enum.IsIncomplete)
continue;
PushBlock(CLIBlockKind.Enum, @enum);
GenerateEnum(@enum);
PopBlock(NewLineKind.BeforeNextBlock);
}
// Generate all the typedef declarations for the module.
GenerateTypedefs(@namespace);
// Generate all the struct/class declarations for the module.
foreach (var @class in @namespace.Classes)
{
if (@class.Ignore || @class.IsIncomplete)
continue;
if (@class.IsOpaque)
continue;
PushBlock(CLIBlockKind.Class, @class);
GenerateClass(@class);
PopBlock(NewLineKind.BeforeNextBlock);
}
if (@namespace.HasFunctions)
GenerateFunctions(@namespace);
foreach(var childNamespace in @namespace.Namespaces)
GenerateNamespace(childNamespace);
if (generateNamespace)
{
WriteCloseBraceIndent();
PopBlock(NewLineKind.BeforeNextBlock);
}
}
public void GenerateTypedefs(Namespace @namespace)
{
foreach (var typedef in @namespace.Typedefs)
{
if (typedef.Ignore)
continue;
GenerateTypedef(typedef);
}
}
public void GenerateFunctions(Namespace @namespace)
{
PushBlock(CLIBlockKind.FunctionsClass);
WriteLine("public ref class {0}{1}", SafeIdentifier(Options.OutputNamespace),
TranslationUnit.FileNameWithoutExtension);
WriteLine("{");
WriteLine("public:");
PushIndent();
// Generate all the function declarations for the module.
foreach (var function in @namespace.Functions)
{
GenerateFunction(function);
}
PopIndent();
WriteLine("};");
PopBlock(NewLineKind.BeforeNextBlock);
}
public void GenerateDeclarationCommon(Declaration T)
{
GenerateSummary(T.BriefComment);
GenerateDebug(T);
}
public void GenerateClass(Class @class)
{
if (@class.Ignore || @class.IsIncomplete)
return;
GenerateDeclarationCommon(@class);
if (@class.IsUnion)
{
// TODO: How to do wrapping of unions?
//const string @namespace = "System::Runtime::InteropServices";
//WriteLine("[{0}::StructLayout({0}::LayoutKind::Explicit)]",
// @namespace);
//throw new NotImplementedException("Unions are not supported yet");
}
if (GenerateClassProlog(@class))
return;
var nativeType = string.Format("::{0}*", @class.QualifiedOriginalName);
if (@class.IsRefType)
{
GenerateClassNativeField(@class, nativeType);
}
GenerateClassConstructors(@class, nativeType);
GenerateClassFields(@class);
// Generate a property for each field if class is not value type
if (@class.IsRefType)
GenerateClassProperties(@class);
GenerateClassEvents(@class);
GenerateClassMethods(@class);
if (Options.GenerateFunctionTemplates)
GenerateClassGenericMethods(@class);
GenerateClassVariables(@class);
WriteLine("};");
}
internal static bool HasRefBase(Class @class)
{
Class baseClass = null;
if (@class.HasBaseClass)
baseClass = @class.Bases[0].Class;
var hasRefBase = baseClass != null && baseClass.IsRefType
&& !baseClass.Ignore;
return hasRefBase;
}
public void GenerateClassNativeField(Class @class, string nativeType)
{
if (HasRefBase(@class)) return;
WriteLineIndent("property {0} NativePtr;", nativeType);
PushIndent();
WriteLine("property System::IntPtr Instance");
WriteStartBraceIndent();
WriteLine("virtual System::IntPtr get() override;");
WriteLine("virtual void set(System::IntPtr instance) override;");
WriteCloseBraceIndent();
NewLine();
PopIndent();
}
public void GenerateClassGenericMethods(Class @class)
{
var printer = TypePrinter as CLITypePrinter;
var oldCtx = printer.Context;
PushIndent();
foreach (var template in @class.Templates)
{
if (template.Ignore) continue;
var functionTemplate = template as FunctionTemplate;
if (functionTemplate == null) continue;
var function = functionTemplate.TemplatedFunction;
var typeNames = template.Parameters.Select(
param => "typename " + param.Name).ToList();
var typeCtx = new CLITypePrinterContext()
{
Kind = TypePrinterContextKind.Template,
Declaration = template
};
printer.Context = typeCtx;
var typePrinter = new CLITypePrinter(Driver, typeCtx);
var retType = function.ReturnType.Type.Visit(typePrinter,
function.ReturnType.Qualifiers);
WriteLine("generic<{0}>", string.Join(", ", typeNames));
WriteLine("{0} {1}({2});", retType, SafeIdentifier(function.Name),
GenerateParametersList(function.Parameters));
}
PopIndent();
printer.Context = oldCtx;
}
public void GenerateClassConstructors(Class @class, string nativeType)
{
PushIndent();
// Output a default constructor that takes the native pointer.
WriteLine("{0}({1} native);", SafeIdentifier(@class.Name), nativeType);
WriteLine("{0}({1} native);", SafeIdentifier(@class.Name), "System::IntPtr");
foreach (var ctor in @class.Constructors)
{
if (ctor.IsCopyConstructor || ctor.IsMoveConstructor)
continue;
// Default constructors are not supported in .NET value types.
if (ctor.Parameters.Count == 0 && @class.IsValueType)
continue;
GenerateMethod(ctor);
}
PopIndent();
}
public void GenerateClassFields(Class @class)
{
if (!@class.IsValueType)
return;
// Handle the case of struct (value-type) inheritance by adding the base
// fields to the managed value subtypes.
foreach (var @base in @class.Bases)
{
Class baseClass;
if (!@base.Type.IsTagDecl(out baseClass))
continue;
if (!baseClass.IsValueType || baseClass.Ignore)
{
Console.WriteLine("Ignored base class of value type '{0}'",
baseClass.Name);
continue;
}
GenerateClassFields(baseClass);
}
PushIndent();
foreach (var field in @class.Fields)
{
if (ASTUtils.CheckIgnoreField(@class, field)) continue;
GenerateDeclarationCommon(field);
if (@class.IsUnion)
WriteLine("[FieldOffset({0})]", field.Offset);
WriteLine("{0} {1};", field.Type, SafeIdentifier(field.Name));
}
PopIndent();
}
public void GenerateClassEvents(Class @class)
{
foreach (var @event in @class.Events)
{
if (@event.Ignore) continue;
var cppTypePrinter = new CppTypePrinter(Driver.TypeDatabase);
var cppArgs = cppTypePrinter.VisitParameters(@event.Parameters, hasNames: true);
WriteLine("private:");
PushIndent();
var delegateName = string.Format("_{0}Delegate", @event.Name);
WriteLine("delegate void {0}({1});", delegateName, cppArgs);
WriteLine("{0}^ {0}Instance;", delegateName);
WriteLine("void _{0}Raise({1});", @event.Name, cppArgs);
WriteLine("{0} _{1};", @event.Type, @event.Name);
PopIndent();
WriteLine("public:");
PushIndent();
WriteLine("event {0} {1}", @event.Type, @event.Name);
WriteStartBraceIndent();
WriteLine("void add({0} evt);", @event.Type);
WriteLine("void remove({0} evt);", @event.Type);
var cliTypePrinter = new CLITypePrinter(Driver);
var cliArgs = cliTypePrinter.VisitParameters(@event.Parameters, hasNames: true);
WriteLine("void raise({0});", cliArgs);
WriteCloseBraceIndent();
PopIndent();
}
}
public void GenerateClassMethods(Class @class)
{
PushIndent();
var staticMethods = new List<Method>();
foreach (var method in @class.Methods)
{
if (ASTUtils.CheckIgnoreMethod(@class, method))
continue;
if (method.IsConstructor)
continue;
if (method.IsStatic)
{
staticMethods.Add(method);
continue;
}
GenerateMethod(method);
}
foreach(var method in staticMethods)
GenerateMethod(method);
PopIndent();
}
public void GenerateClassVariables(Class @class)
{
PushIndent();
foreach(var variable in @class.Variables)
{
if (variable.Ignore) continue;
if (variable.Access != AccessSpecifier.Public)
continue;
var type = variable.Type;
WriteLine("static property {0} {1}", type, variable.Name);
WriteStartBraceIndent();
WriteLine("{0} get();", type);
if (!variable.QualifiedType.Qualifiers.IsConst)
WriteLine("void set({0});", type);
WriteCloseBraceIndent();
}
PopIndent();
}
public bool GenerateClassProlog(Class @class)
{
Write("public ");
Write(@class.IsValueType ? "value struct " : "ref class ");
Write("{0}", SafeIdentifier(@class.Name));
if (@class.IsOpaque)
{
WriteLine(";");
return true;
}
if (HasRefBase(@class))
Write(" : {0}", QualifiedIdentifier(@class.Bases[0].Class));
else if (@class.IsRefType)
Write(" : ICppInstance");
NewLine();
WriteLine("{");
WriteLine("public:");
return false;
}
public void GenerateClassProperties(Class @class)
{
PushIndent();
foreach (var field in @class.Fields)
{
if (ASTUtils.CheckIgnoreField(@class, field))
continue;
GenerateDeclarationCommon(field);
GenerateFieldProperty(field);
}
PopIndent();
}
public void GenerateFieldProperty(Field field)
{
var type = field.Type.Visit(TypePrinter, field.QualifiedType.Qualifiers);
WriteLine("property {0} {1}", type, field.Name);
WriteStartBraceIndent();
WriteLine("{0} get();", type);
WriteLine("void set({0});", type);
WriteCloseBraceIndent();
}
public void GenerateMethod(Method method)
{
if (method.Ignore) return;
if (method.Access != AccessSpecifier.Public)
return;
PushBlock(CLIBlockKind.Method, method);
GenerateDeclarationCommon(method);
if (method.IsOverride)
Write("virtual ");
if (method.IsStatic)
Write("static ");
if (method.Kind == CXXMethodKind.Constructor || method.Kind == CXXMethodKind.Destructor)
Write("{0}(", SafeIdentifier(method.Name));
else
Write("{0} {1}(", method.ReturnType, SafeIdentifier(method.Name));
GenerateMethodParameters(method);
Write(")");
if (method.IsOverride)
Write(" override");
WriteLine(";");
PopBlock();
}
public bool GenerateTypedef(TypedefDecl typedef)
{
if (typedef.Ignore)
return false;
FunctionType function;
if (typedef.Type.IsPointerTo<FunctionType>(out function))
{
PushBlock(CLIBlockKind.Typedef, typedef);
GenerateDeclarationCommon(typedef);
WriteLine("public {0};",
string.Format(TypePrinter.VisitDelegate(function),
SafeIdentifier(typedef.Name)));
PopBlock(NewLineKind.BeforeNextBlock);
return true;
}
else if (typedef.Type.IsEnumType())
{
// Already handled in the parser.
}
else
{
Console.WriteLine("Unhandled typedef type: {0}", typedef);
}
return false;
}
public void GenerateFunction(Function function)
{
if (function.Ignore)
return;
PushBlock(CLIBlockKind.Function, function);
GenerateDeclarationCommon(function);
var retType = function.ReturnType.ToString();
Write("static {0} {1}(", retType, SafeIdentifier(function.Name));
Write(GenerateParametersList(function.Parameters));
WriteLine(");");
PopBlock();
}
public void GenerateDebug(Declaration decl)
{
if (Options.OutputDebug && !String.IsNullOrWhiteSpace(decl.DebugText))
WriteLine("// DEBUG: " + decl.DebugText);
}
public void GenerateEnum(Enumeration @enum)
{
if (@enum.Ignore || @enum.IsIncomplete)
return;
GenerateDeclarationCommon(@enum);
if (@enum.Modifiers.HasFlag(Enumeration.EnumModifiers.Flags))
WriteLine("[System::Flags]");
Write("public enum struct {0}", SafeIdentifier(@enum.Name));
var typeName = TypePrinter.VisitPrimitiveType(@enum.BuiltinType.Type,
new TypeQualifiers());
if (@enum.BuiltinType.Type != PrimitiveType.Int32)
WriteLine(" : {0}", typeName);
else
NewLine();
WriteLine("{");
PushIndent();
for (int i = 0; i < @enum.Items.Count; ++i)
{
var item = @enum.Items[i];
GenerateInlineSummary(item.Comment);
if (item.ExplicitValue)
Write(String.Format("{0} = {1}", SafeIdentifier(item.Name),
@enum.GetItemValueAsString(item)));
else
Write(String.Format("{0}", SafeIdentifier(item.Name)));
if (i < @enum.Items.Count - 1)
WriteLine(",");
}
PopIndent();
NewLine();
WriteLine("};");
}
}
}