diff --git a/src/Generator/Extensions/LayoutFieldExtensions.cs b/src/Generator/Extensions/LayoutFieldExtensions.cs index 3f2a997d..d9ffabf1 100644 --- a/src/Generator/Extensions/LayoutFieldExtensions.cs +++ b/src/Generator/Extensions/LayoutFieldExtensions.cs @@ -12,6 +12,19 @@ namespace CppSharp.Extensions var prevFieldSize = previousField.QualifiedType.Type.Desugar().GetWidth(targetInfo) / 8; var unalignedOffset = previousField.Offset + prevFieldSize; var alignment = type.GetAlignment(targetInfo) / 8; + + if (type is ArrayType arrayType && arrayType.Type.Desugar().IsClass()) + { + // We have an array of structs. When we generate this field, we'll transform it into an fixed + // array of bytes to which the elements in the embedded struct array can be bound (via their + // accessors). At that point, the .Net subsystem will have no information in the generated + // class on which to base its own alignment calculation. Consequently, we must generate + // padding. Set alignment to indicate one-byte alignment which is consistent with the "fixed + // byte fieldName[]" we'll eventually generate so that the offset we return here mimics what + // will be the case once the struct[] -> byte[] transformation occurs. + alignment = 1; + } + var alignedOffset = (unalignedOffset + (alignment - 1)) & -alignment; return (int)alignedOffset; } diff --git a/src/Generator/Generators/CSharp/CSharpSources.cs b/src/Generator/Generators/CSharp/CSharpSources.cs index 8ed89aae..fe2e2006 100644 --- a/src/Generator/Generators/CSharp/CSharpSources.cs +++ b/src/Generator/Generators/CSharp/CSharpSources.cs @@ -837,7 +837,7 @@ namespace CppSharp.Generators.CSharp if (sequentialLayout && i > 0) { var padding = field.Offset - field.CalculateOffset(fields[i - 1], Context.TargetInfo); - + if (padding > 1) WriteLine($"internal fixed byte {field.Name}Padding[{padding}];"); else if (padding > 0) diff --git a/tests/CSharp/CSharp.Tests.cs b/tests/CSharp/CSharp.Tests.cs index b6ed1f51..e101983a 100644 --- a/tests/CSharp/CSharp.Tests.cs +++ b/tests/CSharp/CSharp.Tests.cs @@ -752,6 +752,7 @@ public unsafe class CSharpTests (typeof(ClassCustomTypeAlignment), CSharp.CSharp.ClassCustomTypeAlignmentOffsets), (typeof(ClassCustomObjectAlignment), CSharp.CSharp.ClassCustomObjectAlignmentOffsets), (typeof(ClassMicrosoftObjectAlignment), CSharp.CSharp.ClassMicrosoftObjectAlignmentOffsets), + (typeof(StructWithEmbeddedArrayOfStructObjectAlignment), CSharp.CSharp.StructWithEmbeddedArrayOfStructObjectAlignmentOffsets), }) { var internalType = type.GetNestedType("__Internal"); diff --git a/tests/CSharp/CSharp.cpp b/tests/CSharp/CSharp.cpp index e6c17fb0..5f88ec65 100644 --- a/tests/CSharp/CSharp.cpp +++ b/tests/CSharp/CSharp.cpp @@ -1888,3 +1888,9 @@ const unsigned ClassMicrosoftObjectAlignmentOffsets[4] offsetof(ClassMicrosoftObjectAlignment, i16), offsetof(ClassMicrosoftObjectAlignment, boolean), }; + +const unsigned StructWithEmbeddedArrayOfStructObjectAlignmentOffsets[2] +{ + offsetof(StructWithEmbeddedArrayOfStructObjectAlignment, boolean), + offsetof(StructWithEmbeddedArrayOfStructObjectAlignment, embedded_struct), +}; \ No newline at end of file diff --git a/tests/CSharp/CSharp.h b/tests/CSharp/CSharp.h index e794cc3b..be6a1cbc 100644 --- a/tests/CSharp/CSharp.h +++ b/tests/CSharp/CSharp.h @@ -1502,6 +1502,16 @@ struct DLL_API ClassMicrosoftObjectAlignment : ClassMicrosoftObjectAlignmentBase bool boolean; }; +struct DLL_API EmbeddedStruct +{ + int64_t i64; +}; +struct DLL_API StructWithEmbeddedArrayOfStructObjectAlignment +{ + bool boolean; + EmbeddedStruct embedded_struct[2]; +}; + class DLL_API ProtectedConstructorDestructor { protected: @@ -1512,6 +1522,7 @@ protected: DLL_API extern const unsigned ClassCustomTypeAlignmentOffsets[5]; DLL_API extern const unsigned ClassCustomObjectAlignmentOffsets[2]; DLL_API extern const unsigned ClassMicrosoftObjectAlignmentOffsets[4]; +DLL_API extern const unsigned StructWithEmbeddedArrayOfStructObjectAlignmentOffsets[2]; DLL_API const char* TestCSharpString(const char* in, CS_OUT const char** out); DLL_API const wchar_t* TestCSharpStringWide(const wchar_t* in, CS_OUT const wchar_t** out);