Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 54 additions & 8 deletions sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,6 @@ private string GetCursorName(NamedDecl namedDecl)
else
{
name = GetTypeName(namedDecl, context: null, typeDecl.TypeForDecl, out var nativeTypeName);
Debug.Assert(string.IsNullOrWhiteSpace(nativeTypeName));
}
}
else if (namedDecl is ParmVarDecl)
Expand Down Expand Up @@ -1027,6 +1026,10 @@ private static CXXRecordDecl GetRecordDeclForBaseSpecifier(CXXBaseSpecifier cxxB
{
baseType = elaboratedType.CanonicalType;
}
else if (baseType is TemplateSpecializationType templateSpecializationType)
{
baseType = templateSpecializationType.CanonicalType;
}
else if (baseType is TypedefType typedefType)
{
baseType = typedefType.Decl.UnderlyingType;
Expand Down Expand Up @@ -1140,12 +1143,6 @@ private string GetRemappedTypeName(Cursor cursor, Cursor context, Type type, out
var name = GetTypeName(cursor, context, type, out nativeTypeName);
name = GetRemappedName(name, cursor, tryRemapOperatorName: false);

if (name.Contains("::"))
{
name = name.Split(new string[] { "::" }, StringSplitOptions.RemoveEmptyEntries).Last();
name = GetRemappedName(name, cursor, tryRemapOperatorName: false);
}

var canonicalType = type.CanonicalType;

if ((canonicalType is ConstantArrayType constantArrayType) && (constantArrayType.ElementType is RecordType))
Expand Down Expand Up @@ -1389,6 +1386,49 @@ private string GetTypeName(Cursor cursor, Cursor context, Type type, out string
{
name = GetTypeNameForPointeeType(cursor, context, referenceType.PointeeType, out var nativePointeeTypeName);
}
else if (type is TemplateSpecializationType templateSpecializationType)
{
var nameBuilder = new StringBuilder();

var templateTypeDecl = ((RecordType)templateSpecializationType.CanonicalType).Decl;
nameBuilder.Append(GetRemappedName(templateTypeDecl.Name, templateTypeDecl, tryRemapOperatorName: false));

nameBuilder.Append('<');

bool shouldWritePrecedingComma = false;

foreach (var arg in templateSpecializationType.Args)
{
if (shouldWritePrecedingComma)
{
nameBuilder.Append(',');
nameBuilder.Append(' ');
}

var typeName = GetRemappedTypeName(cursor, context: null, arg.AsType, out _);

if (typeName == "bool")
{
// bool is not blittable, so we shouldn't use it for P/Invoke signatures
typeName = "byte";
}

if (typeName.EndsWith("*"))
{
// Pointers are not yet supported as generic arguments; remap to IntPtr
typeName = "IntPtr";
_outputBuilder.AddUsingDirective("System");
}

nameBuilder.Append(typeName);

shouldWritePrecedingComma = true;
}

nameBuilder.Append('>');

name = nameBuilder.ToString();
}
else if (type is TagType tagType)
{
if (tagType.Decl.Handle.IsAnonymous)
Expand All @@ -1401,7 +1441,13 @@ private string GetTypeName(Cursor cursor, Cursor context, Type type, out string
}
else
{
// The default name should be correct
// The default name should be correct
}

if (name.Contains("::"))
{
name = name.Split(new string[] { "::" }, StringSplitOptions.RemoveEmptyEntries).Last();
name = GetRemappedName(name, cursor, tryRemapOperatorName: false);
}
}
else if (type is TypedefType typedefType)
Expand Down
10 changes: 5 additions & 5 deletions sources/ClangSharp/TemplateArgument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ internal TemplateArgument(Type parentType, uint index)
{
_parentType = parentType;
_index = index;
_asDecl = new Lazy<ValueDecl>(() => _parentDecl.TranslationUnit.GetOrCreate<ValueDecl>(_parentType.Handle.GetTemplateArgumentAsDecl(_index)));
_asExpr = new Lazy<Expr>(() => _parentDecl.TranslationUnit.GetOrCreate<Expr>(_parentType.Handle.GetTemplateArgumentAsExpr(_index)));
_asType = new Lazy<Type>(() => _parentDecl.TranslationUnit.GetOrCreate<Type>(_parentType.Handle.GetTemplateArgumentAsType(_index)));
_integralType = new Lazy<Type>(() => _parentDecl.TranslationUnit.GetOrCreate<Type>(_parentType.Handle.GetTemplateArgumentIntegralType(_index)));
_nullPtrType = new Lazy<Type>(() => _parentDecl.TranslationUnit.GetOrCreate<Type>(_parentType.Handle.GetTemplateArgumentNullPtrType(_index)));
_asDecl = new Lazy<ValueDecl>(() => _parentType.TranslationUnit.GetOrCreate<ValueDecl>(_parentType.Handle.GetTemplateArgumentAsDecl(_index)));
_asExpr = new Lazy<Expr>(() => _parentType.TranslationUnit.GetOrCreate<Expr>(_parentType.Handle.GetTemplateArgumentAsExpr(_index)));
_asType = new Lazy<Type>(() => _parentType.TranslationUnit.GetOrCreate<Type>(_parentType.Handle.GetTemplateArgumentAsType(_index)));
_integralType = new Lazy<Type>(() => _parentType.TranslationUnit.GetOrCreate<Type>(_parentType.Handle.GetTemplateArgumentIntegralType(_index)));
_nullPtrType = new Lazy<Type>(() => _parentType.TranslationUnit.GetOrCreate<Type>(_parentType.Handle.GetTemplateArgumentNullPtrType(_index)));
}

public ValueDecl AsDecl => _asDecl.Value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,64 @@ public static partial class Methods
await ValidateGeneratedBindingsAsync(inputContents, expectedOutputContents);
}

[Theory]
[InlineData("MyTemplate<int>", @"MyTemplate<int>", "")]
[InlineData("MyTemplate<bool>", @"[NativeTypeName(""MyTemplate<bool>"")] MyTemplate<byte>", "")]
[InlineData("MyTemplate<float*>", @"[NativeTypeName(""MyTemplate<float *>"")] MyTemplate<IntPtr>", "using System;\n")]
[InlineData("MyTemplate<void(*)(int)>", @"[NativeTypeName(""MyTemplate<void (*)(int)>"")] MyTemplate<IntPtr>", "using System;\n")]
public async Task TemplateParameterTest(string nativeParameter, string expectedManagedParameter, string expectedUsingStatement)
{
var inputContents = @$"template <typename T> struct MyTemplate;

void MyFunction({nativeParameter} myStruct);";

var expectedOutputContents = $@"{expectedUsingStatement}using System.Runtime.InteropServices;

namespace ClangSharp.Test
{{
public static partial class Methods
{{
[DllImport(""ClangSharpPInvokeGenerator"", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern void MyFunction({expectedManagedParameter} myStruct);
}}
}}
";

await ValidateGeneratedBindingsAsync(inputContents, expectedOutputContents, excludedNames: new[] { "MyTemplate" });
}


[Fact]
public async Task TemplateMemberTest()
{
var inputContents = @$"template <typename T> struct MyTemplate
{{
}};

struct MyStruct
{{
MyTemplate<float*> a;
}};
";

var expectedOutputContents = $@"using System;

namespace ClangSharp.Test
{{
public partial struct MyStruct
{{
[NativeTypeName(""MyTemplate<float *>"")]
public MyTemplate<IntPtr> a;
}}
}}
";

await ValidateGeneratedBindingsAsync(inputContents, expectedOutputContents, excludedNames: new[] { "MyTemplate" });
}




[Fact]
public async Task NoLibraryPathTest()
{
Expand Down