boring c++ porn
After this discussion with my arch-nemesis form functional.cafe, where I made some bold claims about what's possible in C, I went down the rabbit hole of tail/sibling calls.
Found a few discussions in compiler mailing lists, where the consensus seems to be that they are happy to add something explicit in the backend but not is C frontend, since it makes little sense there, so I decided to see how far can I push my idea and if it can be viable.
TLDR of my idea, if you want to jump into a procedure/function without storing the return address in the stack, just call it and if there is nothing to return to, nobody will be returning. The challenge is to make sure of this of course, without explicit syntax.
In C it isn't too difficult, as long as you stick to other well know functional programming good practices, (minimal mutability, maximal purity), you should be good to go. However it's otherwise a poor choice since it lacks necessary abstraction power to make this kind of technique viable. For example, to do anything remotely polymorphic, you would usually turn to type erasure and manual rtti, which will inevitably defeat the compiler.
C++ on the other hand seems more promising, there is a lot more you can do there at compile time, however ensuring that there is "nothing to return to" and the optimization will stick is harder. It's likely to be very unstable and finicky way to write code, but I find myself in pursuit of it anyway for some reason.
First thing I tried is to beef up my previous example with everyone's favourite these days - sum types (std::variant). Absolutely destroyed the optimization, calling into some random address, that wasn't even in code/text segment, labelled "...vtable...". The culprit was std::visit, which turns out does some form of type matching on steroids, to match/visit multiple variants at once, passing them to multi-parameter(what's the word I'm looking for?) function, I guess to be able to advertise it as a full-blown replacement for virtual member functions. Obviously the compiler would have a hard time seeing through all of that across translation units, so I had to write my own visit_first that would just match/visit one and forward the rest. That's the ugliest part of it all, but hey! it can go in the library (you are not a C++ programmer if you don't have a library :v).
Here is what I got so far:
https://git.sr.ht/~namark/funky/tree/5b10ed447cf6552710effbe1ed5fba17100239e6
I'm now tempted to write something a bit more realistic utilizing this technique, maybe a small game, so to be continued perhaps...
QT: https://qoto.org/@namark/105327075320230599
re: true co-routines in C
how wonderful, another use case wayland broke
https://github.com/debauchee/barrier/issues/109
I see, they got offended that I considered libstdc++ standard library mailing list before them
https://codereview.stackexchange.com/posts/252686/revisions
probably the main reason they closed it
guess I got what I deserved for this sin -_-
https://codereview.stackexchange.com/questions/252686/whats-wrong-with-my-midpoint
unsigned short promotes to SIGNED int?! why... why did you do this to us C ?!
want!
Should 0 total duration be a valid state for a timer?
It's a sentinel value that might take care of some edge cases, but then have to guard against division by 0 when calculating progress ratio (elapsed/total). It's the only sensible default initialization value, but it also doesn't really makes sense conceptually - it's done/triggered before it's even started.
I wrote
container.push_back(item);
auto& new_item = container.front();
and suffered grave consequences.
From now on i will only write
auto& new_item = container.emplace_back(item);
Another even crazier "compile my unit test" challenge. This time must run as well.
std::visit should have been called transform and should have worked with tuples
std::reduce as it is now should have just been an std::accumulate overload (barring the default init, which is evil, and should be instead inferred from the operator when possible, through some standard interface)
a function called reduce should support partial reductions