BrainFlood: Runtime code generation via reflection in .NET
sbox.game
sbox.game
Despite the name, 'Unsafe.SizeOf<T>()` is the safe and preferred way to do this - it is guaranteed to be a compiler constant (in the past you'd say "JIT constant" but you can no longer assume that with the advent of NativeAOT).
It's a bit unfortunate because much like with MemoryMarshal, not all methods are equally unsafe. Some are benign or safe like this one, same are Unsafe.As or, worse, Unsafe.AsPointer.
Why even have something labelled "Unsafe" which is in fact safe ? Genuine question. What lead to this choice?
Not that I necessarily agree with the decision myself, but that's the argument made by others.
Rust spent lots of effort on making sure these lines are in the right place. The current work to try to "make C++ safer" is likely to ban taking a 32-bit unsigned integer and treating that as a 32-bit IEEE floating point value because it can invoke Undefined Behaviour in their language. Meanwhile in Rust that exact feature is not only safe, it's a pure function named `f32::from_bits` because 0x40000000 literally is 2.0, it's never any other value, there's no actual danger here, you just need to fix your programming language but C++ would rather waste your time on this non-existent problem.
This is not abuse, it's intended behavior, standard library itself likes to use it a lot internally. That's also how generics work in Rust!
This can be pushed further by passing data by ref's. You can also easily allocate structs in unmanaged heap (aka malloc and free) and then pass them by `ref` without using unsafe (save for construction and alloc/free). Just don't do it for structs which contain gcrefs. C# can lean very heavily into systems programming :)
There have also been a large amount of struct optimizations across the last few versions. So it is not a coincidence that RyuJIT/ILC can trade blows with LLVM (not always but still!).
Edit: Oh god, I saw what this refers to. I take it back. There is likely a better, idiomatic and compiler-friendly way haha.
https://www.cs.utexas.edu/~wcook/Drafts/2012/ecoop2012.pdf
If the OP author has any interest in doing a 1000x service to the C# community, the community needs a translator like you to convert the paper's compiler example into other domain specific examples. I'm pretty sure this paper went nowhere because it lacked a hello world example. something easier to digest and smaller and scope. The classic "dog is an animal and cat is an animal" level of explanation.
Another cool thing that I learned about via this post (as I was wondering about the .scene files in the GitHub repo) is this whole ecosystem:
> S&box is coded in C#. Under the hood, it uses the Source 2 engine (CS2, HL:Alyx, DOTA2) and some of its systems: rendering, resources, physics, and audio
And people say there is no more Good Stuff on the Internet...
[1]: https://blog.stephencleary.com/2022/10/modern-csharp-techniq...
[2]: https://github.com/dotnet/runtime/blob/main/src/libraries/Sy...
I'm going to dig into your implementation.
https://docs.unity3d.com/6000.0/Documentation/Manual/create-...
https://github.com/julesjacobs/ImmutableCollections/discussi...
It has similar "source generator" hallmarks like in OP's post, for example calling a generic constructor which is JITted to a deeply nested struct type.
It's (in my opinion) very hard to reason what the code is doing because it has that flavor of "writing code to generate source to generate code"
It's impressive. And i'm pretty sure it's only real hinderance, why it could not be adopted into the base library, is that the repo needs a phd holder to finish implementing the `IList` interface because deleting is really difficult and theoretical.
System.Linq.Expressions is a useful intermediate (that also works in sandboxes). People overlook its compilation power all the time, especially this long after the DLR (System.Dynamic and especially System.Dynamic's IDynamicMetaObject) has been forgotten, but it is a far easier API than System.Reflection.Emit.
I was half-hoping in this article to see a comparison with BF implemented in System.Linq.Expressions.
Edit: Looks like it's slower by a pretty good margin (~3.6 seconds). It's certainly possible I'm doing something stupid. This would be really nice to have for webassembly, where I'm convinced I need to implement some sort of virtual register allocation, and I'm still not entirely sure about control flow.