diff --git a/sources/ClangSharp.PInvokeGenerator/Abstractions/TransparentStructDesc.cs b/sources/ClangSharp.PInvokeGenerator/Abstractions/TransparentStructDesc.cs
new file mode 100644
index 00000000..51e85528
--- /dev/null
+++ b/sources/ClangSharp.PInvokeGenerator/Abstractions/TransparentStructDesc.cs
@@ -0,0 +1,13 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+namespace ClangSharp.Abstractions;
+
+internal struct TransparentStructDesc
+{
+ public string ParentName { get; set; }
+ public string Name { get; set; }
+ public string? NativeName { get; set; }
+ public string Type { get; set; }
+ public string? NativeType { get; set; }
+ public PInvokeGeneratorTransparentStructKind Kind { get; set; }
+}
diff --git a/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.cs b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.cs
index 30b8ccc1..7f78ea8a 100644
--- a/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.cs
+++ b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.cs
@@ -299,7 +299,7 @@ private void AddVtblIndexAttribute(long vtblIndex, string? prefix = null, string
}
}
- private void AddNativeTypeNameAttribute(string nativeTypeName, string? prefix = null, string? postfix = null, string? attributePrefix = null)
+ public void AddNativeTypeNameAttribute(string nativeTypeName, string? prefix = null, string? postfix = null, string? attributePrefix = null)
{
foreach (var entry in _generator.Config.NativeTypeNamesToStrip)
{
diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs
index c0e54ee8..0b78a631 100644
--- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs
+++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs
@@ -717,7 +717,7 @@ private void VisitFunctionDecl(FunctionDecl functionDecl)
{
outputBuilder.Write("Base");
}
-
+
outputBuilder.Write('.');
outputBuilder.Write(name);
outputBuilder.Write('(');
@@ -3194,7 +3194,8 @@ private void VisitTypedefDecl(TypedefDecl typedefDecl, bool onlyHandleRemappings
void ForFunctionProtoType(TypedefDecl typedefDecl, FunctionProtoType functionProtoType, Type? parentType, bool onlyHandleRemappings)
{
- if (!_config.ExcludeFnptrCodegen || onlyHandleRemappings)
+ var hasOutput = _config.GenerateFnPtrWrapper || _config.ExcludeFnptrCodegen;
+ if (!hasOutput || onlyHandleRemappings)
{
return;
}
@@ -3202,14 +3203,35 @@ void ForFunctionProtoType(TypedefDecl typedefDecl, FunctionProtoType functionPro
var name = GetRemappedCursorName(typedefDecl);
var escapedName = EscapeName(name);
- var callingConventionName = GetCallingConvention(typedefDecl, context: null, typedefDecl.TypeForDecl);
+ StartUsingOutputBuilder(name);
+ Debug.Assert(_outputBuilder is not null);
- var returnType = functionProtoType.ReturnType;
- var returnTypeName = GetRemappedTypeName(typedefDecl, context: null, returnType, out var nativeTypeName);
+ if (_config.GenerateFnPtrWrapper)
+ {
+ var type = GetTypeName(typedefDecl, null, typedefDecl.TypeForDecl, true, false, out var nativeName);
+ _ = GetTypeName(typedefDecl, null, typedefDecl.UnderlyingType, true, false, out var nativeType);
- StartUsingOutputBuilder(name);
+ if (IsNativeTypeNameEquivalent(nativeName, name))
+ {
+ nativeName = null;
+ }
+
+ var desc = new TransparentStructDesc() {
+ ParentName = name,
+ Name = escapedName,
+ NativeName = nativeName,
+ Type = type,
+ NativeType = nativeType,
+ Kind = PInvokeGeneratorTransparentStructKind.FnPtr
+ };
+ GenerateTransparentStruct(desc);
+ }
+ else
{
- Debug.Assert(_outputBuilder is not null);
+ var callingConventionName = GetCallingConvention(typedefDecl, context: null, typedefDecl.TypeForDecl);
+
+ var returnType = functionProtoType.ReturnType;
+ var returnTypeName = GetRemappedTypeName(typedefDecl, context: null, returnType, out var nativeTypeName);
var desc = new FunctionOrDelegateDesc {
AccessSpecifier = GetAccessSpecifier(typedefDecl, matchStar: true),
diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs
index 08320b66..c1af5ff6 100644
--- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs
+++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs
@@ -513,7 +513,7 @@ static void GenerateDisableRuntimeMarshallingAttribute(PInvokeGenerator generato
sw.WriteLine("using System.Runtime.CompilerServices;");
sw.WriteLine();
sw.WriteLine("[assembly: DisableRuntimeMarshalling]");
-
+
if (!leaveStreamOpen)
{
stream = null;
@@ -972,408 +972,404 @@ static void GenerateTransparentStructs(PInvokeGenerator generator, Stream? strea
var type = transparentStruct.Value.Name;
var kind = transparentStruct.Value.Kind;
- var isTypePointer = type.Contains('*', StringComparison.Ordinal);
+ generator.StartUsingOutputBuilder(name);
- if (stream is null)
- {
- var outputPath = Path.Combine(config.OutputLocation, $"{name}.cs");
- stream = generator._outputStreamFactory(outputPath);
- }
-
- using var sw = new StreamWriter(stream, s_defaultStreamWriterEncoding, DefaultStreamWriterBufferSize, leaveStreamOpen);
- sw.NewLine = "\n";
+ var desc = new TransparentStructDesc() {
+ ParentName = name,
+ Name = name,
+ Type = type,
+ Kind = kind
+ };
+ generator.GenerateTransparentStruct(desc);
- if (!string.IsNullOrEmpty(config.HeaderText))
- {
- sw.WriteLine(config.HeaderText);
- }
+ generator.StopUsingOutputBuilder();
+ }
+ }
+ }
- var indentString = " ";
- var targetNamespace = generator.GetNamespace(name);
+ private void GenerateTransparentStruct(in TransparentStructDesc desc)
+ {
+ var name = desc.Name;
+ var type = desc.Type;
+ var kind = desc.Kind;
- sw.WriteLine("using System;");
+ var isTypePointer = type.Contains('*', StringComparison.Ordinal);
- if (kind == PInvokeGeneratorTransparentStructKind.HandleWin32)
- {
- var handleNamespace = generator.GetNamespace("HANDLE");
+ var sw = StartCSharpCode();
- if (targetNamespace != handleNamespace)
- {
- sw.Write("using ");
- sw.Write(handleNamespace);
- sw.WriteLine(';');
- }
- }
+ sw.EmitSystemSupport();
- sw.WriteLine();
-
- sw.Write("namespace ");
- sw.Write(targetNamespace);
-
- if (generator.Config.GenerateFileScopedNamespaces)
- {
- sw.WriteLine(';');
- sw.WriteLine();
- indentString = "";
- }
- else
- {
- sw.WriteLine();
- sw.WriteLine('{');
- }
+ if (kind == PInvokeGeneratorTransparentStructKind.HandleWin32)
+ {
+ var targetNamespace = GetNamespace(name);
+ var handleNamespace = GetNamespace("HANDLE");
- sw.Write(indentString);
- sw.Write("public readonly ");
+ if (targetNamespace != handleNamespace)
+ {
+ sw.AddUsingDirective(handleNamespace);
+ }
+ }
- if (isTypePointer || IsTransparentStructHexBased(kind))
- {
- sw.Write("unsafe ");
- }
+ if (Config.GenerateDocIncludes)
+ {
+ sw.WriteIndented("/// ");
+ }
- sw.Write("partial struct ");
- sw.Write(name);
- sw.Write(" : IComparable, IComparable<");
- sw.Write(name);
- sw.Write(">, IEquatable<");
- sw.Write(name);
- sw.WriteLine(">, IFormattable");
+ if (desc.NativeName is not null)
+ {
+ sw.AddNativeTypeNameAttribute(desc.NativeName);
+ }
- sw.Write(indentString);
- sw.WriteLine('{');
+ sw.WriteIndented("public readonly ");
- sw.Write(indentString);
- sw.Write(" public readonly ");
- sw.Write(type);
- sw.WriteLine(" Value;");
- sw.WriteLine();
+ if (isTypePointer || IsTransparentStructHexBased(kind))
+ {
+ sw.Write("unsafe ");
+ }
- // All transparent structs be created directly from the underlying type
+ sw.Write("partial struct ");
+ sw.Write(name);
+ sw.Write(" : ");
+ if (IsTransparentStructComparable(kind))
+ {
+ sw.Write("IComparable, IComparable<");
+ sw.Write(name);
+ sw.Write(">, IEquatable<");
+ sw.Write(name);
+ sw.Write(">, ");
+ }
+ sw.WriteLine("IFormattable");
- sw.Write(indentString);
- sw.Write(" public ");
- sw.Write(name);
- sw.Write('(');
- sw.Write(type);
- sw.WriteLine(" value)");
- sw.Write(indentString);
- sw.WriteLine(" {");
- sw.Write(indentString);
- sw.WriteLine(" Value = value;");
- sw.Write(indentString);
- sw.WriteLine(" }");
- sw.WriteLine();
+ sw.WriteBlockStart();
+ {
+ if (desc.NativeType is not null)
+ {
+ sw.AddNativeTypeNameAttribute(desc.NativeType);
+ }
+ sw.WriteIndented("public readonly ");
+ sw.Write(type);
+ sw.WriteLine(" Value;");
+ sw.WriteDivider();
- if (IsTransparentStructHandle(kind) || (kind == PInvokeGeneratorTransparentStructKind.HandleVulkan))
- {
- // Handle like transparent structs define a NULL member
+ // All transparent structs be created directly from the underlying type
- if (kind == PInvokeGeneratorTransparentStructKind.HandleWin32)
- {
- sw.Write(indentString);
- sw.Write(" public static ");
- sw.Write(name);
- sw.Write(" INVALID_VALUE => new ");
- sw.Write(name);
+ sw.WriteIndented("public ");
+ sw.Write(name);
+ sw.Write('(');
+ sw.Write(type);
+ sw.WriteLine(" value)");
+ sw.WriteBlockStart();
+ sw.WriteIndentedLine("Value = value;");
+ sw.WriteBlockEnd();
- if (isTypePointer)
- {
- sw.Write("((");
- sw.Write(type);
- sw.WriteLine(")(-1));");
- }
- else
- {
- sw.WriteLine("(-1);");
- }
+ sw.WriteDivider();
- sw.WriteLine();
- }
+ if (IsTransparentStructHandle(kind) || (kind == PInvokeGeneratorTransparentStructKind.HandleVulkan))
+ {
+ // Handle like transparent structs define a NULL member
- sw.Write(indentString);
- sw.Write(" public static ");
+ if (kind == PInvokeGeneratorTransparentStructKind.HandleWin32)
+ {
+ sw.WriteIndented("public static ");
sw.Write(name);
- sw.Write(" NULL => new ");
+ sw.Write(" INVALID_VALUE => new ");
sw.Write(name);
if (isTypePointer)
{
- sw.WriteLine("(null);");
+ sw.Write("((");
+ sw.Write(type);
+ sw.WriteLine(")(-1));");
}
else
{
- sw.WriteLine("(0);");
+ sw.WriteLine("(-1);");
}
- sw.WriteLine();
+ sw.WriteDivider();
}
- else if (IsTransparentStructBoolean(kind))
- {
- // Boolean like transparent structs define FALSE and TRUE members
- sw.Write(indentString);
- sw.Write(" public static ");
- sw.Write(name);
- sw.Write(" FALSE => new ");
- sw.Write(name);
- sw.WriteLine("(0);");
- sw.WriteLine();
+ sw.WriteIndented("public static ");
+ sw.Write(name);
+ sw.Write(" NULL => new ");
+ sw.Write(name);
- sw.Write(indentString);
- sw.Write(" public static ");
- sw.Write(name);
- sw.Write(" TRUE => new ");
- sw.Write(name);
- sw.WriteLine("(1);");
- sw.WriteLine();
+ if (isTypePointer)
+ {
+ sw.WriteLine("(null);");
+ }
+ else
+ {
+ sw.WriteLine("(0);");
}
- // All transparent structs support equality and relational comparisons with themselves
+ sw.WriteDivider();
+ }
+ else if (IsTransparentStructBoolean(kind))
+ {
+ // Boolean like transparent structs define FALSE and TRUE members
+
+ sw.WriteIndented("public static ");
+ sw.Write(name);
+ sw.Write(" FALSE => new ");
+ sw.Write(name);
+ sw.WriteLine("(0);");
+ sw.WriteDivider();
- sw.Write(indentString);
- sw.Write(" public static bool operator ==(");
+ sw.WriteIndented("public static ");
+ sw.Write(name);
+ sw.Write(" TRUE => new ");
+ sw.Write(name);
+ sw.WriteLine("(1);");
+ sw.WriteDivider();
+ }
+
+ if (IsTransparentStructComparable(kind))
+ {
+ // Non-FnPtr transparent structs support equality and relational comparisons with themselves
+
+ sw.WriteIndented("public static bool operator ==(");
sw.Write(name);
sw.Write(" left, ");
sw.Write(name);
sw.WriteLine(" right) => left.Value == right.Value;");
- sw.WriteLine();
+ sw.WriteDivider();
- sw.Write(indentString);
- sw.Write(" public static bool operator !=(");
+ sw.WriteIndented("public static bool operator !=(");
sw.Write(name);
sw.Write(" left, ");
sw.Write(name);
sw.WriteLine(" right) => left.Value != right.Value;");
- sw.WriteLine();
+ sw.WriteDivider();
- sw.Write(indentString);
- sw.Write(" public static bool operator <(");
+ sw.WriteIndented("public static bool operator <(");
sw.Write(name);
sw.Write(" left, ");
sw.Write(name);
sw.WriteLine(" right) => left.Value < right.Value;");
- sw.WriteLine();
+ sw.WriteDivider();
- sw.Write(indentString);
- sw.Write(" public static bool operator <=(");
+ sw.WriteIndented("public static bool operator <=(");
sw.Write(name);
sw.Write(" left, ");
sw.Write(name);
sw.WriteLine(" right) => left.Value <= right.Value;");
- sw.WriteLine();
+ sw.WriteDivider();
- sw.Write(indentString);
- sw.Write(" public static bool operator >(");
+ sw.WriteIndented("public static bool operator >(");
sw.Write(name);
sw.Write(" left, ");
sw.Write(name);
sw.WriteLine(" right) => left.Value > right.Value;");
- sw.WriteLine();
+ sw.WriteDivider();
- sw.Write(indentString);
- sw.Write(" public static bool operator >=(");
+ sw.WriteIndented("public static bool operator >=(");
sw.Write(name);
sw.Write(" left, ");
sw.Write(name);
sw.WriteLine(" right) => left.Value >= right.Value;");
- sw.WriteLine();
+ sw.WriteDivider();
+ }
- if (IsTransparentStructHandle(kind))
- {
- // Handle like transparent structs can be cast to/from void*
+ if (IsTransparentStructHandle(kind))
+ {
+ // Handle like transparent structs can be cast to/from void*
- sw.Write(indentString);
- sw.Write(" public static explicit operator ");
- sw.Write(name);
- sw.Write("(void* value) => new ");
- sw.Write(name);
+ sw.WriteIndented("public static explicit operator ");
+ sw.Write(name);
+ sw.Write("(void* value) => new ");
+ sw.Write(name);
- if (type.Equals("void*", StringComparison.Ordinal))
+ if (type.Equals("void*", StringComparison.Ordinal))
+ {
+ sw.WriteLine("(value);");
+ }
+ else
+ {
+ if (!IsUnsigned(type))
{
- sw.WriteLine("(value);");
+ sw.Write("unchecked");
}
- else
- {
- if (!IsUnsigned(type))
- {
- sw.Write("unchecked");
- }
- sw.Write("((");
- sw.Write(type);
- sw.WriteLine(")(value));");
- }
- sw.WriteLine();
+ sw.Write("((");
+ sw.Write(type);
+ sw.WriteLine(")(value));");
+ }
+ sw.WriteDivider();
- sw.Write(indentString);
- sw.Write(" public static implicit operator void*(");
- sw.Write(name);
+ sw.WriteIndented("public static implicit operator void*(");
+ sw.Write(name);
- if (isTypePointer)
+ if (isTypePointer)
+ {
+ sw.WriteLine(" value) => value.Value;");
+ }
+ else
+ {
+ var isUnchecked = !IsUnsigned(type);
+ sw.Write(" value) => ");
+
+ if (isUnchecked)
{
- sw.WriteLine(" value) => value.Value;");
+ sw.Write("unchecked(");
}
- else
+ sw.Write("(void*)(value.Value)");
+
+ if (isUnchecked)
{
- var isUnchecked = !IsUnsigned(type);
- sw.Write(" value) => ");
+ sw.Write(")");
+ }
+ sw.WriteDivider();
+ }
- if (isUnchecked)
- {
- sw.Write("unchecked(");
- }
- sw.Write("(void*)(value.Value)");
+ sw.WriteDivider();
- if (isUnchecked)
- {
- sw.Write(")");
- }
- sw.WriteLine();
- }
+ if ((kind == PInvokeGeneratorTransparentStructKind.HandleWin32) && !name.Equals("HANDLE", StringComparison.Ordinal))
+ {
+ // Win32 handle like transparent structs can also be cast to/from HANDLE
- sw.WriteLine();
+ sw.WriteIndented("public static explicit operator ");
+ sw.Write(name);
+ sw.Write("(HANDLE value) => new ");
+ sw.Write(name);
+ sw.WriteLine("(value);");
+ sw.WriteDivider();
- if ((kind == PInvokeGeneratorTransparentStructKind.HandleWin32) && !name.Equals("HANDLE", StringComparison.Ordinal))
- {
- // Win32 handle like transparent structs can also be cast to/from HANDLE
-
- sw.Write(indentString);
- sw.Write(" public static explicit operator ");
- sw.Write(name);
- sw.Write("(HANDLE value) => new ");
- sw.Write(name);
- sw.WriteLine("(value);");
- sw.WriteLine();
+ sw.WriteIndented("public static implicit operator HANDLE(");
+ sw.Write(name);
+ sw.WriteLine(" value) => new HANDLE(value.Value);");
+ sw.WriteDivider();
+ }
+ }
+ else if (IsTransparentStructBoolean(kind))
+ {
+ // Boolean like transparent structs define conversion to/from bool
+ // and support for usage in bool like scenarios.
- sw.Write(indentString);
- sw.Write(" public static implicit operator HANDLE(");
- sw.Write(name);
- sw.WriteLine(" value) => new HANDLE(value.Value);");
- sw.WriteLine();
- }
+ sw.WriteIndented("public static implicit operator bool(");
+ sw.Write(name);
+ sw.WriteLine(" value) => value.Value != 0;");
+ sw.WriteDivider();
+
+ sw.WriteIndented("public static implicit operator ");
+ sw.Write(name);
+ sw.Write("(bool value) => new ");
+ sw.Write(name);
+
+ if (type.Equals("int", StringComparison.Ordinal))
+ {
+ sw.WriteLine("(value ? 1 : 0);");
+ }
+ else if (type.Equals("uint", StringComparison.Ordinal))
+ {
+ sw.WriteLine("(value ? 1u : 0u);");
}
- else if (IsTransparentStructBoolean(kind))
+ else
{
- // Boolean like transparent structs define conversion to/from bool
- // and support for usage in bool like scenarios.
+ sw.Write("((");
+ sw.Write(type);
+ sw.WriteLine(")(value ? 1u : 0u);");
+ }
- sw.Write(indentString);
- sw.Write(" public static implicit operator bool(");
- sw.Write(name);
- sw.WriteLine(" value) => value.Value != 0;");
- sw.WriteLine();
+ sw.WriteDivider();
- sw.Write(indentString);
- sw.Write(" public static implicit operator ");
- sw.Write(name);
- sw.Write("(bool value) => new ");
- sw.Write(name);
+ sw.WriteIndented("public static bool operator false(");
+ sw.Write(name);
+ sw.WriteLine(" value) => value.Value == 0;");
+ sw.WriteDivider();
- if (type.Equals("int", StringComparison.Ordinal))
- {
- sw.WriteLine("(value ? 1 : 0);");
- }
- else if (type.Equals("uint", StringComparison.Ordinal))
- {
- sw.WriteLine("(value ? 1u : 0u);");
- }
- else
- {
- sw.Write("((");
- sw.Write(type);
- sw.WriteLine(")(value ? 1u : 0u);");
- }
+ sw.WriteIndented("public static bool operator true(");
+ sw.Write(name);
+ sw.WriteLine(" value) => value.Value != 0;");
+ sw.WriteDivider();
+ }
- sw.WriteLine();
+ // All transparent structs define casts to/from the various integer types
- sw.Write(indentString);
- sw.Write(" public static bool operator false(");
- sw.Write(name);
- sw.WriteLine(" value) => value.Value == 0;");
- sw.WriteLine();
+ if (kind == PInvokeGeneratorTransparentStructKind.FnPtr)
+ {
+ OutputConversions(sw, name, type, kind, type);
+ }
+ else
+ {
+ OutputConversions(sw, name, type, kind, "byte");
+ OutputConversions(sw, name, type, kind, "short");
+ OutputConversions(sw, name, type, kind, "sbyte");
+ OutputConversions(sw, name, type, kind, "ushort");
+ OutputConversions(sw, name, type, kind, "int");
+ OutputConversions(sw, name, type, kind, "long");
+ OutputConversions(sw, name, type, kind, "uint");
+ OutputConversions(sw, name, type, kind, "ulong");
+ }
- sw.Write(indentString);
- sw.Write(" public static bool operator true(");
- sw.Write(name);
- sw.WriteLine(" value) => value.Value != 0;");
- sw.WriteLine();
- }
+ OutputConversions(sw, name, type, kind, "nint");
+ OutputConversions(sw, name, type, kind, "nuint");
- // All transparent structs define casts to/from the various integer types
+ if (IsTransparentStructComparable(kind))
+ {
+ // Non-FnPtr transparent structs override CompareTo, Equals, GetHashCode
- OutputConversions(sw, indentString, name, type, kind, "byte");
- OutputConversions(sw, indentString, name, type, kind, "short");
- OutputConversions(sw, indentString, name, type, kind, "int");
- OutputConversions(sw, indentString, name, type, kind, "long");
- OutputConversions(sw, indentString, name, type, kind, "nint");
- OutputConversions(sw, indentString, name, type, kind, "sbyte");
- OutputConversions(sw, indentString, name, type, kind, "ushort");
- OutputConversions(sw, indentString, name, type, kind, "uint");
- OutputConversions(sw, indentString, name, type, kind, "ulong");
- OutputConversions(sw, indentString, name, type, kind, "nuint");
+ sw.WriteIndentedLine("public int CompareTo(object? obj)");
+ sw.WriteBlockStart();
+ {
+ sw.WriteIndented("if (obj is ");
+ sw.Write(name);
+ sw.WriteLine(" other)");
+ sw.WriteBlockStart();
+ sw.WriteIndentedLine("return CompareTo(other);");
+ sw.WriteBlockEnd();
- // All transparent structs override CompareTo, Equals, GetHashCode, and ToString
+ sw.WriteDivider();
- sw.Write(indentString);
- sw.WriteLine(" public int CompareTo(object? obj)");
- sw.Write(indentString);
- sw.WriteLine(" {");
- sw.Write(indentString);
- sw.Write(" if (obj is ");
- sw.Write(name);
- sw.WriteLine(" other)");
- sw.Write(indentString);
- sw.WriteLine(" {");
- sw.Write(indentString);
- sw.WriteLine(" return CompareTo(other);");
- sw.Write(indentString);
- sw.WriteLine(" }");
- sw.WriteLine();
- sw.Write(indentString);
- sw.Write(" return (obj is null) ? 1 : throw new ArgumentException(\"obj is not an instance of ");
- sw.Write(name);
- sw.WriteLine(".\");");
- sw.Write(indentString);
- sw.WriteLine(" }");
- sw.WriteLine();
+ sw.WriteIndented("return (obj is null) ? 1 : throw new ArgumentException(\"obj is not an instance of ");
+ sw.Write(name);
+ sw.WriteLine(".\");");
+ }
+ sw.WriteBlockEnd();
+
+ sw.WriteDivider();
- sw.Write(indentString);
- sw.Write(" public int CompareTo(");
+ sw.WriteIndented("public int CompareTo(");
sw.Write(name);
+ sw.Write(" other) => ");
if (isTypePointer)
{
- sw.WriteLine(" other) => ((nuint)(Value)).CompareTo((nuint)(other.Value));");
+ sw.WriteLine("((nuint)(Value)).CompareTo((nuint)(other.Value));");
}
else
{
- sw.WriteLine(" other) => Value.CompareTo(other.Value);");
+ sw.WriteLine("Value.CompareTo(other.Value);");
}
- sw.WriteLine();
+ sw.WriteDivider();
- sw.Write(indentString);
- sw.Write(" public override bool Equals(object? obj) => (obj is ");
+ sw.WriteIndented("public override bool Equals(object? obj) => (obj is ");
sw.Write(name);
sw.WriteLine(" other) && Equals(other);");
- sw.WriteLine();
- sw.Write(indentString);
- sw.Write(" public bool Equals(");
+ sw.WriteDivider();
+
+ sw.WriteIndented("public bool Equals(");
sw.Write(name);
+ sw.Write(" other) => ");
if (isTypePointer)
{
- sw.WriteLine(" other) => ((nuint)(Value)).Equals((nuint)(other.Value));");
+ sw.WriteLine("((nuint)(Value)).Equals((nuint)(other.Value));");
}
else
{
- sw.WriteLine(" other) => Value.Equals(other.Value);");
+ sw.WriteLine("Value.Equals(other.Value);");
}
- sw.WriteLine();
+ sw.WriteDivider();
- sw.Write(indentString);
- sw.Write(" public override int GetHashCode() => ");
+ sw.WriteIndented("public override int GetHashCode() => ");
if (isTypePointer)
{
@@ -1385,122 +1381,122 @@ static void GenerateTransparentStructs(PInvokeGenerator generator, Stream? strea
}
sw.WriteLine(".GetHashCode();");
- sw.WriteLine();
- sw.Write(indentString);
- sw.Write(" public override string ToString() => ");
+ sw.WriteDivider();
+ }
- if (isTypePointer)
- {
- sw.Write("((nuint)(Value))");
- }
- else
- {
- sw.Write("Value");
- }
+ // All transparent structs override ToString
- sw.Write(".ToString(");
+ sw.WriteIndented("public override string ToString() => ");
- if (IsTransparentStructHexBased(kind))
- {
- var (typeSrcSize, typeDstSize, typeSign) = GetSizeAndSignOf(type);
+ if (isTypePointer)
+ {
+ sw.Write("((nuint)(Value))");
+ }
+ else
+ {
+ sw.Write("Value");
+ }
- if (typeSrcSize != typeDstSize)
- {
- sw.Write("(sizeof(nint) == 4) ? \"X8\" : \"X16\"");
- }
- else
- {
- sw.Write('"');
- sw.Write('X');
- sw.Write(typeSrcSize * 2);
- sw.Write('"');
- }
- }
+ sw.Write(".ToString(");
- sw.WriteLine(");");
- sw.WriteLine();
-
- sw.Write(indentString);
- sw.Write(" public string ToString(string? format, IFormatProvider? formatProvider) => ");
+ if (IsTransparentStructHexBased(kind))
+ {
+ var (typeSrcSize, typeDstSize, typeSign) = GetSizeAndSignOf(type);
- if (isTypePointer)
+ if (typeSrcSize != typeDstSize)
{
- sw.Write("((nuint)(Value))");
+ sw.Write("(sizeof(nint) == 4) ? \"X8\" : \"X16\"");
}
else
{
- sw.Write("Value");
+ sw.Write('"');
+ sw.Write('X');
+ sw.Write(typeSrcSize * 2);
+ sw.Write('"');
}
+ }
- sw.WriteLine(".ToString(format, formatProvider);");
+ sw.WriteLine(");");
- sw.Write(indentString);
- sw.WriteLine('}');
+ sw.WriteDivider();
- if (!generator.Config.GenerateFileScopedNamespaces)
- {
- sw.WriteLine('}');
- }
+ sw.WriteIndented("public string ToString(string? format, IFormatProvider? formatProvider) => ");
- if (!leaveStreamOpen)
- {
- stream = null;
- }
+ if (isTypePointer)
+ {
+ sw.Write("((nuint)(Value))");
}
-
- static (int srcSize, int dstSize, int sign) GetSizeAndSignOf(string type)
+ else
{
- if (type.Contains('*', StringComparison.Ordinal))
- {
- return (8, 4, +1);
- }
-
- return type switch {
- "sbyte" => (1, 1, -1),
- "byte" => (1, 1, +1),
- "short" => (2, 2, -1),
- "ushort" => (2, 2, +1),
- "int" => (4, 4, -1),
- "uint" => (4, 4, +1),
- "nint" => (8, 4, -1),
- "nuint" => (8, 4, +1),
- "long" => (8, 8, -1),
- "ulong" => (8, 8, +1),
- _ => (0, 0, 0),
- };
+ sw.Write("Value");
}
- static void OutputConversions(StreamWriter sw, string indentString, string name, string type, PInvokeGeneratorTransparentStructKind kind, string target)
+ sw.WriteLine(".ToString(format, formatProvider);");
+ }
+ sw.WriteBlockEnd();
+
+ StopCSharpCode();
+
+ static (int srcSize, int dstSize, int sign) GetSizeAndSignOf(string type)
+ {
+ if (type.Contains('*', StringComparison.Ordinal))
{
- var (typeSrcSize, typeDstSize, typeSign) = GetSizeAndSignOf(type);
- var (targetSrcSize, targetDstSize, targetSign) = GetSizeAndSignOf(target);
+ return (8, 4, +1);
+ }
- var isTypePointer = type.Contains('*', StringComparison.Ordinal);
- var isPointerToNativeCast = (isTypePointer && target.Equals("nint", StringComparison.Ordinal)) || (isTypePointer && target.Equals("nuint", StringComparison.Ordinal));
+ return type switch {
+ "sbyte" => (1, 1, -1),
+ "byte" => (1, 1, +1),
+ "short" => (2, 2, -1),
+ "ushort" => (2, 2, +1),
+ "int" => (4, 4, -1),
+ "uint" => (4, 4, +1),
+ "nint" => (8, 4, -1),
+ "nuint" => (8, 4, +1),
+ "long" => (8, 8, -1),
+ "ulong" => (8, 8, +1),
+ _ => (0, 0, 0),
+ };
+ }
- // public static castFromKind operator name(target value) => new name((type)(value));
+ static void OutputConversions(CSharpOutputBuilder sw, string name, string type, PInvokeGeneratorTransparentStructKind kind, string target)
+ {
+ var (typeSrcSize, typeDstSize, typeSign) = GetSizeAndSignOf(type);
+ var (targetSrcSize, targetDstSize, targetSign) = GetSizeAndSignOf(target);
- var castFromKind = "implicit";
- var areEquivalentTypeAndTarget = (type == target) || isPointerToNativeCast
- || (type.Equals("nint", StringComparison.Ordinal) && target.Equals("int", StringComparison.Ordinal))
- || (type.Equals("nuint", StringComparison.Ordinal) && target.Equals("uint", StringComparison.Ordinal))
- || (type.Equals("long", StringComparison.Ordinal) && target.Equals("nint", StringComparison.Ordinal))
- || (type.Equals("ulong", StringComparison.Ordinal) && target.Equals("nuint", StringComparison.Ordinal));
+ var isTypePointer = type.Contains('*', StringComparison.Ordinal);
+ var isPointerToNativeCast = (isTypePointer && target.Equals("nint", StringComparison.Ordinal)) || (isTypePointer && target.Equals("nuint", StringComparison.Ordinal));
- if (((typeDstSize <= targetSrcSize) && !areEquivalentTypeAndTarget) || ((targetSign == -1) && (typeSign == +1)) || IsTransparentStructHandle(kind))
- {
- castFromKind = "explicit";
- }
+ // public static castFromKind operator name(target value) => new name((type)(value));
- sw.Write(indentString);
- sw.Write(" public static ");
- sw.Write(castFromKind);
- sw.Write(" operator ");
- sw.Write(name);
- sw.Write('(');
- sw.Write(target);
- sw.Write(" value) => new ");
+ var castFromKind = "implicit";
+ var areEquivalentType = type == target;
+ var areEquivalentTypeAndTarget = areEquivalentType || isPointerToNativeCast
+ || (type.Equals("nint", StringComparison.Ordinal) && target.Equals("int", StringComparison.Ordinal))
+ || (type.Equals("nuint", StringComparison.Ordinal) && target.Equals("uint", StringComparison.Ordinal))
+ || (type.Equals("long", StringComparison.Ordinal) && target.Equals("nint", StringComparison.Ordinal))
+ || (type.Equals("ulong", StringComparison.Ordinal) && target.Equals("nuint", StringComparison.Ordinal));
+
+ var isForcedExplicit = !areEquivalentType && (kind == PInvokeGeneratorTransparentStructKind.FnPtr);
+
+ var isDowncast = (typeDstSize <= targetSrcSize) && !areEquivalentTypeAndTarget;
+ var isSignChange = (targetSign == -1) && (typeSign == +1);
+ if (isForcedExplicit || isDowncast || isSignChange || IsTransparentStructHandle(kind))
+ {
+ castFromKind = "explicit";
+ }
+
+ sw.WriteIndented("public static ");
+ sw.Write(castFromKind);
+ sw.Write(" operator ");
+ sw.Write(name);
+ sw.Write('(');
+ sw.Write(target);
+ sw.Write(" value)");
+ sw.BeginBody(isExpressionBody: true);
+ {
+ sw.Write("new ");
sw.Write(name);
sw.Write('(');
@@ -1518,32 +1514,37 @@ static void OutputConversions(StreamWriter sw, string indentString, string name,
sw.Write("))");
}
- sw.WriteLine(");");
- sw.WriteLine();
+ sw.Write(")");
+ sw.WriteSemicolon();
+ }
+ sw.EndBody(true);
+ sw.WriteDivider();
- // public static castToKind operator target(name value) => ((target)(value.Value));
+ // public static castToKind operator target(name value) => ((target)(value.Value));
- var castToKind = "implicit";
- areEquivalentTypeAndTarget = (type == target) || isPointerToNativeCast
- || (type.Equals("int", StringComparison.Ordinal) && target.Equals("nint", StringComparison.Ordinal))
- || (type.Equals("uint", StringComparison.Ordinal) && target.Equals("nuint", StringComparison.Ordinal))
- || (type.Equals("nint", StringComparison.Ordinal) && target.Equals("long", StringComparison.Ordinal))
- || (type.Equals("nuint", StringComparison.Ordinal) && target.Equals("ulong", StringComparison.Ordinal));
+ var castToKind = "implicit";
+ areEquivalentTypeAndTarget = (type == target) || isPointerToNativeCast
+ || (type.Equals("int", StringComparison.Ordinal) && target.Equals("nint", StringComparison.Ordinal))
+ || (type.Equals("uint", StringComparison.Ordinal) && target.Equals("nuint", StringComparison.Ordinal))
+ || (type.Equals("nint", StringComparison.Ordinal) && target.Equals("long", StringComparison.Ordinal))
+ || (type.Equals("nuint", StringComparison.Ordinal) && target.Equals("ulong", StringComparison.Ordinal));
- if (((targetDstSize <= typeSrcSize) && !areEquivalentTypeAndTarget) || ((typeSign == -1) && (targetSign == +1)))
- {
- castToKind = "explicit";
- }
-
- sw.Write(indentString);
- sw.Write(" public static ");
- sw.Write(castToKind);
- sw.Write(" operator ");
- sw.Write(target);
- sw.Write('(');
- sw.Write(name);
- sw.Write(" value) => ");
+ isDowncast = (targetDstSize <= typeSrcSize) && !areEquivalentTypeAndTarget;
+ isSignChange = (typeSign == -1) && (targetSign == +1);
+ if (isForcedExplicit || isDowncast || isSignChange)
+ {
+ castToKind = "explicit";
+ }
+ sw.WriteIndented("public static ");
+ sw.Write(castToKind);
+ sw.Write(" operator ");
+ sw.Write(target);
+ sw.Write('(');
+ sw.Write(name);
+ sw.Write(" value)");
+ sw.BeginBody(isExpressionBody: true);
+ {
if (castToKind.Equals("explicit", StringComparison.Ordinal) || isPointerToNativeCast)
{
sw.Write('(');
@@ -1558,9 +1559,10 @@ static void OutputConversions(StreamWriter sw, string indentString, string name,
sw.Write(')');
}
- sw.WriteLine(';');
- sw.WriteLine();
+ sw.WriteSemicolon();
}
+ sw.EndBody(true);
+ sw.WriteDivider();
}
}
@@ -3117,7 +3119,6 @@ private string GetRemappedName(string name, Cursor? cursor, bool tryRemapOperato
if (_config.RemappedNames.TryGetValue(tmpName, out remappedName))
{
-
wasRemapped = true;
_ = _usedRemappings.Add(tmpName);
return AddUsingDirectiveIfNeeded(_outputBuilder, remappedName, skipUsing);
@@ -3765,6 +3766,12 @@ private string GetTypeName(Cursor? cursor, Cursor? context, Type rootType, Type
// platform size, based on whatever parameters were passed into clang.
var remappedName = GetRemappedName(result.typeName, cursor, tryRemapOperatorName: false, out var wasRemapped, skipUsing: true);
+
+ if (_config.GenerateFnPtrWrapper && !ignoreTransparentStructsWhereRequired && IsFunctionPointer(cursor, typedefType))
+ {
+ wasRemapped = true;
+ }
+
result.typeName = wasRemapped ? remappedName : GetTypeName(cursor, context, rootType, typedefType.Decl.UnderlyingType, ignoreTransparentStructsWhereRequired, isTemplate, out _);
}
else if (type is UsingType usingType)
@@ -3789,6 +3796,39 @@ private string GetTypeName(Cursor? cursor, Cursor? context, Type rootType, Type
nativeTypeName = result.nativeTypeName;
return result.typeName;
+
+ bool IsFunctionPointer(Cursor? cursor, Type underlyingType)
+ {
+ bool ForPointeeType(Cursor? cursor, Type pointeeType)
+ {
+ if (IsType(cursor, pointeeType, out _))
+ {
+ return true;
+ }
+
+ // Do not recurse if the pointee is a pointer,
+ // we do not want to detect pointers to function pointers.
+ return false;
+ }
+
+ if (IsType(cursor, underlyingType, out var pointerType))
+ {
+ return ForPointeeType(cursor, pointerType.PointeeType);
+ }
+ else if (IsType(cursor, underlyingType, out var referenceType))
+ {
+ return ForPointeeType(cursor, referenceType.PointeeType);
+ }
+ else if (IsType(cursor, underlyingType, out var templateSpecializationType))
+ {
+ if (templateSpecializationType.IsTypeAlias)
+ {
+ return IsFunctionPointer(cursor, templateSpecializationType.AliasedType);
+ }
+ }
+
+ return false;
+ }
}
private string GetTypeNameForPointeeType(Cursor? cursor, Cursor? context, Type rootType, Type pointeeType, bool ignoreTransparentStructsWhereRequired, bool isTemplate, out string nativePointeeTypeName, out bool isAdjusted)
@@ -5403,13 +5443,16 @@ private static bool IsTransparentStructBoolean(PInvokeGeneratorTransparentStruct
=> kind is PInvokeGeneratorTransparentStructKind.Boolean;
private static bool IsTransparentStructHandle(PInvokeGeneratorTransparentStructKind kind)
- => kind is PInvokeGeneratorTransparentStructKind.Handle
+ => kind is PInvokeGeneratorTransparentStructKind.Handle
or PInvokeGeneratorTransparentStructKind.HandleWin32;
private static bool IsTransparentStructHexBased(PInvokeGeneratorTransparentStructKind kind)
=> IsTransparentStructHandle(kind)
|| (kind == PInvokeGeneratorTransparentStructKind.TypedefHex);
+ private static bool IsTransparentStructComparable(PInvokeGeneratorTransparentStructKind kind)
+ => kind is not PInvokeGeneratorTransparentStructKind.FnPtr;
+
private bool IsUnchecked(string targetTypeName, Stmt stmt)
{
if (IsPrevContextDecl(out var parentVarDecl, out _))
@@ -6149,7 +6192,7 @@ private bool NeedsReturnFixup(CXXMethodDecl cxxMethodDecl)
private static bool NeedsNewKeyword(string name)
{
- return name.Equals("Equals",StringComparison.Ordinal)
+ return name.Equals("Equals", StringComparison.Ordinal)
|| name.Equals("GetHashCode", StringComparison.Ordinal)
|| name.Equals("GetType", StringComparison.Ordinal)
|| name.Equals("MemberwiseClone", StringComparison.Ordinal)
diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs
index 3dfb786b..394b765f 100644
--- a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs
+++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs
@@ -227,6 +227,8 @@ public IReadOnlyCollection ExcludedNames
public bool GenerateFileScopedNamespaces => _options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateFileScopedNamespaces);
+ public bool GenerateFnPtrWrapper => _options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateFnPtrWrapper);
+
public bool GenerateGenericPointerWrapper => _options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateGenericPointerWrapper);
public bool GenerateGuidMember => _options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateGuidMember);
diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfigurationOptions.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfigurationOptions.cs
index db91ccd4..3c12233a 100644
--- a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfigurationOptions.cs
+++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfigurationOptions.cs
@@ -86,4 +86,6 @@ public enum PInvokeGeneratorConfigurationOptions : long
GenerateCallConvMemberFunction = 1L << 37,
GenerateGenericPointerWrapper = 1L << 38,
+
+ GenerateFnPtrWrapper = 1L << 39,
}
diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorTransparentStructKind.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorTransparentStructKind.cs
index 8f61061e..e85b4157 100644
--- a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorTransparentStructKind.cs
+++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorTransparentStructKind.cs
@@ -9,4 +9,5 @@ public enum PInvokeGeneratorTransparentStructKind
HandleWin32 = 4,
TypedefHex = 5,
HandleVulkan = 6,
+ FnPtr = 7,
}
diff --git a/sources/ClangSharpPInvokeGenerator/Program.cs b/sources/ClangSharpPInvokeGenerator/Program.cs
index ab53111e..e87a664d 100644
--- a/sources/ClangSharpPInvokeGenerator/Program.cs
+++ b/sources/ClangSharpPInvokeGenerator/Program.cs
@@ -172,6 +172,7 @@ public static class Program
new TwoColumnHelpRow("generate-native-bitfield-attribute", "[NativeBitfield(\"\", offset: #, length: #)] attribute should be generated to document the encountered bitfield layout."),
new TwoColumnHelpRow("generate-native-inheritance-attribute", "[NativeInheritance(\"\")] attribute should be generated to document the encountered C++ base type."),
new TwoColumnHelpRow("generate-generic-pointer-wrapper", "Pointer should be used for limited generic type support."),
+ new TwoColumnHelpRow("generate-fnptr-wrapper", "Function pointers should be wrapped in transparent structs instead of delegates."),
new TwoColumnHelpRow("generate-setslastsystemerror-attribute", "[SetsLastSystemError] attribute should be generated rather than using SetLastError = true."),
new TwoColumnHelpRow("generate-template-bindings", "Bindings for template-definitions should be generated. This is currently experimental."),
new TwoColumnHelpRow("generate-unmanaged-constants", "Unmanaged constants should be generated using static ref readonly properties. This is currently experimental."),
@@ -573,6 +574,12 @@ public static void Run(InvocationContext context)
break;
}
+ case "generate-fnptr-wrapper":
+ {
+ configOptions |= PInvokeGeneratorConfigurationOptions.GenerateFnPtrWrapper;
+ break;
+ }
+
case "generate-setslastsystemerror-attribute":
{
configOptions |= PInvokeGeneratorConfigurationOptions.GenerateSetsLastSystemErrorAttribute;