-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
StructLayouts that differ between architectures and/or operating systems #60573
Comments
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label. |
This seems like a great opportunity for customization in our struct marshalling source generator that @jkoritzinsky is working on. I'm hesitant to change any of our built-in marshalling support to enable this sort of functionality. /cc @elinor-fung |
I should mention that for my specific use cases I am working purely with blittable structures, avoiding marshaling where I can. So ideally this would affect both the managed and unmanaged layouts. |
@DaZombieKiller Let me elaborate on my primary concern about built-in support. The In order to improve this we would likely need some sentinel value to indicate – look somewhere else for the value. That isn't impossible but would require thought and coordinate with the Roslyn team. |
That makes a lot of sense, yeah. The example in the OP was more of a "this is what a potential solution to this could look like", moreso than a concrete proposal. If supporting differing layouts in the runtime itself proves to be too much of a headache, having APIs akin to |
Its likely also worth considering in what scenarios this occurs. If such APIs appear as part of particularly "hot calls", then it may be problematic to require marshalling to occur. It can also impact where changes become visible if pointers, refs, or arrays are involved. I'd expect that work in this area might not impact Much of this could be simpler if it was more trivial to support |
Note that the conditions that express the platform differences can be quite complex. For example "Windows and not ARM" here: runtime/src/libraries/System.Data.OleDb/src/OleDbStruct.cs Lines 112 to 116 in dfd618d
StructPackOverride to be general purpose enough.
|
This is a good point, and it got me thinking about potential alternatives. One that came to mind was something along the lines of: public interface IRuntimeStructLayout
{
static abstract int Pack { get; }
static abstract int Size { get; }
static abstract CharSet CharSet { get; }
}
public sealed class RuntimeStructLayoutAttribute<T> : Attribute
where T : IRuntimeStructLayout
{
public int Pack => T.Pack;
public int Size => T.Size;
public CharSet CharSet => T.CharSet;
} Which would be used like this: [RuntimeStructLayout<SomeCallbackLayout>]
public struct SomeCallback
{
}
sealed class SomeCallbackLayout : IRuntimeStructLayout
{
public static int Pack => OperatingSystem.IsWindows() ? 8 : 4;
public static int Size => 0;
public static CharSet CharSet => CharSet.None;
} I think such an approach would be fine if it were to be used for marshalling, but it's overall an unsatisfying solution because the requirement of code execution to get the values feels like it would be unsuitable for the managed layout (and thus wouldn't affect access through |
This solution would be incompatible with AOT compilation. It is equivalent to emitting the type definitions at runtime using |
Yeah, AOT was another concern I had. I suppose it could be theoretically possible if there were heavy restrictions placed on what the |
Could you say more about this? Doesn't Nuget hide the complexity from the consumer here? |
@danmoseley, in many senses yes. However, NuGet has a number of tracked issues here that prevent the experience from working cohesively. Some of the ones I can remember off the top of my head are:
In particular the some of the issues I've hit are that:
|
Tanner's comment effectively summarizes most of the issues I could think of. One of the largest concerns for my own use cases is that there is a massive amount of code duplication (there is a not-insignificant number of structs that need to have "runtime layout" here), so with per-RID assemblies a project will need to include each platform-specific assembly (assuming the application or library consuming the code isn't built for a specific RID). |
I would also like if it was possible to have a single defined structure for when other things like so exist as well:
As such I often would like to define a structure with all 5 members and use it with
Where the struct is for calling into native code and Reasonings? Well this could benefit situations like what is noticed in TerraFX.Interop.Windows where one might want both the 32 bit and 64 bit structures to some of the Windows APIs where the only difference is an added member under x64 or even ARM64. With something like this it could improve the codegen inside of TerraFX.Interop.Windows and also make the end users even more happy without adding a burden to @tannergooding for supporting x86 users at the moment and not just dropping 32 bit support entirely. |
Could we please not make assumptions about people's libraries and what would or would not be beneficial. It's fine to request a feature that you think would be beneficial. It is not fine to try and tie it into someone else's library without first checking with the author if it's even something impacting them. There are limitations to what can and cannot be supported here and structs having an additional member between 32-bit and 64-bit is something I've hit less than 10 times in 30k types. The different packing issue is likewise rare overall, but it is at least quite a bit more common across various libraries/ecosystems. Neither of these issues would actually need runtime support if there per-RID building, packing, and distributing was simpler. |
Today, if you need to wrap a native API that has differing structure layouts (namely, structure packing) between platforms, you're stuck in a very messy scenario with not many good options to proceed. An example of such a scenario is Valve's Steamworks API, which has a series of callback structures with packing that varies per platform (
8
on Windows,4
everywhere else).So, when faced with this problem, some solutions that come to mind are:
None of these are particularly appetizing, and all of them have significant drawbacks. Ideally, the runtime could provide some method to annotate structures to allow their layout to change depending on the current platform. If we ignore everything except structure packing, such a feature could look like:
Which would define a struct with a packing value of
8
on Windows, and4
everywhere else.cc @jkoritzinsky @AaronRobinsonMSFT
The text was updated successfully, but these errors were encountered: