diff --git a/sources/ClangSharp.PInvokeGenerator/Abstractions/StructDesc.cs b/sources/ClangSharp.PInvokeGenerator/Abstractions/StructDesc.cs index eaf8606d..a63b3c65 100644 --- a/sources/ClangSharp.PInvokeGenerator/Abstractions/StructDesc.cs +++ b/sources/ClangSharp.PInvokeGenerator/Abstractions/StructDesc.cs @@ -83,21 +83,12 @@ public StructLayoutAttribute? LayoutAttribute { Debug.Assert(layout.Kind == LayoutKind.Explicit); - var attribute = new StructLayoutAttribute(layout.Kind); - - if (layout.Pack != 0) - { - attribute.Pack = (int)layout.Pack; - } - - return attribute; + return new StructLayoutAttribute(layout.Kind); } - if (layout.Pack != 0) + if (layout.Pack is not null) { - return new StructLayoutAttribute(layout.Kind) { - Pack = (int)layout.Pack - }; + return new StructLayoutAttribute(layout.Kind); } return null; @@ -110,7 +101,7 @@ public struct LayoutDesc public long Alignment64 { get; set; } public long Size32 { get; set; } public long Size64 { get; set; } - public long Pack { get; set; } + public string? Pack { get; set; } public long MaxFieldAlignment { get; set; } public LayoutKind Kind { get; set; } } diff --git a/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs index 1dd7df55..5d87fc1f 100644 --- a/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs @@ -746,10 +746,10 @@ public void BeginStruct(in StructDesc desc) WriteIndented("[StructLayout(LayoutKind."); Write(desc.LayoutAttribute.Value); - if (desc.LayoutAttribute.Pack != 0) + if (desc.Layout.Pack is not null) { Write(", Pack = "); - Write(desc.LayoutAttribute.Pack); + Write(desc.Layout.Pack); } WriteLine(")]"); diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs index e2c75b5c..a7143c80 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs @@ -1467,6 +1467,11 @@ private void VisitRecordDecl(RecordDecl recordDecl) baseTypeNames = baseTypeNamesBuilder.ToArray(); } + if (!TryGetRemappedValue(recordDecl, _config.WithPackings, out var pack)) + { + pack = alignment < maxAlignm ? alignment.ToString(CultureInfo.InvariantCulture) : null; + } + var desc = new StructDesc { AccessSpecifier = GetAccessSpecifier(recordDecl, matchStar: true), EscapedName = escapedName, @@ -1478,7 +1483,7 @@ private void VisitRecordDecl(RecordDecl recordDecl) Alignment64 = alignment64, Size32 = size32, Size64 = size64, - Pack = alignment < maxAlignm ? alignment : 0, + Pack = pack, MaxFieldAlignment = maxAlignm, Kind = layoutKind }, @@ -1515,7 +1520,7 @@ private void VisitRecordDecl(RecordDecl recordDecl) if (desc.LayoutAttribute is not null) { - withAttributes.Add($"StructLayout(LayoutKind.{desc.LayoutAttribute.Value}{((desc.LayoutAttribute.Pack != 0) ? $", Pack = {desc.LayoutAttribute.Pack}" : "")})"); + withAttributes.Add($"StructLayout(LayoutKind.{desc.LayoutAttribute.Value}{((desc.Layout.Pack is not null) ? $", Pack = {desc.Layout.Pack}" : "")})"); _ = withUsings.Add("System.Runtime.InteropServices"); } @@ -2817,6 +2822,11 @@ void VisitConstantOrIncompleteArrayFieldDecl(RecordDecl recordDecl, FieldDecl co AddDiagnostic(DiagnosticLevel.Info, $"{escapedName} (constant array field) has a size of 0", constantOrIncompleteArray); } + if (!TryGetRemappedValue(recordDecl, _config.WithPackings, out var pack)) + { + pack = alignment < maxAlignm ? alignment.ToString(CultureInfo.InvariantCulture) : null; + } + var desc = new StructDesc { AccessSpecifier = accessSpecifier, EscapedName = escapedName, @@ -2826,7 +2836,7 @@ void VisitConstantOrIncompleteArrayFieldDecl(RecordDecl recordDecl, FieldDecl co Alignment64 = alignment64, Size32 = size32, Size64 = size64, - Pack = alignment < maxAlignm ? alignment : 0, + Pack = pack, MaxFieldAlignment = maxAlignm, Kind = LayoutKind.Sequential }, diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs index 6d7fc4c7..cb11991b 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs @@ -45,6 +45,7 @@ public sealed class PInvokeGeneratorConfiguration private readonly SortedDictionary _withTransparentStructs; private readonly SortedDictionary _withTypes; private readonly SortedDictionary> _withUsings; + private readonly SortedDictionary _withPackings; private PInvokeGeneratorConfigurationOptions _options; @@ -94,6 +95,7 @@ public PInvokeGeneratorConfiguration(string defaultNamespace, string outputLocat _withTransparentStructs = new SortedDictionary(); _withTypes = new SortedDictionary(); _withUsings = new SortedDictionary>(); + _withPackings = new SortedDictionary(); if ((outputMode == PInvokeGeneratorOutputMode.Xml) && !options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateMultipleFiles) && (options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateTestsNUnit) || options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateTestsXUnit))) { @@ -528,6 +530,20 @@ public IReadOnlyDictionary> WithUsings } } + [AllowNull] + public IReadOnlyDictionary WithPackings + { + get + { + return _withPackings; + } + + init + { + AddRange(_withPackings, value); + } + } + public static AccessSpecifier ConvertStringToAccessSpecifier(string input) { if (input.Equals("internal", StringComparison.OrdinalIgnoreCase)) diff --git a/sources/ClangSharp.PInvokeGenerator/XML/XmlOutputBuilder.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/XML/XmlOutputBuilder.VisitDecl.cs index 2c321370..e3e7b12d 100644 --- a/sources/ClangSharp.PInvokeGenerator/XML/XmlOutputBuilder.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/XML/XmlOutputBuilder.VisitDecl.cs @@ -290,10 +290,10 @@ public void BeginStruct(in StructDesc info) _ = _sb.Append(info.LayoutAttribute.Value); _ = _sb.Append('"'); - if (info.LayoutAttribute.Pack != 0) + if (info.Layout.Pack is not null) { _ = _sb.Append(" pack=\""); - _ = _sb.Append(info.LayoutAttribute.Pack); + _ = _sb.Append(info.Layout.Pack); _ = _sb.Append('"'); } } diff --git a/sources/ClangSharpPInvokeGenerator/Program.cs b/sources/ClangSharpPInvokeGenerator/Program.cs index f708bf16..9303d0d4 100644 --- a/sources/ClangSharpPInvokeGenerator/Program.cs +++ b/sources/ClangSharpPInvokeGenerator/Program.cs @@ -57,6 +57,7 @@ public class Program private static readonly Option s_withTransparentStructNameValuePairs; private static readonly Option s_withTypeNameValuePairs; private static readonly Option s_withUsingNameValuePairs; + private static readonly Option s_withPackingNameValuePairs; private static readonly TwoColumnHelpRow[] s_configOptions = new TwoColumnHelpRow[] @@ -161,6 +162,7 @@ static Program() s_withTransparentStructNameValuePairs = GetWithTransparentStructOption(); s_withTypeNameValuePairs = GetWithTypeOption(); s_withUsingNameValuePairs = GetWithUsingOption(); + s_withPackingNameValuePairs = GetWithPackingOption(); s_rootCommand = new RootCommand("ClangSharp P/Invoke Binding Generator") { @@ -198,7 +200,8 @@ static Program() s_withSuppressGCTransitions, s_withTransparentStructNameValuePairs, s_withTypeNameValuePairs, - s_withUsingNameValuePairs + s_withUsingNameValuePairs, + s_withPackingNameValuePairs }; Handler.SetHandler(s_rootCommand, (Action)Run); } @@ -255,6 +258,7 @@ public static void Run(InvocationContext context) var withTransparentStructNameValuePairs = context.ParseResult.GetValueForOption(s_withTransparentStructNameValuePairs) ?? Array.Empty(); var withTypeNameValuePairs = context.ParseResult.GetValueForOption(s_withTypeNameValuePairs) ?? Array.Empty(); var withUsingNameValuePairs = context.ParseResult.GetValueForOption(s_withUsingNameValuePairs) ?? Array.Empty(); + var withPackingNameValuePairs = context.ParseResult.GetValueForOption(s_withPackingNameValuePairs) ?? Array.Empty(); var versionResult = context.ParseResult.FindResultFor(s_versionOption); @@ -295,6 +299,7 @@ public static void Run(InvocationContext context) ParseKeyValuePairs(withTransparentStructNameValuePairs, errorList, out Dictionary withTransparentStructs); ParseKeyValuePairs(withTypeNameValuePairs, errorList, out Dictionary withTypes); ParseKeyValuePairs(withUsingNameValuePairs, errorList, out Dictionary> withUsings); + ParseKeyValuePairs(withPackingNameValuePairs, errorList, out Dictionary withPackings); foreach (var key in withTransparentStructs.Keys) { @@ -681,6 +686,7 @@ public static void Run(InvocationContext context) WithTransparentStructs = withTransparentStructs, WithTypes = withTypes, WithUsings = withUsings, + WithPackings = withPackings, }; if (config.GenerateMacroBindings) @@ -1288,4 +1294,15 @@ private static Option GetWithUsingOption() AllowMultipleArgumentsPerToken = true }; } + + private static Option GetWithPackingOption() + { + return new Option( + aliases: new string[] { "--with-packing", "-wp" }, + description: "Overrides the StructLayoutAttribute.Pack property for the given type.", + getDefaultValue: Array.Empty + ) { + AllowMultipleArgumentsPerToken = true + }; + } } diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/PInvokeGeneratorTest.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/PInvokeGeneratorTest.cs index fab038cf..a85ec5c4 100644 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/PInvokeGeneratorTest.cs +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/PInvokeGeneratorTest.cs @@ -106,6 +106,7 @@ private static async Task ValidateGeneratedBindingsAsync(string inputContents, s WithTransparentStructs = withTransparentStructs, WithTypes = withTypes, WithUsings = withUsings, + WithPackings = null, }; using (var pinvokeGenerator = new PInvokeGenerator(config, (path) => outputStream))