Browse Source

Implement abstract templates to call virtuals

This allows for, just as with regular classes, the calling of virtual methods of abstract templates returned by functions.

Fixes https://github.com/mono/CppSharp/issues/1270.

Signed-off-by: Dimitar Dobrev <dpldobrev@protonmail.com>
pull/1350/head
Dimitar Dobrev 6 years ago
parent
commit
7b6fb6e340
  1. 2
      src/Generator/Generators/CSharp/CSharpGenerator.cs
  2. 2
      src/Generator/Generators/CSharp/CSharpMarshal.cs
  3. 42
      src/Generator/Generators/CSharp/CSharpSources.cs
  4. 4
      src/Generator/Generators/CSharp/CSharpSourcesExtensions.cs
  5. 28
      src/Generator/Generators/CSharp/CSharpTypePrinter.cs
  6. 20
      src/Generator/Passes/GenerateAbstractImplementationsPass.cs
  7. 11
      tests/CSharp/CSharp.Tests.cs
  8. 5
      tests/CSharp/CSharp.cpp
  9. 1
      tests/CSharp/CSharp.h
  10. 10
      tests/CSharp/CSharpTemplates.cpp
  11. 15
      tests/CSharp/CSharpTemplates.h

2
src/Generator/Generators/CSharp/CSharpGenerator.cs

@ -43,7 +43,7 @@ namespace CppSharp.Generators.CSharp @@ -43,7 +43,7 @@ namespace CppSharp.Generators.CSharp
protected override string TypePrinterDelegate(Type type)
{
return type.Visit(typePrinter).Type;
return type.Visit(typePrinter);
}
}
}

2
src/Generator/Generators/CSharp/CSharpMarshal.cs

@ -288,7 +288,7 @@ namespace CppSharp.Generators.CSharp @@ -288,7 +288,7 @@ namespace CppSharp.Generators.CSharp
Context.Return.Write($"({returnType.Visit(typePrinter)}) (object) ");
if (returnType.IsAddress())
Context.Return.Write(HandleReturnedPointer(@class, qualifiedClass.Type));
Context.Return.Write(HandleReturnedPointer(@class, qualifiedClass));
else
Context.Return.Write($"{qualifiedClass}.{Helpers.CreateInstanceIdentifier}({Context.ReturnVarName})");

42
src/Generator/Generators/CSharp/CSharpSources.cs

