Awhile back, I wrote some C# code that interacts with C/C++ code by reading struct layouts directly. This is unsafe because C structs don't have guaranteed layouts unless you use nonstandard pragmas. So I had the C code bypass regular struct {} and lay out data manually by pointer arithmetic, making it consistent and safe. This worked great, until I discovered that booleans are a different size in C and C++. I fixed this too, but only by doing horrible things I'm only 95% certain actually worked
@slyecho That's not the direction I want the data to go in
@mcc coders, is this what coding is like?
@mcc isn’t this what protobuf and frens are for..?
@beatrix *thinks*
Actually… I think protobuf would be a good fit for our situation. I'm not sure why we didn't go with protobuf. I think maybe it's because I thought our needs were simple and protobuf was complicated, but our needs have grown more complex since this project was started.
I just asked the "client", who says that they've actually surveyed C# implementations of Protobuf and as of last survey they all seem to be very hard to use compared to how easy serialization normally is in C#.
@mcc “C structs don’t have guaranteed layouts”
So how does repr(C) work, then? Does it only work with a specific set of compilers? Is it based on platform conventions?
@bytex64 I may be about to say something horribly wrong, but the thing about repr(C) is that it guarantees you are using the same layout as the platform's standard C layout, but DOES NOT convey any information about what this platform is. This means repr(C) can talk to repr(C) but neither one can totally reliably talk to C# which is using pure pointer manipulation.
P/Invoke *can* talk to repr(C)… unless you fall into the hole with the disaster around boolean sizes.
@dotstdy @bytex64 Okay so on the one hand you are entirely right, but on the other hand: Microsoft, creator of this problem, is both the platform defining body who decides the ABI the C compilers are downstream from, AND the author of the currently most common compiler on the platform, AND the body who both defined and implemented the entire C# language
I'm thinking about this because I'm having to add to that code right now and it's miserable. I'm not sure why I'm telling you this. I guess I want to stress there are reasons I'm now on team "Nothing but Rust. Nothing but Rust forever. No more C/C++. Rust. What? Okay, Go or Zig are probably fine. Just no C"
@mcc one day someone will invent a C compiler that lets you write C and native Rust together and they will call it Crust
And then... no one will be laughing anymore
@brandonscript this would make me sad
@mcc to be fair if you want layout to be predictable in rust you also need to use C rules...
@mcc The problem is your code won't work on a PDP-10 or a DEC Alpha.
@TomF I DO NOT CARE ABOUT THIS! However I am somewhat concerned that my Rust code cannot practically speaking be run on various Motorola 680X0 systems I care about
@mcc Interesting! Why is that? I know 68k, but clearly there's a Fun Fact about Rust I'm missing.
@TomF 1. Rust is… not very good… at handling 16-bit pointers. I'm not saying it can't do it, just that Rust is mostly made up of mechanisms to make sure The Assumptions Are Never Broken and "pointers are at least 32 bits" is an assumption that is not checked.
2. Rust is *heavily* dependent on LLVM. There are non-LLVM backends but they're experimental at best. LLVM does not see why it should put effort into supporting build products for Macintoshes older than the year 2000.
@mcc Wait but all 68ks have 32-bit pointers. They have 16-bit integers by default, though 32bit ints are natively supported, it's just a perf cost on earlier than 68020.
The compiler I use for 68k dev is GCC 12 - not sure if that's LLVM-based or not?
@TomF Oh.
Sincere question, then. What does it mean for a Mac OS 7 application to be "32-bit clean" or not "32-bit clean"?
@mcc @TomF The original 68000 and the 68010, despite having a 32-bit internal address bus, only has a 24-bit *external* address bus. The full 32-bit bus was only exposed externally with the 68020 onwards.
IIRC the whole "32-bit clean" thing is based on if programmers were trying to be sneaky and use the upper 8 bits of the address registers for stuff that isn't an address on the older CPUs. Free Real Estate and all that.
And occasionally we see people who haven’t learned from past mistakes and suggest that since AMD64 only cares about the lower 48 bits of pointers the upper 16 bits can be used for other purposes.
@kasperd @TomF @techokami That's me! I'm suggesting it. I'm totally going to write a garbage collector that depends on this assumption
@kasperd @TomF @techokami Someday it will probably break!
@mcc You could also write a small C program that writes the C# struct definition by using the offsetof of the different fields. Although, I'd just add static_asserts in the C side to check that the the offsets are what you have used in the other one. You may have to keep a couple variations for different platforms, or try to order the fields so that there are no paddings.