C
C#•2mo ago
redeexpressos

compile-time `IntrPtr` size for union-like structs with `FieldOffset`

hi. im developing a FFI in Rust in order to allow C# code to call methods from it. im running through an low-level issue, but maybe there is a solution. What I want to represent is something like:
enum Encryption {
NoEncryption,
EncryptionKey(*mut c_char),
EncryptionKeys(EncryptionKeys),
}
enum Encryption {
NoEncryption,
EncryptionKey(*mut c_char),
EncryptionKeys(EncryptionKeys),
}
so my idea was to represent this as the following in C#:
[StructLayout(LayoutKind.Explicit)]
public unsafe struct Encryption
{
[FieldOffset(0)]
public EncryptionTag Tag; // discriminator

[FieldOffset(4)]
public IntPtr EncryptionKey; // pointer to c string, will cast later

[FieldOffset(4)]
public IntPtr EncryptionKeys; // pointer to another struct, will cast later
}
[StructLayout(LayoutKind.Explicit)]
public unsafe struct Encryption
{
[FieldOffset(0)]
public EncryptionTag Tag; // discriminator

[FieldOffset(4)]
public IntPtr EncryptionKey; // pointer to c string, will cast later

[FieldOffset(4)]
public IntPtr EncryptionKeys; // pointer to another struct, will cast later
}
however, there is a issue. IntPtr size can differ on different architectures, which means my memory alignment offsets are wrong. I've tried [FieldOffset(4)] but compiler does not like it. Is there a good solution for this?
10 Replies
ero
ero•2mo ago
they're both nint; why bother with 2 fields? but
public struct Encryption
{
public EncryptionTag Tag;
private EncryptionUnion _union;

public nint EncryptionKey => _union.Key;
public nint EncryptionKeys => _union.Keys;

[StructLayout(LayoutKind.Explicit)]
private struct EncryptionUnion
{
[FieldOffset(0)]
public nint Key;

[FieldOffset(0)]
public nint Keys;
}
}
public struct Encryption
{
public EncryptionTag Tag;
private EncryptionUnion _union;

public nint EncryptionKey => _union.Key;
public nint EncryptionKeys => _union.Keys;

[StructLayout(LayoutKind.Explicit)]
private struct EncryptionUnion
{
[FieldOffset(0)]
public nint Key;

[FieldOffset(0)]
public nint Keys;
}
}
redeexpressos
redeexpressosOP•2mo ago
interesting approach I suppose my top-struct (Encryption) will need [StructLayout(LayoutKind.Sequential)] what if they are not both nint? e.g:
[StructLayout(LayoutKind.Explicit)]
public unsafe struct Encryption
{
[FieldOffset(0)]
public EncryptionTag Tag; // discriminator

[FieldOffset(4)]
public IntPtr EncryptionKey; // pointer to c string, will cast later

[FieldOffset(4)]
public EncKeys* EncryptionKeys; // pointer to another struct, will cast later
}
[StructLayout(LayoutKind.Explicit)]
public unsafe struct Encryption
{
[FieldOffset(0)]
public EncryptionTag Tag; // discriminator

[FieldOffset(4)]
public IntPtr EncryptionKey; // pointer to c string, will cast later

[FieldOffset(4)]
public EncKeys* EncryptionKeys; // pointer to another struct, will cast later
}
ero
ero•2mo ago
no, structs are sequential by default same thing as the solution i provided. i was just saying that you would not need to differentiate between Key and Keys if they're both the same type, just because they have 1 letter difference
redeexpressos
redeexpressosOP•2mo ago
so I can be sure that sizeof(T*) will always be sizeof(IntPtr)?
ero
ero•2mo ago
yes
redeexpressos
redeexpressosOP•2mo ago
I have another question, is SafeHandle a good replacement for IntPtr? ( T: SafeHandle)
ero
ero•2mo ago
for interop? no
redeexpressos
redeexpressosOP•2mo ago
I was looking at this: https://github.com/KodrAus/rust-csharp-ffi/blob/master/dotnet/Db.Storage/Native/Bindings.cs#L59 https://github.com/KodrAus/rust-csharp-ffi/blob/master/dotnet/Db.Storage/Native/StoreHandle.cs#L6 and was intrigued how does this work under the hood.. also https://www.meziantou.net/stop-using-intptr-for-dealing-with-system-handles.htm:
SafeHandles are well-integrated with P/Invoke. You can use SafeHandle-derived class in the definition of the method instead of IntPtr, so they are strongly-typed.
ero
ero•2mo ago
interesting, that's news to me. i say give it a try if you think it benefits you. for further questions i would recommend turning to #allow-unsafe-blocks the users there will know a lot more about this topic
redeexpressos
redeexpressosOP•2mo ago
Thank you very much 🙂

Did you find this page helpful?