@ -374,12 +374,12 @@ namespace CppSharp.Generators.CSharp @@ -374,12 +374,12 @@ namespace CppSharp.Generators.CSharp
return true;
}
if (!@class.IsDependent)
if (!@class.IsDependent && !@class.IsAbstractImpl)
foreach (var nestedTemplate in @class.Classes.Where(
c => !c.IsIncomplete && c.IsDependent))
GenerateClassTemplateSpecializationInternal(nestedTemplate);
if (@class.IsTemplate)
if (@class.IsTemplate && !@class.IsAbstractImpl)
{
if (!(@class.Namespace is Class))
GenerateClassTemplateSpecializationInternal(@class);
@ -1206,7 +1206,8 @@ namespace CppSharp.Generators.CSharp @@ -1206,7 +1206,8 @@ namespace CppSharp.Generators.CSharp
if (!(c is ClassTemplateSpecialization))
return property;
return c.Properties.SingleOrDefault(p => p.GetMethod != null &&
p.GetMethod.InstantiatedFrom == property.GetMethod);
p.GetMethod.InstantiatedFrom ==
(property.GetMethod.OriginalFunction ?? property.GetMethod));
}
private void GenerateFunctionInProperty(Class @class, Method constituent,
@ -1487,6 +1488,9 @@ namespace CppSharp.Generators.CSharp @@ -1487,6 +1488,9 @@ namespace CppSharp.Generators.CSharp
public List<VTableComponent> GetUniqueVTableMethodEntries(Class @class)
{
if (@class.IsDependent)
@class = @class.Specializations[0];
var uniqueEntries = new OrderedSet<VTableComponent>();
var vTableMethodEntries = VTables.GatherVTableMethodEntries(@class);
foreach (var entry in vTableMethodEntries.Where(e => !e.IsIgnored() && !e.Method.IsOperator))
@ -1603,6 +1607,9 @@ namespace CppSharp.Generators.CSharp @@ -1603,6 +1607,9 @@ namespace CppSharp.Generators.CSharp
private void SaveOriginalVTablePointers(Class @class)
{
if (@class.IsDependent)
@class = @class.Specializations[0];
if (Context.ParserOptions.IsMicrosoftAbi)
WriteLine("__OriginalVTables = new void*[] {{ {0} }};",
string.Join(", ",
@ -2086,13 +2093,14 @@ namespace CppSharp.Generators.CSharp @@ -2086,13 +2093,14 @@ namespace CppSharp.Generators.CSharp
var classInternal = TypePrinter.PrintNative(@class);
if (@class.IsDynamic && GetUniqueVTableMethodEntries(@class).Count != 0)
{
ClassLayout layout = (@class.IsDependent ? @class.Specializations[0] : @class).Layout;
if (Context.ParserOptions.IsMicrosoftAbi)
for (var i = 0; i < @class.Layout.VTablePointers.Count; i++)
for (var i = 0; i < layout.VTablePointers.Count; i++)
WriteLine($@"(({classInternal}*) {Helpers.InstanceIdentifier})->{
@class.Layout.VTablePointers[i].Name} = new global::System.IntPtr(__OriginalVTables[{i}]);");
layout.VTablePointers[i].Name} = new global::System.IntPtr(__OriginalVTables[{i}]);");
else
WriteLine($@"(({classInternal}*) {Helpers.InstanceIdentifier})->{
@class.Layout.VTablePointers[0].Name} = new global::System.IntPtr(__OriginalVTables[0]);");
layout.VTablePointers[0].Name} = new global::System.IntPtr(__OriginalVTables[0]);");
}
}
@ -2157,13 +2165,13 @@ namespace CppSharp.Generators.CSharp @@ -2157,13 +2165,13 @@ namespace CppSharp.Generators.CSharp
if (!@class.IsAbstractImpl)
{
PushBlock(BlockKind.Method);
var printedClass = @class.Visit(TypePrinter);
TypePrinterResult printedClass = @class.Visit(TypePrinter);
WriteLine("internal static {0}{1} {2}(global::System.IntPtr native, bool skipVTables = false)",
@class.NeedsBase && !@class.BaseClass.IsInterface ? "new " : string.Empty,
printedClass, Helpers.CreateInstanceIdentifier);
WriteOpenBraceAndIndent();
var suffix = @class.IsAbstract ? "Internal" : string.Empty;
var ctorCall = $"{printedClass}{suffix}";
var ctorCall = $"{printedClass.Type}{suffix}{printedClass.NameSuffix}";
WriteLine("return new {0}(native.ToPointer(), skipVTables);", ctorCall);
UnindentAndWriteCloseBrace();
PopBlock(NewLineKind.BeforeNextBlock);
@ -2196,7 +2204,7 @@ namespace CppSharp.Generators.CSharp @@ -2196,7 +2204,7 @@ namespace CppSharp.Generators.CSharp
WriteLine("{0} = new global::System.IntPtr(native);", Helpers.InstanceIdentifier);
var dtor = @class.Destructors.FirstOrDefault();
var hasVTables = @class.IsDynamic && GetUniqueVTableMethodEntries(@class).Count > 0;
var setupVTables = !@class.IsAbstractImpl && hasVTables && dtor != null && dtor.IsVirtual;
var setupVTables = !@class.IsAbstractImpl && hasVTables && dtor?.IsVirtual == true;
if (setupVTables)
{
WriteLine("if (skipVTables)");
@ -2224,7 +2232,7 @@ namespace CppSharp.Generators.CSharp @@ -2224,7 +2232,7 @@ namespace CppSharp.Generators.CSharp
PopBlock(NewLineKind.BeforeNextBlock);
}
public void GenerateNativeConstructorByValue(Class @class, string returnType)
public void GenerateNativeConstructorByValue(Class @class, TypePrinterResult returnType)
{
var @internal = TypePrinter.PrintNative(@class.IsAbstractImpl ? @class.BaseClass : @class);
@ -2235,7 +2243,7 @@ namespace CppSharp.Generators.CSharp @@ -2235,7 +2243,7 @@ namespace CppSharp.Generators.CSharp
returnType, Helpers.CreateInstanceIdentifier, @internal);
WriteOpenBraceAndIndent();
var suffix = @class.IsAbstract ? "Internal" : "";
WriteLine($"return new {returnType}{suffix}(native, skipVTables);");
WriteLine($"return new {returnType.Type}{suffix}{returnType.NameSuffix}(native, skipVTables);");
UnindentAndWriteCloseBrace();
PopBlock(NewLineKind.BeforeNextBlock);
}
@ -2451,7 +2459,7 @@ namespace CppSharp.Generators.CSharp @@ -2451,7 +2459,7 @@ namespace CppSharp.Generators.CSharp
if (specialization != null)
{
var specializedMethod = @class.Methods.FirstOrDefault(
m => m.InstantiatedFrom == method);
m => m.InstantiatedFrom == (method.OriginalFunction ?? method));
if (specializedMethod == null)
{
WriteLine($@"throw new MissingMethodException(""Method {
@ -2479,10 +2487,6 @@ namespace CppSharp.Generators.CSharp @@ -2479,10 +2487,6 @@ namespace CppSharp.Generators.CSharp
{
GenerateOperator(method, returnType);
}
else if (method.SynthKind == FunctionSynthKind.AbstractImplCall)
{
GenerateVirtualFunctionCall(method);
}
else if (method.IsVirtual)
{
GenerateVirtualFunctionCall(method);
@ -2752,12 +2756,6 @@ namespace CppSharp.Generators.CSharp @@ -2752,12 +2756,6 @@ namespace CppSharp.Generators.CSharp
public void GenerateFunctionCall(string functionName, Function function,
QualifiedType returnType = default(QualifiedType))
{
if (function.IsPure)
{
WriteLine("throw new System.NotImplementedException();");
return;
}
// ignored functions may get here from interfaces for secondary bases
if (function.Ignore)
{

4
src/Generator/Generators/CSharp/CSharpSourcesExtensions.cs

@ -29,11 +29,11 @@ namespace CppSharp.Generators.CSharp @@ -29,11 +29,11 @@ namespace CppSharp.Generators.CSharp
if (@class.IsTemplate)
specializations = specializations.KeepSingleAllPointersSpecialization();
foreach (var specialization in specializations)
gen.GenerateNativeConstructorByValue(specialization, printedClass.Type);
gen.GenerateNativeConstructorByValue(specialization, printedClass);
}
else
{
gen.GenerateNativeConstructorByValue(@class, printedClass.Type);
gen.GenerateNativeConstructorByValue(@class, printedClass);
}
}

28
src/Generator/Generators/CSharp/CSharpTypePrinter.cs

@ -293,9 +293,11 @@ namespace CppSharp.Generators.CSharp @@ -293,9 +293,11 @@ namespace CppSharp.Generators.CSharp
List<TemplateArgument> args = template.Arguments;
var @class = (Class) template.Template.TemplatedDecl;
TemplateArgument lastArg = args.Last();
return $@"{VisitDeclaration(decl)}<{string.Join(", ",
TypePrinterResult typePrinterResult = VisitDeclaration(decl);
typePrinterResult.NameSuffix.Append($@"<{string.Join(", ",
args.Concat(Enumerable.Range(0, @class.TemplateParameters.Count - args.Count).Select(
i => lastArg)).Select(this.VisitTemplateArgument))}>";
i => lastArg)).Select(this.VisitTemplateArgument))}>");
return typePrinterResult;
}
if (ContextKind == TypePrinterContextKind.Native)
@ -529,20 +531,21 @@ namespace CppSharp.Generators.CSharp @@ -529,20 +531,21 @@ namespace CppSharp.Generators.CSharp
return $@"{VisitDeclaration(@class.OriginalClass ?? @class)}.{
Helpers.InternalStruct}{Helpers.GetSuffixForInternal(@class)}";
var printed = VisitDeclaration(@class).Type;
if (!@class.IsTemplate)
return printed;
return $@"{printed}<{string.Join(", ",
@class.TemplateParameters.Select(p => p.Name))}>";
TypePrinterResult printed = VisitDeclaration(@class);
if (@class.IsTemplate)
printed.NameSuffix.Append($@"<{string.Join(", ",
@class.TemplateParameters.Select(p => p.Name))}>");
return printed;
}
public override TypePrinterResult VisitClassTemplateSpecializationDecl(
ClassTemplateSpecialization specialization)
{
if (ContextKind == TypePrinterContextKind.Native)
return $"{VisitClassDecl(specialization)}";
var args = string.Join(", ", specialization.Arguments.Select(VisitTemplateArgument));
return $"{VisitClassDecl(specialization)}<{args}>";
TypePrinterResult typePrinterResult = VisitClassDecl(specialization);
if (ContextKind != TypePrinterContextKind.Native)
typePrinterResult.NameSuffix.Append($@"<{string.Join(", ",
specialization.Arguments.Select(VisitTemplateArgument))}>");
return typePrinterResult;
}
public TypePrinterResult VisitTemplateArgument(TemplateArgument a)
@ -556,7 +559,8 @@ namespace CppSharp.Generators.CSharp @@ -556,7 +559,8 @@ namespace CppSharp.Generators.CSharp
return $@"CppSharp.Runtime.Pointer<{(pointee == PrimitiveType.Void ? IntPtrType :
VisitPrimitiveType(pointee, new TypeQualifiers()).Type)}>";
}
return type.IsPrimitiveType(PrimitiveType.Void) ? "object" : type.Visit(this).Type;
return type.IsPrimitiveType(PrimitiveType.Void) ?
new TypePrinterResult("object") : type.Visit(this);
}
public override TypePrinterResult VisitParameterDecl(Parameter parameter)

