Hacker News new | past | comments | ask | show | jobs | submit login
Minimum Viable Declarative GUI in C++ (ossia.io)
121 points by jcelerier on March 22, 2022 | hide | past | favorite | 41 comments



In my experience, declaring the UI is not the hard part. Iterating 100s of times with minor tweaks to get the final appearance right is what usually kills me. Using something like this would requires a continuous edit,compile,run cycle that would start hurting once compile times reach >5 seconds. For all its faults, I really appreciate that web development comes with a console to inspect data and way to edit the widgets in real time and get immediate feedback.


Like the original NeXT AppKit, where the UI was built from objects in memory, then stored on disk as archived objects. The objects could be manipulated with InterfaceBuilder and live tested. These concepts seem to have been lost along the way, to the point where they are not a part of modern NeXT->Mac->iOS development.

But also they got harder to implement as UI's got more complex.


> stored on disk as archived objects

This at least is still the case for iOS.

> The objects could be manipulated with InterfaceBuilder and live tested.

Is this anything like the new live previews with Canvas? I've only ever done iOS development but if this is the case that is amazing.


Fast iteration times and iterative feedback are exactly the topic of my other project Lafajol: https://youtu.be/fMQvsqTDm3k ; adding support for the UI thing is in-progress but so far I have compilation times around the 200ms mark for the kind of UI I target


2 quick questions:

Q1: is it mostly iterating to get precise visuals to get specific behaviours?

Q2: is the toolchain/compiler/webpack turnaround speed a major factor?


That’s a great requirement for any C++ GUI library. It must support a runtime GUI model. Ideally it would seamlessly support a compile-time model as well but I’m not sure there is a strong argument for the increased complexity in that case.


I always thought that "array of ints" was a greatly unappreciated concept for declarative ... anything. They can be easily hot reloaded, they compile fast, and they're not terrible. OTOH, you'd just be reinventing the Lisp-wheel but in ints.


To a GUI outsider, this blog post is confusing because it is missing necessary introductory information at the beginning and also contradicting itself.

If I put any of the example code into a file and run g++ test.cpp -o test -std=c++2a, I get the very expected error, undefined reference to 'main'. Adding main isn't going to magically make a struct turn into a UI. There are no #include statements. It's clear a library is needed as well as some familiarization.

Midway through, a link to Nuklear is provided. At the very bottom, the reader learns this can be tested with the avendish library... a bit after the sentence stating, "Glory to the post-library era."

I still don't know whether the code uses Nuklear or avendish or both, and because there will be more than a few steps to install and this solves a problem I don't have, I lost interest in the examples and will continue to ncurses my way through GUI MVPs.


hmm, I thought that my sentence in the introduction would have made the expectations as clear as possible, how would you improve it ?

> By user code, I mean that all the “nice” UI work can be done in, say, foo.hpp: <...> without requiring any custom header. Some yet-unknown .cpp file will include foo.hpp, and, no matter what the content of my_ui is, will execute a nice user interface from it. Anyone will be able to create independent libraries which will render UIs according to whatever platform-specific intricacies there are, but the actual UI code will be entirely independent from anything: peak separation of concerns is achieved.

Regarding:

> the reader learns this can be tested with the avendish library... a bit after the sentence stating, "Glory to the post-library era."

I say that because the UI code itself does not depend on any library: any organization can fairly easily make their own library which will parse these classes however they want. For instance, the code that wraps some of these things to python is in itself ~130 lines: https://github.com/celtera/avendish/blob/main/include/avnd/b... + helpers types for introspection ; the day C++ finally gets reflection most of these helper types can disappear and your actual business code won't have to change at all, unlike if you had written it against $TODAY's gui toolkit and wanted to make it work in $TOMORROW's


I just didn't realize there is no fully defined demo and to test the code sample requires some steps that I would need to discover myself. The article was clear, and I figured the post-library blurb referred to "free of dependencies" (akin to some of the single header unit test frameworks). After seeing the avendish compilation steps (last time I needed Boost was a real hassle) I decided I really wasn't motivated enough to try the demos myself. If I had a need for UI writing right now, I would probably take the time to clone and compile.


So you're building an interface that libraries can be written to?


Not really an interface in the OOP term, just trying to define the concepts, which are much more open-ended.


I find it telling you need to distinguish - I do think interface has come to mean closer to "contract".


So you have to write a library that can parse struct definitions?

Like a DSL but without the “___domain specific” part?

I mean, umm… dunno?


Step two: code the rest of the fucking owl.


you don't have to: the code compiles and is valid C++ as-is without issues, unlike when you are using a normal library, say Qt, JUCE or similar.

