C
C#2d ago
OV

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:
typedef struct windowManager_t {
char *name;
void *window;
...
#ifdef Is64BitEnviroment
unsigned long last_request_read_upper32bit;
unsigned long request_upper;
#endif
void *exithandler;
struct ss_font* global_fonts;
}windowManager_t;
windowManager_t *ss_get_current_window_manager();
typedef struct windowManager_t {
char *name;
void *window;
...
#ifdef Is64BitEnviroment
unsigned long last_request_read_upper32bit;
unsigned long request_upper;
#endif
void *exithandler;
struct ss_font* global_fonts;
}windowManager_t;
windowManager_t *ss_get_current_window_manager();
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
Anton
Anton2d ago
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...
Anton
Anton2d ago
it's the same in dotnet
Anton
Anton2d ago
No description
Anton
Anton2d ago
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
OV
OVOP2d ago
@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
Anton
Anton2d ago
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.)
wasabi
wasabi2d ago
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.
OV
OVOP2d ago
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
wasabi
wasabi2d ago
You can use IntPtr.
OV
OVOP2d ago
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
wasabi
wasabi2d ago
But pointer size is a small part of differences that might exist between architectures.
OV
OVOP2d ago
i need to access those method and those valudes
wasabi
wasabi2d ago
If you don't want the end user to deal with it, hide it all behind an API the end user interacts with.
OV
OVOP2d ago
how? the struct size suppose to determine at runtime. how can i do that
wasabi
wasabi2d ago
Don't expose those structs to the end user.
OV
OVOP2d ago
its hard. i mean windowManager_t struct is kind of like a whole game that need to pass arround
wasabi
wasabi2d ago
Pass around a reference to it. Like every other native library wrapper that exists in .NET.
OV
OVOP2d ago
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
wasabi
wasabi2d ago
Sometimes you have to mke different structs for calling User32. And .NET itself especially does. Such things are especially true between implementations of libc.
OV
OVOP2d ago
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.
wasabi
wasabi2d ago
Well why did you write it different for each platform? Don't. Sounds like you built a pretty non-portable C library.
OV
OVOP2d ago
i mean those method contain logic to deter mine the platform and run the code
wasabi
wasabi2d ago
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.
OV
OVOP2d ago
which was easy to port but the problem is the architecture
wasabi
wasabi2d ago
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
OV
OVOP2d ago
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
wasabi
wasabi2d ago
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.
OV
OVOP2d ago
its not solving the issue it just kicking the can
wasabi
wasabi2d ago
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.
OV
OVOP2d ago
is it really impossible to hide it in c# end?
wasabi
wasabi2d ago
It's impossible to hide it in any language.
OV
OVOP2d ago
but dotnet intptr does able to hide it.
wasabi
wasabi2d ago
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.
OV
OVOP2d ago
i mean x11 does that
wasabi
wasabi2d ago
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).
OV
OVOP2d ago
i pick that form x11
wasabi
wasabi2d ago
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.
OV
OVOP2d ago
@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
wasabi
wasabi2d ago
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.
OV
OVOP2d ago
yes this one
No description
reflectronic
reflectronic2d ago
yes. that assembly is not "Any CPU," so what they do will not work for you
OV
OVOP19h ago
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
OV
OVOP19h ago
No description
OV
OVOP19h ago
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>
OV
OVOP19h ago
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
OV
OVOP19h ago
i just don't get it why its not working?
reflectronic
reflectronic19h ago
it’s not working because this strategy does not work when you are AnyCPU, as i said
OV
OVOP19h ago
but its working for the Intptr struct see
OV
OVOP19h ago
No description
reflectronic
reflectronic19h ago
it is not actually Any CPU
OV
OVOP19h ago
what are you taking about its clearly saying Any CPU
reflectronic
reflectronic19h ago
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

Did you find this page helpful?