20
src/Generator/Passes/GenerateAbstractImplementationsPass.cs

@ -57,7 +57,7 @@ namespace CppSharp.Passes @@ -57,7 +57,7 @@ namespace CppSharp.Passes
if (@class.CompleteDeclaration != null)
return VisitClassDecl(@class.CompleteDeclaration as Class);
if (@class.IsAbstract && !@class.IsTemplate)
if (@class.IsAbstract && (!@class.IsTemplate || Options.GenerateClassTemplates))
{
foreach (var ctor in from ctor in @class.Constructors
where ctor.Access == AccessSpecifier.Public
@ -69,7 +69,7 @@ namespace CppSharp.Passes @@ -69,7 +69,7 @@ namespace CppSharp.Passes
return @class.IsAbstract;
}
private static Class AddInternalImplementation(Class @class)
private static T AddInternalImplementation<T>(T @class) where T : Class, new()
{
var internalImpl = GetInternalImpl(@class);
@ -94,14 +94,26 @@ namespace CppSharp.Passes @@ -94,14 +94,26 @@ namespace CppSharp.Passes
return internalImpl;
}
private static Class GetInternalImpl(Declaration @class)
private static T GetInternalImpl<T>(T @class) where T : Class, new()
{
var internalImpl = new Class
var internalImpl = new T
{
Name = @class.Name + "Internal",
Access = AccessSpecifier.Private,
Namespace = @class.Namespace
};
if (@class.IsDependent)
{
internalImpl.IsDependent = true;
internalImpl.TemplateParameters.AddRange(@class.TemplateParameters);
foreach (var specialization in @class.Specializations)
{
var specializationImpl = AddInternalImplementation(specialization);
specializationImpl.Arguments.AddRange(specialization.Arguments);
specializationImpl.TemplatedDecl = specialization.TemplatedDecl;
internalImpl.Specializations.Add(specializationImpl);
}
}
var @base = new BaseClassSpecifier { Type = new TagType(@class) };
internalImpl.Bases.Add(@base);

11
tests/CSharp/CSharp.Tests.cs

@ -992,6 +992,17 @@ public unsafe class CSharpTests : GeneratorTestFixture @@ -992,6 +992,17 @@ public unsafe class CSharpTests : GeneratorTestFixture
}
}
[Test]
public void TestAbstractTemplate()
{
using (Foo foo = new Foo())
using (AbstractTemplate<int> abstractTemplate = foo.AbstractTemplate)
{
Assert.That(abstractTemplate.Property, Is.EqualTo(55));
Assert.That(abstractTemplate.CallFunction(), Is.EqualTo(65));
}
}
[Test]
public void TestSpecializationForSecondaryBase()
{

5
tests/CSharp/CSharp.cpp

@ -88,6 +88,11 @@ const int& Foo::returnConstRef() @@ -88,6 +88,11 @@ const int& Foo::returnConstRef()
return rename;
}
AbstractTemplate<int>* Foo::getAbstractTemplate()
{
return new ImplementAbstractTemplate();
}
const int Foo::rename;
int Foo::makeFunctionCall()

