Change the struct size based on current architecture
Hi, I'm working on porting a C library to C# that contains architecture-dependent structs. Here's my C code:
the problem is that my struct has conditional fields that only exist in 64-bit builds. In C, this is handled with #ifdef Is64BitEnviroment, but I need a C# equivalent that works with "Any CPU" configuration.
i tried to copy some runtime code on the same topic. which was the Intptr struct. but it did not work. for my 64 bit windows pc with Any Cpu configuration. it produce the 32 bit (x86) code .
How can I define a C# struct that correctly matches this architecture-dependent C struct when compiling with "Any CPU"?
52 Replies
GitHub
Target runtime preprocessor directives · Issue #32627 · dotnet/docs
Background and motivation Hello, I have an use case of preprocessor directives in my current project and I missed them for when you have to compile a code only for a specific OS. Related Stack Over...
it's the same in dotnet

there are msbuild properties for this too
you can pass these to the compiler either by defining them or on the command line
if you have specific code for different sizes, this won't work if you only have a single any cpu build
the directives are only resolved at compile time
you may want to compile both versions in this case and runtime switch to select the right one
I assume that's what most people will suggest
@Anton just to be clear you compile it twice one on x86 and then x64. and use use them accordingly.
how 2 version can be deal with nuget
well if the user code references that struct, they will have to also build for a set word size
i would imagine
if you want to select which dll to load at runtime based on the target arch, having compiled the app for any CPU, I don't know
there might be a mechanism to allow this to happen aitomatically
if the user code does a word size specific build, it will reference the right dll in the nuget package
you can include multiple artifacts in a nuget package fyi
it's kind of impossible to Google, I asked chat gpt, it doesn't know of a built-in thing for this
so it probably doesn't exist, but you can't trust the robot
you might have to compile against a reference assembly, and then link it at runtime manually. I think that should be possible
the only issue is that I'd imagine that reference assembly will have to exclude the arch dependent struct
otherwise it probably won't link if you use it
you'll have to type-erase it somehow (handles, ids, pointers, etc.)
The easiest way to do this is just to make two structs.
The build system for arch-specific libraries in a single package or project is pretty much non-existant. So you'd otherwise have to put that together yourself.
actually i don't want the end user to bother with architecture staff. if you think about intptr struct for reference. when we use that as a c# developer we usually don't think what architecture we are on we just use it the dotnet runtime will mostly take care of that. i'm really looking for something like that. i know its way too specific. but on a large code base the last thing i want to messing around would be architecture.
its not a good idea for a big project
You can use IntPtr.
imean think about it the windowManager_t struct where ever i pass it to another method i have to write 2 version of same code
But pointer size is a small part of differences that might exist between architectures.
i need to access those method and those valudes
If you don't want the end user to deal with it, hide it all behind an API the end user interacts with.
how? the struct size suppose to determine at runtime. how can i do that
Don't expose those structs to the end user.
its hard. i mean windowManager_t struct is kind of like a whole game that need to pass arround
Pass around a reference to it. Like every other native library wrapper that exists in .NET.
im kind of trying to remove the native c code. you can think it like this. my c# code use to call some c code then those c code use to call the platform prific code like user32 and libc. now im trying the remove the c code and create binding to user32 and lic binding.
so i already have the wrapper
Sometimes you have to mke different structs for calling User32.
And .NET itself especially does.
Such things are especially true between implementations of libc.
but those struct for the native library but im taking about my c code.windowManager_t is something i wrote. and the platform spefic section is done because some native struct require those.
Well why did you write it different for each platform?
Don't. Sounds like you built a pretty non-portable C library.
i mean those method contain logic to deter mine the platform and run the code
Iin reality that's fine. Huge numbers of C libraries are non-portable. But this is hte consequence when trying to wrap them in a portable language/framework.
which was easy to port but the problem is the architecture
Like, just as an example, the libc 'stat' structure is different between libc implementations. Has a few fields that differ between OS X and Linux/glibc. The consequence is if you're wrapping it in .NET, you have to declare two structs.
shrug that's just the way it is
you lets say i wrote 2 version on that struct like windowManager_t_32 and windowManager_t_64
then lets say i have a struct which has 2 field like windowManager_t* pre; windowManager_t* post
that mean i need to have same 2 version of that struct too
You could make that second struct a void* or intptr.
And just cast it.
Or you could make two structs for that as well.
Either way, you would usually never make those public. You would make good looking .NET classes public.
its not solving the issue it just kicking the can
With properties and methods.
There is no "solving the issue": you wrote crap C code. You have to wrap it with crap C# code.
If you want a portable API that places no burdens on other languages consuming it, that code has to be portable. That means not exposing platform specific things.
is it really impossible to hide it in c# end?
It's impossible to hide it in any language.
but dotnet intptr does able to hide it.
You have code which is fundamentally a different layout on different architectures. From your little example, actual fields are missing. That means anybody interacting with that interface inherits that burden.
i mean x11 does that
Because that is the ONE and only one thing that can be both discovered by the runtime, and inferred about external libraries: native pointer size (and to be fair there are even exceptions about that).
i pick that form x11
Yeah, and as I said, there are non-portable interfaces all over: stat being a big one.
And we all periodically suffer because of it.
But there are lots of ways and examples to write nice good clean C that other people can consume from other platforms.
For instance, don't expose the requirement that users know the content/layout of structs.
Provide functions for all interaction.
Don't pass structs around. Pass pointers to them. Don't require external users to alloc or free them: provide functions for that.
@wasabi just listen my thought for a sec.
so the IntPtr struct has 2 preprocessor like TARGET_64BIT and TARGET_32BIT now when i copy their preprocessor it does not work for any cpu configuration. but when microsoft use it for the Intptr struct how does that working
even thought i select any cpu
is it like the TARGET_64BIT preprocessor have something extra than a normal preprocessor
They don't use preprocessors for IntPtr do they?
Or are you talking about the PAL stuff?
Ahh. I see what you're referring to. They do use that for the struct declaration. What they do is distribute different versions of System.Private.CoreLib.
But it's important to understand that IntPtr is a special magic type, as well.
yes this one

