3 ms·
I saw a talk at a C++ conference where the presenter floated the idea of writing a compile-time C++ GUI library for use in industrial equipment UIs. Why parse
by MathMonkeyMan 2y ago
I saw a talk at a C++ conference where the presenter floated the idea of writing a compile-time C++ GUI library for use in industrial equipment UIs.
Why parse markup and generate objects on the fly when there is exactly one UI that will ever be burned into the CNC machine's firmware? Why even bother with an object library that instantiates components at runtime when you know upfront exactly which components will be instantiated and all of the interactions that can occur among them?
At the time, I filed the idea under "C++ wizard has too much fun with metaprogramming," but he was probably on to something.
Another way to think about the idea is "let's invent a new programming language that allows us to express a single UI, and the output of the compiler will be a native program that IS an optimized implementation of that UI."
- XorNot 2y agoI'm trying to figure out why this wouldn't be much more widely applicable? I.e. all mobile apps are basically a finite series of screens with limited actions as well.
- kevmo314 2y agoI think it's somewhat hard to get the API right. This is what React-based static-site generators are but people love defeating them by introducing runtime dependencies and configuration.
- bsder 2y agoYou can't continuously release a mobile app thanks to app stores. So, a lot of runtime stuff is papering over the fact that your submission to Apple takes too long to resolve.
- jiggawatts 2y agoI've been thinking about this kind of approach a lot ever since I learned Rust. A small aside about a personal theory about language design: Every new major language feature like templates or whatever gets "tacked on" without really redoing the fundamentals of how the language works. As in: you could remove it and things would still be fine. For example, you can use C without the preprocessor, it's just a bit clunky. Then, later, sometimes much later a language comes along that really leans into the feature to the point that it can no longer be removed. It becomes fundamental. The ultimate metaprogramming capability would be to have the compiler phases exposed to the programmer. That is, the compiler would no longer be a binary black box into which text is fed and binary pops out. Instead, the compiler and its phases would be "just" the standard library. Rust started down this path but the designers seemed to shy away from fully committing. Zig is closer still to this idealised vision, but still isn't 100% there. Ideally, one should be able to control every part of code generation with code, including C# style "source generators", Zig-style comptime, custom optimisation passes or extensions, custom code-gen, etc... In a system like this, a single GUI framework could be used to either statically or dynamically generate UI elements, with templating code being run either at comptime or runtime depending on attributes similar to passing a value by copy or by reference. Look at it this way: We're perfectly happy writing code to generate code. We do it all the time! As long as it is HTML or JavaScript and sent over the wire...
- SkiFire13 2y ago> Ideally, one should be able to control every part of code generation The issue with this approach is that the more you can control, the less the compiler can assume. This in turn means that it can check less for you, and tools become harder to write because code analysis often heavily realies on those assumptions. Just to make an example, Zig doesn't (and with the current approach can't) have declaration checked generics. > In a system like this, a single GUI framework could be used to either statically or dynamically generate UI elements, with templating code being run either at comptime or runtime I feel like this is overly optimistic. Some things will always be runtime-only, even some very basic ones like allocating heap memory. You can likely sidestep this issue and still precompute a lot at compile time, but then chances are this way of computing will be less efficient at runtime. In the end you'll likely still end up with different code for comptime and runtime just because of specific optimizations.
- jiggawatts 2y ago> The issue with this approach is that the more you can control, the less the compiler can assume. That's absolutely true, but there's a workaround, albeit a complicated one. The compiler internals need the same kind of constraints or traits that abstract code such as language interfaces or template parameters can have. These can then be used to constrain the internals in a way that then would allow assumptions to be safely "plumbed through" the various layers. The (big!) challenge here is that these abstractions haven't been well-developed in the industry. Certainly nowhere near as well as the typical runtime "type theory" as seen in modern languages. > some very basic ones like allocating heap memory. Well... this is sort-of my point! For example, what's the fundamental difference between allocating memory in some heap[1] structure at runtime and a compiler allocating members in a struct/record/class for optimal packing? IMHO, not much. E.g.: Watch this talk by Andrei Alexandrescu titled "std::allocator Is to Allocation what std::vector Is to Vexation": https://www.youtube.com/watch?v=LIb3L4vKZ7U https://www.youtube.com/watch?v=LIb3L4vKZ7U It really opened my eyes to how one could very elegantly make a very complex and high-performance heap allocator from trivial parts composed at compile-time. There's no reason that a nearly identical abstraction couldn't also be used to efficiently "bin pack" variables into a struct. E.g.: accounting for alignment, collecting like-sized items into contiguous sections, extra padding for "lock" objects to prevent cache issues, etc... This is what my dream is: that a struct might just be treated as a sort-of comptime heap without deletions. Or even with deletions, allowing fun stuff like type algebra that supports division. I.e.: The SELECT or PROJECT-AWAY operators! There was some experimental work done in the Jai language to support this kind of thing, allowing layouts such as structure-of-arrays or arrays-of-structures to be defined in code but natively implemented by the compiler as-if it was a built-in capability. [1] Not really a traditional heap in most language runtimes these days. Typically a combination of various different allocators specialised for small, medium, and large allocations. PS: The biggest issue I'm aware of with ideas like mine is that tab-complete and IDE assistance becomes very difficult to implement. On the other hand, keeping the full compiler running and directly implementing the LSP can help mitigate this... to a degree. Unsolved problems definitely remain!
- DanielHB 2y agoSounds a lot like static website generation that a lot of JS frameworks do. There are a lot of pitfalls with this approach, but for a subset of problems it is very good.