1
tests/CSharp/CSharp.h

@ -32,6 +32,7 @@ public: @@ -32,6 +32,7 @@ public:
int width();
void set_width(int value);
const int& returnConstRef();
AbstractTemplate<int>* getAbstractTemplate();
static const int rename = 5;
static int makeFunctionCall();

10
tests/CSharp/CSharpTemplates.cpp

@ -145,6 +145,16 @@ void RegularDynamic::virtualFunction() @@ -145,6 +145,16 @@ void RegularDynamic::virtualFunction()
{
}
int ImplementAbstractTemplate::property()
{
return 55;
}
int ImplementAbstractTemplate::callFunction()
{
return 65;
}
void forceUseSpecializations(IndependentFields<int> _1, IndependentFields<bool> _2,
IndependentFields<T1> _3, IndependentFields<std::string> _4,
DependentValueFields<int> _5,

15
tests/CSharp/CSharpTemplates.h

@ -721,6 +721,21 @@ HasCtorWithMappedToEnum<T>::HasCtorWithMappedToEnum(QFlags<T> t) @@ -721,6 +721,21 @@ HasCtorWithMappedToEnum<T>::HasCtorWithMappedToEnum(QFlags<T> t)
{
}
template <typename T>
class AbstractTemplate
{
public:
virtual int property() = 0;
virtual int callFunction() = 0;
};
class ImplementAbstractTemplate : public AbstractTemplate<int>
{
public:
int property() override;
int callFunction() override;
};
enum class TestFlag
{
Flag1,

Loading…
Cancel
Save