From 89ebda49b1c9357a613376ce480fda114db44f11 Mon Sep 17 00:00:00 2001 From: TechPizza <23627133+TechnologicalPizza@users.noreply.github.com> Date: Mon, 8 Jan 2024 16:48:51 +0100 Subject: [PATCH 01/11] Extract body of GenerateTransparentStructs into new method --- .../PInvokeGenerator.cs | 1001 +++++++++-------- 1 file changed, 504 insertions(+), 497 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 08320b66..4079bca7 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,595 +972,602 @@ static void GenerateTransparentStructs(PInvokeGenerator generator, Stream? strea var type = transparentStruct.Value.Name; var kind = transparentStruct.Value.Kind; - var isTypePointer = type.Contains('*', StringComparison.Ordinal); - - 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"; - - if (!string.IsNullOrEmpty(config.HeaderText)) - { - sw.WriteLine(config.HeaderText); - } - - var indentString = " "; - var targetNamespace = generator.GetNamespace(name); - - sw.WriteLine("using System;"); - - if (kind == PInvokeGeneratorTransparentStructKind.HandleWin32) - { - var handleNamespace = generator.GetNamespace("HANDLE"); - - if (targetNamespace != handleNamespace) - { - sw.Write("using "); - sw.Write(handleNamespace); - sw.WriteLine(';'); - } - } - - sw.WriteLine(); - - sw.Write("namespace "); - sw.Write(targetNamespace); - - if (generator.Config.GenerateFileScopedNamespaces) - { - sw.WriteLine(';'); - sw.WriteLine(); - indentString = ""; - } - else - { - sw.WriteLine(); - sw.WriteLine('{'); - } - - sw.Write(indentString); - sw.Write("public readonly "); - - if (isTypePointer || IsTransparentStructHexBased(kind)) - { - sw.Write("unsafe "); - } + GenerateTransparentStruct(name, type, kind, generator, stream, leaveStreamOpen); + } + } + } - sw.Write("partial struct "); - sw.Write(name); - sw.Write(" : IComparable, IComparable<"); - sw.Write(name); - sw.Write(">, IEquatable<"); - sw.Write(name); - sw.WriteLine(">, IFormattable"); + private static void GenerateTransparentStruct(string name, string type, PInvokeGeneratorTransparentStructKind kind, PInvokeGenerator generator, Stream? stream, bool leaveStreamOpen) + { + var config = generator.Config; - sw.Write(indentString); - sw.WriteLine('{'); + var isTypePointer = type.Contains('*', StringComparison.Ordinal); - sw.Write(indentString); - sw.Write(" public readonly "); - sw.Write(type); - sw.WriteLine(" Value;"); - sw.WriteLine(); + if (stream is null) + { + var outputPath = Path.Combine(config.OutputLocation, $"{name}.cs"); + stream = generator._outputStreamFactory(outputPath); + } - // All transparent structs be created directly from the underlying type + using var sw = new StreamWriter(stream, s_defaultStreamWriterEncoding, DefaultStreamWriterBufferSize, leaveStreamOpen); + sw.NewLine = "\n"; - 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(); + if (!string.IsNullOrEmpty(config.HeaderText)) + { + sw.WriteLine(config.HeaderText); + } - if (IsTransparentStructHandle(kind) || (kind == PInvokeGeneratorTransparentStructKind.HandleVulkan)) - { - // Handle like transparent structs define a NULL member + var indentString = " "; + var targetNamespace = generator.GetNamespace(name); - if (kind == PInvokeGeneratorTransparentStructKind.HandleWin32) - { - sw.Write(indentString); - sw.Write(" public static "); - sw.Write(name); - sw.Write(" INVALID_VALUE => new "); - sw.Write(name); + sw.WriteLine("using System;"); - if (isTypePointer) - { - sw.Write("(("); - sw.Write(type); - sw.WriteLine(")(-1));"); - } - else - { - sw.WriteLine("(-1);"); - } + if (kind == PInvokeGeneratorTransparentStructKind.HandleWin32) + { + var handleNamespace = generator.GetNamespace("HANDLE"); - sw.WriteLine(); - } + if (targetNamespace != handleNamespace) + { + sw.Write("using "); + sw.Write(handleNamespace); + sw.WriteLine(';'); + } + } - sw.Write(indentString); - sw.Write(" public static "); - sw.Write(name); - sw.Write(" NULL => new "); - sw.Write(name); + sw.WriteLine(); - if (isTypePointer) - { - sw.WriteLine("(null);"); - } - else - { - sw.WriteLine("(0);"); - } + sw.Write("namespace "); + sw.Write(targetNamespace); - sw.WriteLine(); - } - else if (IsTransparentStructBoolean(kind)) - { - // Boolean like transparent structs define FALSE and TRUE members + if (generator.Config.GenerateFileScopedNamespaces) + { + sw.WriteLine(';'); + sw.WriteLine(); + indentString = ""; + } + else + { + sw.WriteLine(); + sw.WriteLine('{'); + } - sw.Write(indentString); - sw.Write(" public static "); - sw.Write(name); - sw.Write(" FALSE => new "); - sw.Write(name); - sw.WriteLine("(0);"); - sw.WriteLine(); + sw.Write(indentString); + sw.Write("public readonly "); - sw.Write(indentString); - sw.Write(" public static "); - sw.Write(name); - sw.Write(" TRUE => new "); - sw.Write(name); - sw.WriteLine("(1);"); - sw.WriteLine(); - } + if (isTypePointer || IsTransparentStructHexBased(kind)) + { + sw.Write("unsafe "); + } - // All transparent structs support equality and relational comparisons with themselves + sw.Write("partial struct "); + sw.Write(name); + sw.Write(" : IComparable, IComparable<"); + sw.Write(name); + sw.Write(">, IEquatable<"); + sw.Write(name); + sw.WriteLine(">, IFormattable"); - sw.Write(indentString); - sw.Write(" public static bool operator ==("); - sw.Write(name); - sw.Write(" left, "); - sw.Write(name); - sw.WriteLine(" right) => left.Value == right.Value;"); - sw.WriteLine(); + sw.Write(indentString); + sw.WriteLine('{'); - sw.Write(indentString); - sw.Write(" public static bool operator !=("); - sw.Write(name); - sw.Write(" left, "); - sw.Write(name); - sw.WriteLine(" right) => left.Value != right.Value;"); - sw.WriteLine(); + sw.Write(indentString); + sw.Write(" public readonly "); + sw.Write(type); + sw.WriteLine(" Value;"); + sw.WriteLine(); - sw.Write(indentString); - sw.Write(" public static bool operator <("); - sw.Write(name); - sw.Write(" left, "); - sw.Write(name); - sw.WriteLine(" right) => left.Value < right.Value;"); - sw.WriteLine(); + // All transparent structs be created directly from the underlying type - sw.Write(indentString); - sw.Write(" public static bool operator <=("); - sw.Write(name); - sw.Write(" left, "); - sw.Write(name); - sw.WriteLine(" right) => left.Value <= right.Value;"); - sw.WriteLine(); + 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.Write(indentString); - sw.Write(" public static bool operator >("); - sw.Write(name); - sw.Write(" left, "); - sw.Write(name); - sw.WriteLine(" right) => left.Value > right.Value;"); - sw.WriteLine(); + if (IsTransparentStructHandle(kind) || (kind == PInvokeGeneratorTransparentStructKind.HandleVulkan)) + { + // Handle like transparent structs define a NULL member + if (kind == PInvokeGeneratorTransparentStructKind.HandleWin32) + { sw.Write(indentString); - sw.Write(" public static bool operator >=("); + sw.Write(" public static "); sw.Write(name); - sw.Write(" left, "); + sw.Write(" INVALID_VALUE => new "); sw.Write(name); - sw.WriteLine(" right) => left.Value >= right.Value;"); - sw.WriteLine(); - if (IsTransparentStructHandle(kind)) + if (isTypePointer) { - // 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.Write("(("); + sw.Write(type); + sw.WriteLine(")(-1));"); + } + else + { + sw.WriteLine("(-1);"); + } - if (type.Equals("void*", StringComparison.Ordinal)) - { - sw.WriteLine("(value);"); - } - else - { - if (!IsUnsigned(type)) - { - sw.Write("unchecked"); - } + sw.WriteLine(); + } - sw.Write("(("); - sw.Write(type); - sw.WriteLine(")(value));"); - } - sw.WriteLine(); + sw.Write(indentString); + sw.Write(" public static "); + sw.Write(name); + sw.Write(" NULL => new "); + sw.Write(name); - sw.Write(indentString); - sw.Write(" public static implicit operator void*("); - sw.Write(name); + if (isTypePointer) + { + sw.WriteLine("(null);"); + } + else + { + sw.WriteLine("(0);"); + } - if (isTypePointer) - { - sw.WriteLine(" value) => value.Value;"); - } - else - { - var isUnchecked = !IsUnsigned(type); - sw.Write(" value) => "); + sw.WriteLine(); + } + else if (IsTransparentStructBoolean(kind)) + { + // Boolean like transparent structs define FALSE and TRUE members - if (isUnchecked) - { - sw.Write("unchecked("); - } - sw.Write("(void*)(value.Value)"); + sw.Write(indentString); + sw.Write(" public static "); + sw.Write(name); + sw.Write(" FALSE => new "); + sw.Write(name); + sw.WriteLine("(0);"); + sw.WriteLine(); - if (isUnchecked) - { - sw.Write(")"); - } - sw.WriteLine(); - } + sw.Write(indentString); + sw.Write(" public static "); + sw.Write(name); + sw.Write(" TRUE => new "); + sw.Write(name); + sw.WriteLine("(1);"); + sw.WriteLine(); + } - sw.WriteLine(); + // All transparent structs support equality and relational comparisons with themselves + + sw.Write(indentString); + sw.Write(" public static bool operator ==("); + sw.Write(name); + sw.Write(" left, "); + sw.Write(name); + sw.WriteLine(" right) => left.Value == right.Value;"); + sw.WriteLine(); + + sw.Write(indentString); + sw.Write(" public static bool operator !=("); + sw.Write(name); + sw.Write(" left, "); + sw.Write(name); + sw.WriteLine(" right) => left.Value != right.Value;"); + sw.WriteLine(); + + sw.Write(indentString); + sw.Write(" public static bool operator <("); + sw.Write(name); + sw.Write(" left, "); + sw.Write(name); + sw.WriteLine(" right) => left.Value < right.Value;"); + sw.WriteLine(); + + sw.Write(indentString); + sw.Write(" public static bool operator <=("); + sw.Write(name); + sw.Write(" left, "); + sw.Write(name); + sw.WriteLine(" right) => left.Value <= right.Value;"); + sw.WriteLine(); + + sw.Write(indentString); + sw.Write(" public static bool operator >("); + sw.Write(name); + sw.Write(" left, "); + sw.Write(name); + sw.WriteLine(" right) => left.Value > right.Value;"); + sw.WriteLine(); + + sw.Write(indentString); + sw.Write(" public static bool operator >=("); + sw.Write(name); + sw.Write(" left, "); + sw.Write(name); + sw.WriteLine(" right) => left.Value >= right.Value;"); + sw.WriteLine(); + + if (IsTransparentStructHandle(kind)) + { + // Handle like transparent structs can be cast to/from void* - 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.Write(indentString); + sw.Write(" public static explicit operator "); + sw.Write(name); + sw.Write("(void* value) => new "); + sw.Write(name); - sw.Write(indentString); - sw.Write(" public static implicit operator HANDLE("); - sw.Write(name); - sw.WriteLine(" value) => new HANDLE(value.Value);"); - sw.WriteLine(); - } - } - else if (IsTransparentStructBoolean(kind)) + if (type.Equals("void*", StringComparison.Ordinal)) + { + sw.WriteLine("(value);"); + } + else + { + if (!IsUnsigned(type)) { - // 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 bool("); - sw.Write(name); - sw.WriteLine(" value) => value.Value != 0;"); - sw.WriteLine(); - - sw.Write(indentString); - sw.Write(" public static implicit operator "); - sw.Write(name); - sw.Write("(bool value) => new "); - sw.Write(name); + sw.Write("unchecked"); + } - 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.Write("(("); + sw.Write(type); + sw.WriteLine(")(value));"); + } + sw.WriteLine(); - sw.WriteLine(); + sw.Write(indentString); + sw.Write(" public static implicit operator void*("); + sw.Write(name); - sw.Write(indentString); - sw.Write(" public static bool operator false("); - sw.Write(name); - sw.WriteLine(" value) => value.Value == 0;"); - sw.WriteLine(); + if (isTypePointer) + { + sw.WriteLine(" value) => value.Value;"); + } + else + { + var isUnchecked = !IsUnsigned(type); + sw.Write(" value) => "); - sw.Write(indentString); - sw.Write(" public static bool operator true("); - sw.Write(name); - sw.WriteLine(" value) => value.Value != 0;"); - sw.WriteLine(); + if (isUnchecked) + { + sw.Write("unchecked("); } + sw.Write("(void*)(value.Value)"); - // All transparent structs define casts to/from the various integer types + if (isUnchecked) + { + sw.Write(")"); + } + sw.WriteLine(); + } - 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.WriteLine(); - // All transparent structs override CompareTo, Equals, GetHashCode, and ToString + 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.WriteLine(" public int CompareTo(object? obj)"); - sw.Write(indentString); - sw.WriteLine(" {"); - sw.Write(indentString); - sw.Write(" if (obj is "); + sw.Write(" public static explicit operator "); 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("(HANDLE value) => new "); sw.Write(name); - sw.WriteLine(".\");"); - sw.Write(indentString); - sw.WriteLine(" }"); + sw.WriteLine("(value);"); sw.WriteLine(); sw.Write(indentString); - sw.Write(" public int CompareTo("); + sw.Write(" public static implicit operator HANDLE("); sw.Write(name); - - if (isTypePointer) - { - sw.WriteLine(" other) => ((nuint)(Value)).CompareTo((nuint)(other.Value));"); - } - else - { - sw.WriteLine(" other) => Value.CompareTo(other.Value);"); - } - + sw.WriteLine(" value) => new HANDLE(value.Value);"); sw.WriteLine(); + } + } + 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 override bool Equals(object? obj) => (obj is "); - sw.Write(name); - sw.WriteLine(" other) && Equals(other);"); - sw.WriteLine(); + sw.Write(indentString); + sw.Write(" public static implicit operator bool("); + sw.Write(name); + sw.WriteLine(" value) => value.Value != 0;"); + sw.WriteLine(); - sw.Write(indentString); - sw.Write(" public bool Equals("); - sw.Write(name); + sw.Write(indentString); + sw.Write(" public static implicit operator "); + sw.Write(name); + sw.Write("(bool value) => new "); + sw.Write(name); - if (isTypePointer) - { - sw.WriteLine(" other) => ((nuint)(Value)).Equals((nuint)(other.Value));"); - } - else - { - sw.WriteLine(" other) => Value.Equals(other.Value);"); - } + 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.WriteLine(); + sw.WriteLine(); - sw.Write(indentString); - sw.Write(" public override int GetHashCode() => "); + sw.Write(indentString); + sw.Write(" public static bool operator false("); + sw.Write(name); + sw.WriteLine(" value) => value.Value == 0;"); + sw.WriteLine(); - if (isTypePointer) - { - sw.Write("((nuint)(Value))"); - } - else - { - sw.Write("Value"); - } + sw.Write(indentString); + sw.Write(" public static bool operator true("); + sw.Write(name); + sw.WriteLine(" value) => value.Value != 0;"); + sw.WriteLine(); + } - sw.WriteLine(".GetHashCode();"); - sw.WriteLine(); + // All transparent structs define casts to/from the various integer types + + 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"); + + // All transparent structs override CompareTo, Equals, GetHashCode, and ToString + + 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.Write(indentString); + sw.Write(" public int CompareTo("); + sw.Write(name); + + if (isTypePointer) + { + sw.WriteLine(" other) => ((nuint)(Value)).CompareTo((nuint)(other.Value));"); + } + else + { + sw.WriteLine(" other) => Value.CompareTo(other.Value);"); + } - sw.Write(indentString); - sw.Write(" public override string ToString() => "); + sw.WriteLine(); - if (isTypePointer) - { - sw.Write("((nuint)(Value))"); - } - else - { - sw.Write("Value"); - } + sw.Write(indentString); + sw.Write(" public override bool Equals(object? obj) => (obj is "); + sw.Write(name); + sw.WriteLine(" other) && Equals(other);"); + sw.WriteLine(); - sw.Write(".ToString("); + sw.Write(indentString); + sw.Write(" public bool Equals("); + sw.Write(name); - if (IsTransparentStructHexBased(kind)) - { - var (typeSrcSize, typeDstSize, typeSign) = GetSizeAndSignOf(type); + if (isTypePointer) + { + sw.WriteLine(" other) => ((nuint)(Value)).Equals((nuint)(other.Value));"); + } + else + { + sw.WriteLine(" other) => Value.Equals(other.Value);"); + } - if (typeSrcSize != typeDstSize) - { - sw.Write("(sizeof(nint) == 4) ? \"X8\" : \"X16\""); - } - else - { - sw.Write('"'); - sw.Write('X'); - sw.Write(typeSrcSize * 2); - sw.Write('"'); - } - } + sw.WriteLine(); - sw.WriteLine(");"); - sw.WriteLine(); + sw.Write(indentString); + sw.Write(" public override int GetHashCode() => "); - sw.Write(indentString); - sw.Write(" public string ToString(string? format, IFormatProvider? formatProvider) => "); + if (isTypePointer) + { + sw.Write("((nuint)(Value))"); + } + else + { + sw.Write("Value"); + } - if (isTypePointer) - { - sw.Write("((nuint)(Value))"); - } - else - { - sw.Write("Value"); - } + sw.WriteLine(".GetHashCode();"); + sw.WriteLine(); - sw.WriteLine(".ToString(format, formatProvider);"); + sw.Write(indentString); + sw.Write(" public override string ToString() => "); - sw.Write(indentString); - sw.WriteLine('}'); + if (isTypePointer) + { + sw.Write("((nuint)(Value))"); + } + else + { + sw.Write("Value"); + } - if (!generator.Config.GenerateFileScopedNamespaces) - { - sw.WriteLine('}'); - } + sw.Write(".ToString("); - if (!leaveStreamOpen) - { - stream = null; - } - } + if (IsTransparentStructHexBased(kind)) + { + var (typeSrcSize, typeDstSize, typeSign) = GetSizeAndSignOf(type); - static (int srcSize, int dstSize, int sign) GetSizeAndSignOf(string type) + if (typeSrcSize != typeDstSize) { - 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("(sizeof(nint) == 4) ? \"X8\" : \"X16\""); } + else + { + sw.Write('"'); + sw.Write('X'); + sw.Write(typeSrcSize * 2); + sw.Write('"'); + } + } + + sw.WriteLine(");"); + sw.WriteLine(); + + sw.Write(indentString); + sw.Write(" public string ToString(string? format, IFormatProvider? formatProvider) => "); + + if (isTypePointer) + { + sw.Write("((nuint)(Value))"); + } + else + { + sw.Write("Value"); + } + + sw.WriteLine(".ToString(format, formatProvider);"); - static void OutputConversions(StreamWriter sw, string indentString, string name, string type, PInvokeGeneratorTransparentStructKind kind, string target) + sw.Write(indentString); + sw.WriteLine('}'); + + if (!generator.Config.GenerateFileScopedNamespaces) + { + sw.WriteLine('}'); + } + + if (!leaveStreamOpen) + { + stream = null; + } + + 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(StreamWriter sw, string indentString, 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 "); - sw.Write(name); - sw.Write('('); + 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)); - if (castFromKind.Equals("explicit", StringComparison.Ordinal) || isPointerToNativeCast) - { - sw.Write("unchecked(("); - sw.Write(type); - sw.Write(")("); - } + if (((typeDstSize <= targetSrcSize) && !areEquivalentTypeAndTarget) || ((targetSign == -1) && (typeSign == +1)) || IsTransparentStructHandle(kind)) + { + castFromKind = "explicit"; + } - sw.Write("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 "); + sw.Write(name); + sw.Write('('); + + if (castFromKind.Equals("explicit", StringComparison.Ordinal) || isPointerToNativeCast) + { + sw.Write("unchecked(("); + sw.Write(type); + sw.Write(")("); + } - if (castFromKind.Equals("explicit", StringComparison.Ordinal) || isPointerToNativeCast) - { - sw.Write("))"); - } + sw.Write("value"); - sw.WriteLine(");"); - sw.WriteLine(); + if (castFromKind.Equals("explicit", StringComparison.Ordinal) || isPointerToNativeCast) + { + sw.Write("))"); + } - // public static castToKind operator target(name value) => ((target)(value.Value)); + sw.WriteLine(");"); + sw.WriteLine(); - 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)); + // public static castToKind operator target(name value) => ((target)(value.Value)); - if (((targetDstSize <= typeSrcSize) && !areEquivalentTypeAndTarget) || ((typeSign == -1) && (targetSign == +1))) - { - castToKind = "explicit"; - } + 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)); - sw.Write(indentString); - sw.Write(" public static "); - sw.Write(castToKind); - sw.Write(" operator "); - sw.Write(target); - sw.Write('('); - sw.Write(name); - sw.Write(" value) => "); + if (((targetDstSize <= typeSrcSize) && !areEquivalentTypeAndTarget) || ((typeSign == -1) && (targetSign == +1))) + { + castToKind = "explicit"; + } - if (castToKind.Equals("explicit", StringComparison.Ordinal) || isPointerToNativeCast) - { - sw.Write('('); - sw.Write(target); - sw.Write(")("); - } + sw.Write(indentString); + sw.Write(" public static "); + sw.Write(castToKind); + sw.Write(" operator "); + sw.Write(target); + sw.Write('('); + sw.Write(name); + sw.Write(" value) => "); - sw.Write("value.Value"); + if (castToKind.Equals("explicit", StringComparison.Ordinal) || isPointerToNativeCast) + { + sw.Write('('); + sw.Write(target); + sw.Write(")("); + } - if (castToKind.Equals("explicit", StringComparison.Ordinal) || isPointerToNativeCast) - { - sw.Write(')'); - } + sw.Write("value.Value"); - sw.WriteLine(';'); - sw.WriteLine(); + if (castToKind.Equals("explicit", StringComparison.Ordinal) || isPointerToNativeCast) + { + sw.Write(')'); } + + sw.WriteLine(';'); + sw.WriteLine(); } } @@ -5403,7 +5410,7 @@ 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) @@ -6149,7 +6156,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) From b7810a12acb3389e1d9388092a7667c81e64d599 Mon Sep 17 00:00:00 2001 From: TechPizza <23627133+TechnologicalPizza@users.noreply.github.com> Date: Mon, 8 Jan 2024 19:49:57 +0100 Subject: [PATCH 02/11] Use CSharpOutputBuilder for GenerateTransparentStruct --- .../PInvokeGenerator.cs | 269 ++++++------------ 1 file changed, 93 insertions(+), 176 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 4079bca7..a5109f44 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -972,67 +972,33 @@ static void GenerateTransparentStructs(PInvokeGenerator generator, Stream? strea var type = transparentStruct.Value.Name; var kind = transparentStruct.Value.Kind; - GenerateTransparentStruct(name, type, kind, generator, stream, leaveStreamOpen); + generator.StartUsingOutputBuilder(name); + generator.GenerateTransparentStruct(name, name, type, kind); + generator.StopUsingOutputBuilder(); } } } - private static void GenerateTransparentStruct(string name, string type, PInvokeGeneratorTransparentStructKind kind, PInvokeGenerator generator, Stream? stream, bool leaveStreamOpen) + private void GenerateTransparentStruct(string parentName, string name, string type, PInvokeGeneratorTransparentStructKind kind) { - var config = generator.Config; - var isTypePointer = type.Contains('*', StringComparison.Ordinal); - 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"; - - if (!string.IsNullOrEmpty(config.HeaderText)) - { - sw.WriteLine(config.HeaderText); - } + var sw = StartCSharpCode(); - var indentString = " "; - var targetNamespace = generator.GetNamespace(name); - - sw.WriteLine("using System;"); + sw.EmitSystemSupport(); if (kind == PInvokeGeneratorTransparentStructKind.HandleWin32) { - var handleNamespace = generator.GetNamespace("HANDLE"); + var targetNamespace = GetNamespace(name); + var handleNamespace = GetNamespace("HANDLE"); if (targetNamespace != handleNamespace) { - sw.Write("using "); - sw.Write(handleNamespace); - sw.WriteLine(';'); + sw.AddUsingDirective(handleNamespace); } } - sw.WriteLine(); - - sw.Write("namespace "); - sw.Write(targetNamespace); - - if (generator.Config.GenerateFileScopedNamespaces) - { - sw.WriteLine(';'); - sw.WriteLine(); - indentString = ""; - } - else - { - sw.WriteLine(); - sw.WriteLine('{'); - } - - sw.Write(indentString); - sw.Write("public readonly "); + sw.WriteIndented("public readonly "); if (isTypePointer || IsTransparentStructHexBased(kind)) { @@ -1047,30 +1013,24 @@ private static void GenerateTransparentStruct(string name, string type, PInvokeG sw.Write(name); sw.WriteLine(">, IFormattable"); - sw.Write(indentString); - sw.WriteLine('{'); + sw.WriteIndentedLine('{'); - sw.Write(indentString); - sw.Write(" public readonly "); + sw.WriteIndented(" public readonly "); sw.Write(type); sw.WriteLine(" Value;"); - sw.WriteLine(); + sw.WriteDivider(); // All transparent structs be created directly from the underlying type - sw.Write(indentString); - sw.Write(" public "); + sw.WriteIndented(" 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.WriteIndentedLine(" {"); + sw.WriteIndentedLine(" Value = value;"); + sw.WriteIndentedLine(" }"); + sw.WriteDivider(); if (IsTransparentStructHandle(kind) || (kind == PInvokeGeneratorTransparentStructKind.HandleVulkan)) { @@ -1078,8 +1038,7 @@ private static void GenerateTransparentStruct(string name, string type, PInvokeG if (kind == PInvokeGeneratorTransparentStructKind.HandleWin32) { - sw.Write(indentString); - sw.Write(" public static "); + sw.WriteIndented(" public static "); sw.Write(name); sw.Write(" INVALID_VALUE => new "); sw.Write(name); @@ -1095,11 +1054,10 @@ private static void GenerateTransparentStruct(string name, string type, PInvokeG sw.WriteLine("(-1);"); } - sw.WriteLine(); + sw.WriteDivider(); } - sw.Write(indentString); - sw.Write(" public static "); + sw.WriteIndented(" public static "); sw.Write(name); sw.Write(" NULL => new "); sw.Write(name); @@ -1113,85 +1071,76 @@ private static void GenerateTransparentStruct(string name, string type, PInvokeG sw.WriteLine("(0);"); } - 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.WriteIndented(" public static "); sw.Write(name); sw.Write(" FALSE => new "); sw.Write(name); sw.WriteLine("(0);"); - sw.WriteLine(); + sw.WriteDivider(); - sw.Write(indentString); - sw.Write(" public static "); + sw.WriteIndented(" public static "); sw.Write(name); sw.Write(" TRUE => new "); sw.Write(name); sw.WriteLine("(1);"); - sw.WriteLine(); + sw.WriteDivider(); } // All transparent structs support equality and relational comparisons with themselves - 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(); - 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* - sw.Write(indentString); - sw.Write(" public static explicit operator "); + sw.WriteIndented(" public static explicit operator "); sw.Write(name); sw.Write("(void* value) => new "); sw.Write(name); @@ -1211,10 +1160,9 @@ private static void GenerateTransparentStruct(string name, string type, PInvokeG sw.Write(type); sw.WriteLine(")(value));"); } - sw.WriteLine(); + sw.WriteDivider(); - sw.Write(indentString); - sw.Write(" public static implicit operator void*("); + sw.WriteIndented(" public static implicit operator void*("); sw.Write(name); if (isTypePointer) @@ -1236,28 +1184,26 @@ private static void GenerateTransparentStruct(string name, string type, PInvokeG { sw.Write(")"); } - sw.WriteLine(); + sw.WriteDivider(); } - sw.WriteLine(); + 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.WriteIndented(" public static explicit operator "); sw.Write(name); sw.Write("(HANDLE value) => new "); sw.Write(name); sw.WriteLine("(value);"); - sw.WriteLine(); + sw.WriteDivider(); - sw.Write(indentString); - sw.Write(" public static implicit operator HANDLE("); + sw.WriteIndented(" public static implicit operator HANDLE("); sw.Write(name); sw.WriteLine(" value) => new HANDLE(value.Value);"); - sw.WriteLine(); + sw.WriteDivider(); } } else if (IsTransparentStructBoolean(kind)) @@ -1265,14 +1211,12 @@ private static void GenerateTransparentStruct(string name, string type, PInvokeG // 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 bool("); + sw.WriteIndented(" 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.WriteIndented(" public static implicit operator "); sw.Write(name); sw.Write("(bool value) => new "); sw.Write(name); @@ -1292,61 +1236,50 @@ private static void GenerateTransparentStruct(string name, string type, PInvokeG sw.WriteLine(")(value ? 1u : 0u);"); } - sw.WriteLine(); + sw.WriteDivider(); - sw.Write(indentString); - sw.Write(" public static bool operator false("); + sw.WriteIndented(" public static bool operator false("); sw.Write(name); sw.WriteLine(" value) => value.Value == 0;"); - sw.WriteLine(); + sw.WriteDivider(); - sw.Write(indentString); - sw.Write(" public static bool operator true("); + sw.WriteIndented(" public static bool operator true("); sw.Write(name); sw.WriteLine(" value) => value.Value != 0;"); - sw.WriteLine(); + sw.WriteDivider(); } // All transparent structs define casts to/from the various integer types - 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"); + OutputConversions(sw, name, type, kind, "byte"); + OutputConversions(sw, name, type, kind, "short"); + OutputConversions(sw, name, type, kind, "int"); + OutputConversions(sw, name, type, kind, "long"); + OutputConversions(sw, name, type, kind, "nint"); + OutputConversions(sw, name, type, kind, "sbyte"); + OutputConversions(sw, name, type, kind, "ushort"); + OutputConversions(sw, name, type, kind, "uint"); + OutputConversions(sw, name, type, kind, "ulong"); + OutputConversions(sw, name, type, kind, "nuint"); // All transparent structs override CompareTo, Equals, GetHashCode, and ToString - sw.Write(indentString); - sw.WriteLine(" public int CompareTo(object? obj)"); - sw.Write(indentString); - sw.WriteLine(" {"); - sw.Write(indentString); - sw.Write(" if (obj is "); + sw.WriteIndentedLine(" public int CompareTo(object? obj)"); + sw.WriteIndentedLine(" {"); + sw.WriteIndented(" 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.WriteIndentedLine(" {"); + sw.WriteIndentedLine(" return CompareTo(other);"); + sw.WriteIndentedLine(" }"); + sw.WriteDivider(); + sw.WriteIndented(" 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.WriteIndentedLine(" }"); + sw.WriteDivider(); - sw.Write(indentString); - sw.Write(" public int CompareTo("); + sw.WriteIndented(" public int CompareTo("); sw.Write(name); if (isTypePointer) @@ -1358,16 +1291,14 @@ private static void GenerateTransparentStruct(string name, string type, PInvokeG sw.WriteLine(" other) => 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.WriteDivider(); - sw.Write(indentString); - sw.Write(" public bool Equals("); + sw.WriteIndented(" public bool Equals("); sw.Write(name); if (isTypePointer) @@ -1379,10 +1310,9 @@ private static void GenerateTransparentStruct(string name, string type, PInvokeG sw.WriteLine(" other) => 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) { @@ -1394,10 +1324,9 @@ private static void GenerateTransparentStruct(string name, string type, PInvokeG } sw.WriteLine(".GetHashCode();"); - sw.WriteLine(); + sw.WriteDivider(); - sw.Write(indentString); - sw.Write(" public override string ToString() => "); + sw.WriteIndented(" public override string ToString() => "); if (isTypePointer) { @@ -1428,10 +1357,9 @@ private static void GenerateTransparentStruct(string name, string type, PInvokeG } sw.WriteLine(");"); - sw.WriteLine(); + sw.WriteDivider(); - sw.Write(indentString); - sw.Write(" public string ToString(string? format, IFormatProvider? formatProvider) => "); + sw.WriteIndented(" public string ToString(string? format, IFormatProvider? formatProvider) => "); if (isTypePointer) { @@ -1444,18 +1372,9 @@ private static void GenerateTransparentStruct(string name, string type, PInvokeG sw.WriteLine(".ToString(format, formatProvider);"); - sw.Write(indentString); - sw.WriteLine('}'); + sw.WriteIndentedLine('}'); - if (!generator.Config.GenerateFileScopedNamespaces) - { - sw.WriteLine('}'); - } - - if (!leaveStreamOpen) - { - stream = null; - } + StopCSharpCode(); static (int srcSize, int dstSize, int sign) GetSizeAndSignOf(string type) { @@ -1479,7 +1398,7 @@ private static void GenerateTransparentStruct(string name, string type, PInvokeG }; } - static void OutputConversions(StreamWriter sw, string indentString, string name, string type, PInvokeGeneratorTransparentStructKind kind, string target) + 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); @@ -1501,8 +1420,7 @@ static void OutputConversions(StreamWriter sw, string indentString, string name, castFromKind = "explicit"; } - sw.Write(indentString); - sw.Write(" public static "); + sw.WriteIndented(" public static "); sw.Write(castFromKind); sw.Write(" operator "); sw.Write(name); @@ -1527,7 +1445,7 @@ static void OutputConversions(StreamWriter sw, string indentString, string name, } sw.WriteLine(");"); - sw.WriteLine(); + sw.WriteDivider(); // public static castToKind operator target(name value) => ((target)(value.Value)); @@ -1543,8 +1461,7 @@ static void OutputConversions(StreamWriter sw, string indentString, string name, castToKind = "explicit"; } - sw.Write(indentString); - sw.Write(" public static "); + sw.WriteIndented(" public static "); sw.Write(castToKind); sw.Write(" operator "); sw.Write(target); @@ -1567,7 +1484,7 @@ static void OutputConversions(StreamWriter sw, string indentString, string name, } sw.WriteLine(';'); - sw.WriteLine(); + sw.WriteDivider(); } } From 7f0a16ab64e009797d26e0923ba88a65430b378a Mon Sep 17 00:00:00 2001 From: TechPizza <23627133+TechnologicalPizza@users.noreply.github.com> Date: Sun, 14 Jan 2024 02:12:13 +0100 Subject: [PATCH 03/11] Add FnPtr kind of transparent structs Avoids implicit casts and comparison operators --- .../PInvokeGenerator.cs | 239 ++++++++++-------- .../PInvokeGeneratorTransparentStructKind.cs | 1 + 2 files changed, 136 insertions(+), 104 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index a5109f44..bfb53363 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -1007,11 +1007,16 @@ private void GenerateTransparentStruct(string parentName, string name, string ty sw.Write("partial struct "); sw.Write(name); - sw.Write(" : IComparable, IComparable<"); - sw.Write(name); - sw.Write(">, IEquatable<"); - sw.Write(name); - sw.WriteLine(">, IFormattable"); + sw.Write(" : "); + if (IsTransparentStructComparable(kind)) + { + sw.Write("IComparable, IComparable<"); + sw.Write(name); + sw.Write(">, IEquatable<"); + sw.Write(name); + sw.Write(">, "); + } + sw.WriteLine("IFormattable"); sw.WriteIndentedLine('{'); @@ -1092,49 +1097,52 @@ private void GenerateTransparentStruct(string parentName, string name, string ty sw.WriteDivider(); } - // All transparent structs support equality and relational comparisons with themselves + 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.WriteDivider(); + sw.WriteIndented(" public static bool operator ==("); + sw.Write(name); + sw.Write(" left, "); + sw.Write(name); + sw.WriteLine(" right) => left.Value == right.Value;"); + sw.WriteDivider(); - sw.WriteIndented(" public static bool operator !=("); - sw.Write(name); - sw.Write(" left, "); - sw.Write(name); - sw.WriteLine(" right) => left.Value != right.Value;"); - sw.WriteDivider(); + sw.WriteIndented(" public static bool operator !=("); + sw.Write(name); + sw.Write(" left, "); + sw.Write(name); + sw.WriteLine(" right) => left.Value != right.Value;"); + sw.WriteDivider(); - sw.WriteIndented(" public static bool operator <("); - sw.Write(name); - sw.Write(" left, "); - sw.Write(name); - sw.WriteLine(" right) => left.Value < right.Value;"); - sw.WriteDivider(); + sw.WriteIndented(" public static bool operator <("); + sw.Write(name); + sw.Write(" left, "); + sw.Write(name); + sw.WriteLine(" right) => left.Value < right.Value;"); + sw.WriteDivider(); - sw.WriteIndented(" public static bool operator <=("); - sw.Write(name); - sw.Write(" left, "); - sw.Write(name); - sw.WriteLine(" right) => left.Value <= right.Value;"); - sw.WriteDivider(); + sw.WriteIndented(" public static bool operator <=("); + sw.Write(name); + sw.Write(" left, "); + sw.Write(name); + sw.WriteLine(" right) => left.Value <= right.Value;"); + sw.WriteDivider(); - sw.WriteIndented(" public static bool operator >("); - sw.Write(name); - sw.Write(" left, "); - sw.Write(name); - sw.WriteLine(" right) => left.Value > right.Value;"); - sw.WriteDivider(); + sw.WriteIndented(" public static bool operator >("); + sw.Write(name); + sw.Write(" left, "); + sw.Write(name); + sw.WriteLine(" right) => left.Value > right.Value;"); + sw.WriteDivider(); - sw.WriteIndented(" public static bool operator >=("); - sw.Write(name); - sw.Write(" left, "); - sw.Write(name); - sw.WriteLine(" right) => left.Value >= right.Value;"); - sw.WriteDivider(); + sw.WriteIndented(" public static bool operator >=("); + sw.Write(name); + sw.Write(" left, "); + sw.Write(name); + sw.WriteLine(" right) => left.Value >= right.Value;"); + sw.WriteDivider(); + } if (IsTransparentStructHandle(kind)) { @@ -1251,80 +1259,93 @@ private void GenerateTransparentStruct(string parentName, string name, string ty // All transparent structs define casts to/from the various integer types - OutputConversions(sw, name, type, kind, "byte"); - OutputConversions(sw, name, type, kind, "short"); - OutputConversions(sw, name, type, kind, "int"); - OutputConversions(sw, name, type, kind, "long"); + 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"); + } + OutputConversions(sw, name, type, kind, "nint"); - OutputConversions(sw, name, type, kind, "sbyte"); - OutputConversions(sw, name, type, kind, "ushort"); - OutputConversions(sw, name, type, kind, "uint"); - OutputConversions(sw, name, type, kind, "ulong"); OutputConversions(sw, name, type, kind, "nuint"); - // All transparent structs override CompareTo, Equals, GetHashCode, and ToString + if (IsTransparentStructComparable(kind)) + { + // Non-FnPtr transparent structs override CompareTo, Equals, GetHashCode - sw.WriteIndentedLine(" public int CompareTo(object? obj)"); - sw.WriteIndentedLine(" {"); - sw.WriteIndented(" if (obj is "); - sw.Write(name); - sw.WriteLine(" other)"); - sw.WriteIndentedLine(" {"); - sw.WriteIndentedLine(" return CompareTo(other);"); - sw.WriteIndentedLine(" }"); - sw.WriteDivider(); - sw.WriteIndented(" return (obj is null) ? 1 : throw new ArgumentException(\"obj is not an instance of "); - sw.Write(name); - sw.WriteLine(".\");"); - sw.WriteIndentedLine(" }"); - sw.WriteDivider(); + sw.WriteIndentedLine(" public int CompareTo(object? obj)"); + sw.WriteIndentedLine(" {"); + sw.WriteIndented(" if (obj is "); + sw.Write(name); + sw.WriteLine(" other)"); + sw.WriteIndentedLine(" {"); + sw.WriteIndentedLine(" return CompareTo(other);"); + sw.WriteIndentedLine(" }"); + sw.WriteDivider(); + sw.WriteIndented(" return (obj is null) ? 1 : throw new ArgumentException(\"obj is not an instance of "); + sw.Write(name); + sw.WriteLine(".\");"); + sw.WriteIndentedLine(" }"); + sw.WriteDivider(); - sw.WriteIndented(" public int CompareTo("); - sw.Write(name); + sw.WriteIndented(" public int CompareTo("); + sw.Write(name); - if (isTypePointer) - { - sw.WriteLine(" other) => ((nuint)(Value)).CompareTo((nuint)(other.Value));"); - } - else - { - sw.WriteLine(" other) => Value.CompareTo(other.Value);"); - } + if (isTypePointer) + { + sw.WriteLine(" other) => ((nuint)(Value)).CompareTo((nuint)(other.Value));"); + } + else + { + sw.WriteLine(" other) => Value.CompareTo(other.Value);"); + } - sw.WriteDivider(); + sw.WriteDivider(); - sw.WriteIndented(" public override bool Equals(object? obj) => (obj is "); - sw.Write(name); - sw.WriteLine(" other) && Equals(other);"); - sw.WriteDivider(); + sw.WriteIndented(" public override bool Equals(object? obj) => (obj is "); + sw.Write(name); + sw.WriteLine(" other) && Equals(other);"); + sw.WriteDivider(); - sw.WriteIndented(" public bool Equals("); - sw.Write(name); + sw.WriteIndented(" public bool Equals("); + sw.Write(name); - if (isTypePointer) - { - sw.WriteLine(" other) => ((nuint)(Value)).Equals((nuint)(other.Value));"); - } - else - { - sw.WriteLine(" other) => Value.Equals(other.Value);"); - } + if (isTypePointer) + { + sw.WriteLine(" other) => ((nuint)(Value)).Equals((nuint)(other.Value));"); + } + else + { + sw.WriteLine(" other) => Value.Equals(other.Value);"); + } - sw.WriteDivider(); + sw.WriteDivider(); - sw.WriteIndented(" public override int GetHashCode() => "); + sw.WriteIndented(" public override int GetHashCode() => "); - if (isTypePointer) - { - sw.Write("((nuint)(Value))"); - } - else - { - sw.Write("Value"); + if (isTypePointer) + { + sw.Write("((nuint)(Value))"); + } + else + { + sw.Write("Value"); + } + + sw.WriteLine(".GetHashCode();"); + sw.WriteDivider(); } - sw.WriteLine(".GetHashCode();"); - sw.WriteDivider(); + // All transparent structs override ToString sw.WriteIndented(" public override string ToString() => "); @@ -1409,13 +1430,18 @@ static void OutputConversions(CSharpOutputBuilder sw, string name, string type, // public static castFromKind operator name(target value) => new name((type)(value)); var castFromKind = "implicit"; - var areEquivalentTypeAndTarget = (type == target) || isPointerToNativeCast + 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)); - if (((typeDstSize <= targetSrcSize) && !areEquivalentTypeAndTarget) || ((targetSign == -1) && (typeSign == +1)) || IsTransparentStructHandle(kind)) + 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"; } @@ -1456,7 +1482,9 @@ static void OutputConversions(CSharpOutputBuilder sw, string name, string type, || (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))) + isDowncast = (targetDstSize <= typeSrcSize) && !areEquivalentTypeAndTarget; + isSignChange = (typeSign == -1) && (targetSign == +1); + if (isForcedExplicit || isDowncast || isSignChange) { castToKind = "explicit"; } @@ -5334,6 +5362,9 @@ private static bool IsTransparentStructHexBased(PInvokeGeneratorTransparentStruc => 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 _)) 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, } From 27c443001afc98410a9d98e4a2990df992a2b654 Mon Sep 17 00:00:00 2001 From: TechPizza <23627133+TechnologicalPizza@users.noreply.github.com> Date: Mon, 15 Jan 2024 09:41:56 +0100 Subject: [PATCH 04/11] Integrate fnptr wrappers in configuration --- .../PInvokeGenerator.VisitDecl.cs | 21 ++++++++++++------- .../PInvokeGeneratorConfiguration.cs | 2 ++ .../PInvokeGeneratorConfigurationOptions.cs | 2 ++ sources/ClangSharpPInvokeGenerator/Program.cs | 7 +++++++ 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs index c0e54ee8..5281d5ea 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('('); @@ -3202,14 +3202,21 @@ void ForFunctionProtoType(TypedefDecl typedefDecl, FunctionProtoType functionPro var name = GetRemappedCursorName(typedefDecl); var escapedName = EscapeName(name); - var callingConventionName = GetCallingConvention(typedefDecl, context: null, typedefDecl.TypeForDecl); - - var returnType = functionProtoType.ReturnType; - var returnTypeName = GetRemappedTypeName(typedefDecl, context: null, returnType, out var nativeTypeName); - StartUsingOutputBuilder(name); + Debug.Assert(_outputBuilder is not null); + + if (_config.GenerateFnPtrWrapper) { - Debug.Assert(_outputBuilder is not null); + var typeName = GetTargetTypeName(typedefDecl, out var nativeTypeName); + + GenerateTransparentStruct(name, escapedName, typeName, PInvokeGeneratorTransparentStructKind.FnPtr); + } + else + { + 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/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/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; From 06d8ff218eaf361b86a333556b41db888ddd7268 Mon Sep 17 00:00:00 2001 From: TechPizza <23627133+TechnologicalPizza@users.noreply.github.com> Date: Mon, 15 Jan 2024 09:48:35 +0100 Subject: [PATCH 05/11] Pass native type name to transparent fnptr --- .../CSharp/CSharpOutputBuilder.cs | 2 +- .../PInvokeGenerator.VisitDecl.cs | 10 ++++++++-- .../ClangSharp.PInvokeGenerator/PInvokeGenerator.cs | 11 ++++++++--- 3 files changed, 17 insertions(+), 6 deletions(-) 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 5281d5ea..419d4746 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs @@ -3208,8 +3208,14 @@ void ForFunctionProtoType(TypedefDecl typedefDecl, FunctionProtoType functionPro if (_config.GenerateFnPtrWrapper) { var typeName = GetTargetTypeName(typedefDecl, out var nativeTypeName); - - GenerateTransparentStruct(name, escapedName, typeName, PInvokeGeneratorTransparentStructKind.FnPtr); + + // TODO: use the underlying typedef type instead of name? + if (IsNativeTypeNameEquivalent(nativeTypeName, name)) + { + nativeTypeName = null; + } + + GenerateTransparentStruct(name, escapedName, typeName, nativeTypeName, PInvokeGeneratorTransparentStructKind.FnPtr); } else { diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index bfb53363..b88a2dca 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -973,19 +973,19 @@ static void GenerateTransparentStructs(PInvokeGenerator generator, Stream? strea var kind = transparentStruct.Value.Kind; generator.StartUsingOutputBuilder(name); - generator.GenerateTransparentStruct(name, name, type, kind); + generator.GenerateTransparentStruct(name, name, type, null, kind); generator.StopUsingOutputBuilder(); } } } - private void GenerateTransparentStruct(string parentName, string name, string type, PInvokeGeneratorTransparentStructKind kind) + private void GenerateTransparentStruct(string parentName, string name, string type, string? nativeTypeName, PInvokeGeneratorTransparentStructKind kind) { var isTypePointer = type.Contains('*', StringComparison.Ordinal); var sw = StartCSharpCode(); - sw.EmitSystemSupport(); + sw.EmitSystemSupport(); if (kind == PInvokeGeneratorTransparentStructKind.HandleWin32) { @@ -998,6 +998,11 @@ private void GenerateTransparentStruct(string parentName, string name, string ty } } + if (nativeTypeName is not null) + { + sw.AddNativeTypeNameAttribute(nativeTypeName); + } + sw.WriteIndented("public readonly "); if (isTypePointer || IsTransparentStructHexBased(kind)) From 948cc64f3c05e2860ee7fa0b224137cc26275a4d Mon Sep 17 00:00:00 2001 From: TechPizza <23627133+TechnologicalPizza@users.noreply.github.com> Date: Mon, 15 Jan 2024 09:48:50 +0100 Subject: [PATCH 06/11] Generate doc include for transparent fnptr --- sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index b88a2dca..c6af63c2 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -998,6 +998,15 @@ private void GenerateTransparentStruct(string parentName, string name, string ty } } + if (Config.GenerateDocIncludes) + { + sw.WriteIndented("/// "); + } + if (nativeTypeName is not null) { sw.AddNativeTypeNameAttribute(nativeTypeName); From e58c832dd8119c35d754e021c3e7dad8abbb9d11 Mon Sep 17 00:00:00 2001 From: TechPizza <23627133+TechnologicalPizza@users.noreply.github.com> Date: Mon, 15 Jan 2024 12:32:05 +0100 Subject: [PATCH 07/11] Fix guard to allow fnptr wrapper --- .../ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs index 419d4746..277953ce 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs @@ -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; } From 75e94c1252c155f1665c2cf9f6c161c23e8bd298 Mon Sep 17 00:00:00 2001 From: TechPizza <23627133+TechnologicalPizza@users.noreply.github.com> Date: Mon, 15 Jan 2024 17:25:45 +0100 Subject: [PATCH 08/11] Use fnptr wrapper name as type name --- .../PInvokeGenerator.VisitDecl.cs | 2 +- .../PInvokeGenerator.cs | 41 ++++++++++++++++++- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs index 277953ce..16b5ab39 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs @@ -3208,7 +3208,7 @@ void ForFunctionProtoType(TypedefDecl typedefDecl, FunctionProtoType functionPro if (_config.GenerateFnPtrWrapper) { - var typeName = GetTargetTypeName(typedefDecl, out var nativeTypeName); + var typeName = GetTypeName(typedefDecl, null, typedefDecl.TypeForDecl, true, false, out var nativeTypeName); // TODO: use the underlying typedef type instead of name? if (IsNativeTypeNameEquivalent(nativeTypeName, name)) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index c6af63c2..6e04212e 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -3061,7 +3061,7 @@ private string GetRemappedCursorName(NamedDecl namedDecl, out string nativeTypeN remappedName += $"_e__{(recordDecl.IsUnion ? "Union" : "Struct")}"; } } - + return remappedName; } @@ -3083,7 +3083,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); @@ -3731,6 +3730,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) @@ -3755,6 +3760,38 @@ 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 of the pointee is a pointer. + 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) From de8bd4aa1039a3a581276d3b1f86f9fc779fa228 Mon Sep 17 00:00:00 2001 From: TechPizza <23627133+TechnologicalPizza@users.noreply.github.com> Date: Tue, 16 Jan 2024 12:05:59 +0100 Subject: [PATCH 09/11] Add NativeTypeName for both (transparent) struct and fnptr --- .../Abstractions/TransparentStructDesc.cs | 13 ++++++++++ .../PInvokeGenerator.VisitDecl.cs | 18 +++++++++---- .../PInvokeGenerator.cs | 26 +++++++++++++++---- 3 files changed, 47 insertions(+), 10 deletions(-) create mode 100644 sources/ClangSharp.PInvokeGenerator/Abstractions/TransparentStructDesc.cs 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/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs index 16b5ab39..0b78a631 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs @@ -3208,15 +3208,23 @@ void ForFunctionProtoType(TypedefDecl typedefDecl, FunctionProtoType functionPro if (_config.GenerateFnPtrWrapper) { - var typeName = GetTypeName(typedefDecl, null, typedefDecl.TypeForDecl, true, false, out var nativeTypeName); + var type = GetTypeName(typedefDecl, null, typedefDecl.TypeForDecl, true, false, out var nativeName); + _ = GetTypeName(typedefDecl, null, typedefDecl.UnderlyingType, true, false, out var nativeType); - // TODO: use the underlying typedef type instead of name? - if (IsNativeTypeNameEquivalent(nativeTypeName, name)) + if (IsNativeTypeNameEquivalent(nativeName, name)) { - nativeTypeName = null; + nativeName = null; } - GenerateTransparentStruct(name, escapedName, typeName, nativeTypeName, PInvokeGeneratorTransparentStructKind.FnPtr); + var desc = new TransparentStructDesc() { + ParentName = name, + Name = escapedName, + NativeName = nativeName, + Type = type, + NativeType = nativeType, + Kind = PInvokeGeneratorTransparentStructKind.FnPtr + }; + GenerateTransparentStruct(desc); } else { diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 6e04212e..51b6df0f 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -973,14 +973,26 @@ static void GenerateTransparentStructs(PInvokeGenerator generator, Stream? strea var kind = transparentStruct.Value.Kind; generator.StartUsingOutputBuilder(name); - generator.GenerateTransparentStruct(name, name, type, null, kind); + + var desc = new TransparentStructDesc() { + ParentName = name, + Name = name, + Type = type, + Kind = kind + }; + generator.GenerateTransparentStruct(desc); + generator.StopUsingOutputBuilder(); } } } - private void GenerateTransparentStruct(string parentName, string name, string type, string? nativeTypeName, PInvokeGeneratorTransparentStructKind kind) + private void GenerateTransparentStruct(in TransparentStructDesc desc) { + var name = desc.Name; + var type = desc.Type; + var kind = desc.Kind; + var isTypePointer = type.Contains('*', StringComparison.Ordinal); var sw = StartCSharpCode(); @@ -1001,15 +1013,15 @@ private void GenerateTransparentStruct(string parentName, string name, string ty if (Config.GenerateDocIncludes) { sw.WriteIndented("/// "); } - if (nativeTypeName is not null) + if (desc.NativeName is not null) { - sw.AddNativeTypeNameAttribute(nativeTypeName); + sw.AddNativeTypeNameAttribute(desc.NativeName); } sw.WriteIndented("public readonly "); @@ -1034,6 +1046,10 @@ private void GenerateTransparentStruct(string parentName, string name, string ty sw.WriteIndentedLine('{'); + if (desc.NativeType is not null) + { + sw.AddNativeTypeNameAttribute(desc.NativeType); + } sw.WriteIndented(" public readonly "); sw.Write(type); sw.WriteLine(" Value;"); From 839e518ba02be152ef8fdfefd9b9959f1bc6ee0a Mon Sep 17 00:00:00 2001 From: TechPizza <23627133+TechnologicalPizza@users.noreply.github.com> Date: Wed, 17 Jan 2024 17:49:49 +0100 Subject: [PATCH 10/11] Improve indentation in GenerateTransparentStruct --- .../PInvokeGenerator.cs | 640 +++++++++--------- 1 file changed, 330 insertions(+), 310 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 51b6df0f..2c679bdd 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -1044,323 +1044,350 @@ private void GenerateTransparentStruct(in TransparentStructDesc desc) } sw.WriteLine("IFormattable"); - sw.WriteIndentedLine('{'); - - if (desc.NativeType is not null) + sw.WriteBlockStart(); { - sw.AddNativeTypeNameAttribute(desc.NativeType); - } - sw.WriteIndented(" public readonly "); - sw.Write(type); - sw.WriteLine(" Value;"); - sw.WriteDivider(); + if (desc.NativeType is not null) + { + sw.AddNativeTypeNameAttribute(desc.NativeType); + } + sw.WriteIndented("public readonly "); + sw.Write(type); + sw.WriteLine(" Value;"); + sw.WriteDivider(); - // All transparent structs be created directly from the underlying type + // All transparent structs be created directly from the underlying type - sw.WriteIndented(" public "); - sw.Write(name); - sw.Write('('); - sw.Write(type); - sw.WriteLine(" value)"); - sw.WriteIndentedLine(" {"); - sw.WriteIndentedLine(" Value = value;"); - sw.WriteIndentedLine(" }"); - sw.WriteDivider(); + sw.WriteIndented("public "); + sw.Write(name); + sw.Write('('); + sw.Write(type); + sw.WriteLine(" value)"); + sw.WriteBlockStart(); + sw.WriteIndentedLine("Value = value;"); + sw.WriteBlockEnd(); - if (IsTransparentStructHandle(kind) || (kind == PInvokeGeneratorTransparentStructKind.HandleVulkan)) - { - // Handle like transparent structs define a NULL member + sw.WriteDivider(); - if (kind == PInvokeGeneratorTransparentStructKind.HandleWin32) + if (IsTransparentStructHandle(kind) || (kind == PInvokeGeneratorTransparentStructKind.HandleVulkan)) { - sw.WriteIndented(" public static "); + // Handle like transparent structs define a NULL member + + if (kind == PInvokeGeneratorTransparentStructKind.HandleWin32) + { + sw.WriteIndented("public static "); + sw.Write(name); + sw.Write(" INVALID_VALUE => new "); + sw.Write(name); + + if (isTypePointer) + { + sw.Write("(("); + sw.Write(type); + sw.WriteLine(")(-1));"); + } + else + { + sw.WriteLine("(-1);"); + } + + sw.WriteDivider(); + } + + sw.WriteIndented("public static "); sw.Write(name); - sw.Write(" INVALID_VALUE => new "); + sw.Write(" NULL => new "); sw.Write(name); if (isTypePointer) { - sw.Write("(("); - sw.Write(type); - sw.WriteLine(")(-1));"); + sw.WriteLine("(null);"); } else { - sw.WriteLine("(-1);"); + sw.WriteLine("(0);"); } sw.WriteDivider(); } - - sw.WriteIndented(" public static "); - sw.Write(name); - sw.Write(" NULL => new "); - sw.Write(name); - - if (isTypePointer) - { - sw.WriteLine("(null);"); - } - else + 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.WriteIndented("public static "); + sw.Write(name); + sw.Write(" TRUE => new "); + sw.Write(name); + sw.WriteLine("(1);"); + sw.WriteDivider(); } - sw.WriteDivider(); - } - else if (IsTransparentStructBoolean(kind)) - { - // Boolean like transparent structs define FALSE and TRUE members + if (IsTransparentStructComparable(kind)) + { + // Non-FnPtr transparent structs support equality and relational comparisons with themselves - sw.WriteIndented(" public static "); - sw.Write(name); - sw.Write(" FALSE => new "); - sw.Write(name); - sw.WriteLine("(0);"); - sw.WriteDivider(); + sw.WriteIndented("public static bool operator ==("); + sw.Write(name); + sw.Write(" left, "); + sw.Write(name); + sw.WriteLine(" right) => left.Value == right.Value;"); + sw.WriteDivider(); - sw.WriteIndented(" public static "); - sw.Write(name); - sw.Write(" TRUE => new "); - sw.Write(name); - sw.WriteLine("(1);"); - sw.WriteDivider(); - } + sw.WriteIndented("public static bool operator !=("); + sw.Write(name); + sw.Write(" left, "); + sw.Write(name); + sw.WriteLine(" right) => left.Value != right.Value;"); + 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.WriteDivider(); - sw.WriteIndented(" public static bool operator ==("); - sw.Write(name); - sw.Write(" left, "); - sw.Write(name); - sw.WriteLine(" right) => left.Value == right.Value;"); - sw.WriteDivider(); + sw.WriteIndented("public static bool operator <=("); + sw.Write(name); + sw.Write(" left, "); + sw.Write(name); + sw.WriteLine(" right) => left.Value <= right.Value;"); + sw.WriteDivider(); - sw.WriteIndented(" public static bool operator !=("); - sw.Write(name); - sw.Write(" left, "); - sw.Write(name); - sw.WriteLine(" right) => left.Value != right.Value;"); - sw.WriteDivider(); + sw.WriteIndented("public static bool operator >("); + sw.Write(name); + sw.Write(" left, "); + sw.Write(name); + sw.WriteLine(" right) => left.Value > right.Value;"); + sw.WriteDivider(); - sw.WriteIndented(" public static bool operator <("); - sw.Write(name); - sw.Write(" left, "); - sw.Write(name); - sw.WriteLine(" right) => left.Value < right.Value;"); - sw.WriteDivider(); + sw.WriteIndented("public static bool operator >=("); + sw.Write(name); + sw.Write(" left, "); + sw.Write(name); + sw.WriteLine(" right) => left.Value >= right.Value;"); + sw.WriteDivider(); + } - sw.WriteIndented(" public static bool operator <=("); - sw.Write(name); - sw.Write(" left, "); - sw.Write(name); - sw.WriteLine(" right) => left.Value <= right.Value;"); - sw.WriteDivider(); + if (IsTransparentStructHandle(kind)) + { + // Handle like transparent structs can be cast to/from void* - sw.WriteIndented(" public static bool operator >("); - sw.Write(name); - sw.Write(" left, "); - sw.Write(name); - sw.WriteLine(" right) => left.Value > right.Value;"); - sw.WriteDivider(); + sw.WriteIndented("public static explicit operator "); + sw.Write(name); + sw.Write("(void* value) => new "); + sw.Write(name); - sw.WriteIndented(" public static bool operator >=("); - sw.Write(name); - sw.Write(" left, "); - sw.Write(name); - sw.WriteLine(" right) => left.Value >= right.Value;"); - sw.WriteDivider(); - } + if (type.Equals("void*", StringComparison.Ordinal)) + { + sw.WriteLine("(value);"); + } + else + { + if (!IsUnsigned(type)) + { + sw.Write("unchecked"); + } - if (IsTransparentStructHandle(kind)) - { - // Handle like transparent structs can be cast to/from void* + sw.Write("(("); + sw.Write(type); + sw.WriteLine(")(value));"); + } + sw.WriteDivider(); - sw.WriteIndented(" public static explicit operator "); - sw.Write(name); - sw.Write("(void* value) => new "); - sw.Write(name); + sw.WriteIndented("public static implicit operator void*("); + sw.Write(name); - if (type.Equals("void*", StringComparison.Ordinal)) - { - sw.WriteLine("(value);"); - } - else - { - if (!IsUnsigned(type)) + if (isTypePointer) { - sw.Write("unchecked"); + sw.WriteLine(" value) => value.Value;"); } + else + { + var isUnchecked = !IsUnsigned(type); + sw.Write(" value) => "); - sw.Write("(("); - sw.Write(type); - sw.WriteLine(")(value));"); - } - sw.WriteDivider(); + if (isUnchecked) + { + sw.Write("unchecked("); + } + sw.Write("(void*)(value.Value)"); - sw.WriteIndented(" public static implicit operator void*("); - sw.Write(name); + if (isUnchecked) + { + sw.Write(")"); + } + sw.WriteDivider(); + } - if (isTypePointer) - { - sw.WriteLine(" value) => value.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.WriteIndented("public static explicit operator "); + sw.Write(name); + sw.Write("(HANDLE value) => new "); + sw.Write(name); + sw.WriteLine("(value);"); + sw.WriteDivider(); + + sw.WriteIndented("public static implicit operator HANDLE("); + sw.Write(name); + sw.WriteLine(" value) => new HANDLE(value.Value);"); + sw.WriteDivider(); + } } - else + else if (IsTransparentStructBoolean(kind)) { - var isUnchecked = !IsUnsigned(type); - sw.Write(" value) => "); + // Boolean like transparent structs define conversion to/from bool + // and support for usage in bool like scenarios. + + sw.WriteIndented("public static implicit operator bool("); + sw.Write(name); + sw.WriteLine(" value) => value.Value != 0;"); + sw.WriteDivider(); - if (isUnchecked) + sw.WriteIndented("public static implicit operator "); + sw.Write(name); + sw.Write("(bool value) => new "); + sw.Write(name); + + if (type.Equals("int", StringComparison.Ordinal)) { - sw.Write("unchecked("); + sw.WriteLine("(value ? 1 : 0);"); } - sw.Write("(void*)(value.Value)"); - - if (isUnchecked) + else if (type.Equals("uint", StringComparison.Ordinal)) { - sw.Write(")"); + sw.WriteLine("(value ? 1u : 0u);"); + } + else + { + sw.Write("(("); + sw.Write(type); + sw.WriteLine(")(value ? 1u : 0u);"); } - sw.WriteDivider(); - } - - sw.WriteDivider(); - if ((kind == PInvokeGeneratorTransparentStructKind.HandleWin32) && !name.Equals("HANDLE", StringComparison.Ordinal)) - { - // Win32 handle like transparent structs can also be cast to/from HANDLE + sw.WriteDivider(); - sw.WriteIndented(" public static explicit operator "); + sw.WriteIndented("public static bool operator false("); sw.Write(name); - sw.Write("(HANDLE value) => new "); - sw.Write(name); - sw.WriteLine("(value);"); + sw.WriteLine(" value) => value.Value == 0;"); sw.WriteDivider(); - sw.WriteIndented(" public static implicit operator HANDLE("); + sw.WriteIndented("public static bool operator true("); sw.Write(name); - sw.WriteLine(" value) => new HANDLE(value.Value);"); + sw.WriteLine(" value) => value.Value != 0;"); sw.WriteDivider(); } - } - else if (IsTransparentStructBoolean(kind)) - { - // Boolean like transparent structs define conversion to/from bool - // and support for usage in bool like scenarios. - sw.WriteIndented(" public static implicit operator bool("); - sw.Write(name); - sw.WriteLine(" value) => value.Value != 0;"); - sw.WriteDivider(); + // All transparent structs define casts to/from the various integer types - 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)) + if (kind == PInvokeGeneratorTransparentStructKind.FnPtr) { - sw.WriteLine("(value ? 1u : 0u);"); + OutputConversions(sw, name, type, kind, type); } else { - sw.Write("(("); - sw.Write(type); - sw.WriteLine(")(value ? 1u : 0u);"); + 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.WriteDivider(); + OutputConversions(sw, name, type, kind, "nint"); + OutputConversions(sw, name, type, kind, "nuint"); - sw.WriteIndented(" public static bool operator false("); - sw.Write(name); - sw.WriteLine(" value) => value.Value == 0;"); - sw.WriteDivider(); + if (IsTransparentStructComparable(kind)) + { + // Non-FnPtr transparent structs override CompareTo, Equals, GetHashCode - sw.WriteIndented(" public static bool operator true("); - sw.Write(name); - sw.WriteLine(" value) => value.Value != 0;"); - sw.WriteDivider(); - } + 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 define casts to/from the various integer types + sw.WriteDivider(); - 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.WriteIndented("return (obj is null) ? 1 : throw new ArgumentException(\"obj is not an instance of "); + sw.Write(name); + sw.WriteLine(".\");"); + } + sw.WriteBlockEnd(); - OutputConversions(sw, name, type, kind, "nint"); - OutputConversions(sw, name, type, kind, "nuint"); + sw.WriteDivider(); - if (IsTransparentStructComparable(kind)) - { - // Non-FnPtr transparent structs override CompareTo, Equals, GetHashCode + sw.WriteIndented("public int CompareTo("); + sw.Write(name); + sw.Write(" other) => "); - sw.WriteIndentedLine(" public int CompareTo(object? obj)"); - sw.WriteIndentedLine(" {"); - sw.WriteIndented(" if (obj is "); - sw.Write(name); - sw.WriteLine(" other)"); - sw.WriteIndentedLine(" {"); - sw.WriteIndentedLine(" return CompareTo(other);"); - sw.WriteIndentedLine(" }"); - sw.WriteDivider(); - sw.WriteIndented(" return (obj is null) ? 1 : throw new ArgumentException(\"obj is not an instance of "); - sw.Write(name); - sw.WriteLine(".\");"); - sw.WriteIndentedLine(" }"); - sw.WriteDivider(); + if (isTypePointer) + { + sw.WriteLine("((nuint)(Value)).CompareTo((nuint)(other.Value));"); + } + else + { + sw.WriteLine("Value.CompareTo(other.Value);"); + } - sw.WriteIndented(" public int CompareTo("); - sw.Write(name); + sw.WriteDivider(); - if (isTypePointer) - { - sw.WriteLine(" other) => ((nuint)(Value)).CompareTo((nuint)(other.Value));"); - } - else - { - sw.WriteLine(" other) => Value.CompareTo(other.Value);"); - } + sw.WriteIndented("public override bool Equals(object? obj) => (obj is "); + sw.Write(name); + sw.WriteLine(" other) && Equals(other);"); - sw.WriteDivider(); + sw.WriteDivider(); - sw.WriteIndented(" public override bool Equals(object? obj) => (obj is "); - sw.Write(name); - sw.WriteLine(" other) && Equals(other);"); - sw.WriteDivider(); + sw.WriteIndented("public bool Equals("); + sw.Write(name); + sw.Write(" other) => "); - sw.WriteIndented(" public bool Equals("); - sw.Write(name); + if (isTypePointer) + { + sw.WriteLine("((nuint)(Value)).Equals((nuint)(other.Value));"); + } + else + { + sw.WriteLine("Value.Equals(other.Value);"); + } - if (isTypePointer) - { - sw.WriteLine(" other) => ((nuint)(Value)).Equals((nuint)(other.Value));"); - } - else - { - sw.WriteLine(" other) => Value.Equals(other.Value);"); + sw.WriteDivider(); + + sw.WriteIndented("public override int GetHashCode() => "); + + if (isTypePointer) + { + sw.Write("((nuint)(Value))"); + } + else + { + sw.Write("Value"); + } + + sw.WriteLine(".GetHashCode();"); + + sw.WriteDivider(); } - sw.WriteDivider(); + // All transparent structs override ToString - sw.WriteIndented(" public override int GetHashCode() => "); + sw.WriteIndented("public override string ToString() => "); if (isTypePointer) { @@ -1371,59 +1398,43 @@ private void GenerateTransparentStruct(in TransparentStructDesc desc) sw.Write("Value"); } - sw.WriteLine(".GetHashCode();"); - sw.WriteDivider(); - } + sw.Write(".ToString("); - // All transparent structs override ToString + if (IsTransparentStructHexBased(kind)) + { + var (typeSrcSize, typeDstSize, typeSign) = GetSizeAndSignOf(type); - sw.WriteIndented(" public override string ToString() => "); + if (typeSrcSize != typeDstSize) + { + sw.Write("(sizeof(nint) == 4) ? \"X8\" : \"X16\""); + } + else + { + sw.Write('"'); + sw.Write('X'); + sw.Write(typeSrcSize * 2); + sw.Write('"'); + } + } - if (isTypePointer) - { - sw.Write("((nuint)(Value))"); - } - else - { - sw.Write("Value"); - } + sw.WriteLine(");"); - sw.Write(".ToString("); + sw.WriteDivider(); - if (IsTransparentStructHexBased(kind)) - { - var (typeSrcSize, typeDstSize, typeSign) = GetSizeAndSignOf(type); + sw.WriteIndented("public string ToString(string? format, IFormatProvider? formatProvider) => "); - if (typeSrcSize != typeDstSize) + if (isTypePointer) { - sw.Write("(sizeof(nint) == 4) ? \"X8\" : \"X16\""); + sw.Write("((nuint)(Value))"); } else { - sw.Write('"'); - sw.Write('X'); - sw.Write(typeSrcSize * 2); - sw.Write('"'); + sw.Write("Value"); } - } - sw.WriteLine(");"); - sw.WriteDivider(); - - sw.WriteIndented(" public string ToString(string? format, IFormatProvider? formatProvider) => "); - - if (isTypePointer) - { - sw.Write("((nuint)(Value))"); + sw.WriteLine(".ToString(format, formatProvider);"); } - else - { - sw.Write("Value"); - } - - sw.WriteLine(".ToString(format, formatProvider);"); - - sw.WriteIndentedLine('}'); + sw.WriteBlockEnd(); StopCSharpCode(); @@ -1476,31 +1487,37 @@ static void OutputConversions(CSharpOutputBuilder sw, string name, string type, castFromKind = "explicit"; } - sw.WriteIndented(" public static "); + sw.WriteIndented("public static "); sw.Write(castFromKind); sw.Write(" operator "); sw.Write(name); sw.Write('('); sw.Write(target); - sw.Write(" value) => new "); - sw.Write(name); - sw.Write('('); - - if (castFromKind.Equals("explicit", StringComparison.Ordinal) || isPointerToNativeCast) + sw.Write(" value)"); + sw.BeginBody(isExpressionBody: true); { - sw.Write("unchecked(("); - sw.Write(type); - sw.Write(")("); - } + sw.Write("new "); + sw.Write(name); + sw.Write('('); - sw.Write("value"); + if (castFromKind.Equals("explicit", StringComparison.Ordinal) || isPointerToNativeCast) + { + sw.Write("unchecked(("); + sw.Write(type); + sw.Write(")("); + } - if (castFromKind.Equals("explicit", StringComparison.Ordinal) || isPointerToNativeCast) - { - sw.Write("))"); - } + sw.Write("value"); - sw.WriteLine(");"); + if (castFromKind.Equals("explicit", StringComparison.Ordinal) || isPointerToNativeCast) + { + sw.Write("))"); + } + + sw.Write(")"); + sw.WriteSemicolon(); + } + sw.EndBody(true); sw.WriteDivider(); // public static castToKind operator target(name value) => ((target)(value.Value)); @@ -1519,29 +1536,32 @@ static void OutputConversions(CSharpOutputBuilder sw, string name, string type, castToKind = "explicit"; } - sw.WriteIndented(" public static "); + sw.WriteIndented("public static "); sw.Write(castToKind); sw.Write(" operator "); sw.Write(target); sw.Write('('); sw.Write(name); - sw.Write(" value) => "); - - if (castToKind.Equals("explicit", StringComparison.Ordinal) || isPointerToNativeCast) + sw.Write(" value)"); + sw.BeginBody(isExpressionBody: true); { - sw.Write('('); - sw.Write(target); - sw.Write(")("); - } + if (castToKind.Equals("explicit", StringComparison.Ordinal) || isPointerToNativeCast) + { + sw.Write('('); + sw.Write(target); + sw.Write(")("); + } - sw.Write("value.Value"); + sw.Write("value.Value"); - if (castToKind.Equals("explicit", StringComparison.Ordinal) || isPointerToNativeCast) - { - sw.Write(')'); - } + if (castToKind.Equals("explicit", StringComparison.Ordinal) || isPointerToNativeCast) + { + sw.Write(')'); + } - sw.WriteLine(';'); + sw.WriteSemicolon(); + } + sw.EndBody(true); sw.WriteDivider(); } } @@ -3077,7 +3097,7 @@ private string GetRemappedCursorName(NamedDecl namedDecl, out string nativeTypeN remappedName += $"_e__{(recordDecl.IsUnion ? "Union" : "Struct")}"; } } - + return remappedName; } From 4b52fff1554a361c627870853cb07e0ba6a616f1 Mon Sep 17 00:00:00 2001 From: TechPizza <23627133+TechnologicalPizza@users.noreply.github.com> Date: Wed, 17 Jan 2024 18:06:47 +0100 Subject: [PATCH 11/11] Improve comment in IsFunctionPointer --- sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 2c679bdd..c1af5ff6 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -3806,7 +3806,8 @@ bool ForPointeeType(Cursor? cursor, Type pointeeType) return true; } - // Do not recurse of the pointee is a pointer. + // Do not recurse if the pointee is a pointer, + // we do not want to detect pointers to function pointers. return false; }