yes. that assembly is not "Any CPU," so what they do will not work for you
no actually for a 64 bit windows pc running vs on Any cpu the 64 section in enable but when i copy the same definition for Target64_bit from the runime and use that on same pc which did not enable
here take a look

for any cpu the IntPtr TARGET_64BIT block is enable but for my case it is not
and here is the preprocessor or constant implementation
<Is64Bit Condition="'$(Platform)' == 'arm64' or '$(Platform)' == 'x64' or '$(Platform)' == 's390x' or '$(Platform)' == 'loongarch64' or '$(Platform)' == 'ppc64le' or '$(Platform)' == 'riscv64'">true</Is64Bit>
<DefineConstants Condition="'$(Is64Bit)' != 'true'">$(DefineConstants);TARGET_32BIT</DefineConstants>
<DefineConstants Condition="'$(Is64Bit)' == 'true'">$(DefineConstants);TARGET_64BIT</DefineConstants>
this implementation can be found here
https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
GitHub
runtime/src/libraries/System.Private.CoreLib/src/System.Private.Cor...
.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps. - dotnet/runtime
i just don't get it why its not working?
it’s not working because this strategy does not work when you are AnyCPU, as i said
but its working for the Intptr struct
see

it is not actually Any CPU
what are you taking about
its clearly saying Any CPU
the fact that the configuration says Any CPU does not mean that the DLL you get from building CoreLib is valid on any CPU
the CoreLib is compiled separately for all architectures but the DLL is marked as Any CPU for all of them
the way
#if
works is that one of those TARGET_XXX
values is enabled at compile time, and the C# compiler completely ignores all of the text inside of #if
for any of the ones that were not enabled
for #if
to mean anything. you must necessarily build different DLLs with different build options for each architecture. so you would not have one DLL which works on any CPU