Each user can then choose what they want to do with it. For instance, the same code can be put on some embedded chip without any UI processing and will be able to be plugged to hardware pretty easily by writing a few lines of simple glue code.


Ah, I think I understand now — basically what you’re describing is a schema for defining UI code as a tree of c++ structs.

…which, honestly, horrifies me.

I remember for a time I was adding drag and drop to various parts of blender (Ton went on his yearly vacation and this is what he brought back) and every little change required a rebuild to test which wasn’t the quickest on my little netbook. Mind you there was zero documentation and I was pretty deep in the innerds so there was a lot of trial and error involved. I literally spent most of the time waiting on the compiler because, well, I’m not the best coder out there.

Working on the UI code was a dream in comparison — edit code, reload python module, see changes, rinse and repeat.


> Ah, I think I understand now — basically what you’re describing is a schema for defining UI code as a tree of c++ structs.

yes :-)

> I literally spent most of the time waiting on the compiler

well, it being a tree of plain structs without 200kloc of header files to parse also means that it's possible to implement very fast live-recompilation, like this: https://youtu.be/fMQvsqTDm3k?t=87


Always fun to see people rediscover Greenspun's 10th rule...

Hmm specifying my UI in code is tedious, and porting it to a different framework is impossible! I wonder if I could encode this somehow...

Eureka! I can just specify my code as a data structure! Now it's implementation agnostic!

How can I actually use it though?

I know! All I have to do is write some functions which recursively descend through the UI data structure and make the corresponding calls to the UI framework!

[Writes an interpreter(s) for their data structure]

Congratulations, now you understand the value prop of lisp, and have a poor supplement in C++.


oh, but I'd definitely use lisp ! If it had static types, easy simd support, complete Qt integration, value types and mandatory unboxed primitives, if it was possible to define non-trivial compile-time assertions and type checks (though maybe some LISP dialects can ? are there statically-typed lisps with the ability for arbitrary type computations ?), if normal programs with normal libraries in the LISP ecosystems could be entirely written without a GC, etc etc... and I'm sure I'm forgetting the other half of the absolute bare minimum of my requirements for the programming languages I use for my day-to-day tasks.

Saying that parsing an AST is on its own a justification for using LISP, is very much like saying that one should use smalltalk or erlang instead of $LANGUAGE every time they implement a message-passing system: at best worth of a shrug. There's a reason why all the projects listed here: https://github.com/celtera/avendish#future-directions are C++ :)


Absolutely! The same way that Lisp is a poor substitute for C and C++. All languages/systems/libraries/frameworks have things they are good at and things they are bad at. Which is why we don’t all use one language/library/framework/… for everything.


Recently I came across another minimal layout engine with Python and I thought it was kind of nifty as well:

https://forums.4fips.com/viewtopic.php?f=3&t=6896

I ported the code to Lua for a game I'm working on. Could still use some improvements and some live update functionality (should be easy enough to add), but useful for my purposes.

https://github.com/wolf81/composer


I'm building a Flutter rendering engine that can be powered by the back-end. Although C++ is not on the immediate list of supported SDKs, it could be added. https://nexusdev.tools/


Folks at my work place would love the fact that it uses boost!


That must mean that you don't work in the same workplace as Linus Torvalds.


Everytime someone says "boost", everybody screams!


Could you also hook a TUI into this to render it on the console?


Trivially


How about native controls?


I guess so ?


Wouldn't this kill compile times?


GCC on GH actions takes 2/3 minutes to build a couple hundred targets with this system (~30 test file times 6-8 backends) to give a reference. I think that's sufficiently short to make compile-times a non-issue when iterating ( https://github.com/celtera/avendish/runs/5652207236?check_su... )


Depends, with something like Live++ or VC++ hot reload, it would be ok.


the mobile version looks too narrow on my android chrome, but PC version looks good.



And yet it uses the very C-like anonymous struct declaration?


it's actually not anonymous structs but unnamed structs, which are a different thing in the standard (and present in both C and C++).

Anonymous:

    struct Foo {
      struct { int x, y; };
    };

    Foo foo; do_stuff_with(foo.x);
Unnamed:

    struct Foo {
      struct { int x, y; } child;
    };

    Foo foo; do_stuff_with(foo.child.x);


Those two words mean the same yet are different terms. Thanks for a correction.


my approach to technical terms is that life is much simpler if you entirely forget their real-world usage and just map them strictly to how they are defined in the technical context... they could be called zroglub and blox that I couldn't care less, and honestly sometimes I feel that it would be much less confusing to do things that way rather than trying to tie things to some common dictionary word


careful—that's how you get hoon/nock


What about it ? I'm certainly missing a reference because this evokes nothing to me




Consider applying for YC's Summer 2025 batch! Applications are open till May 13

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: