
Meta Crush Saga: a C++17 compile-time game - ingve
https://jguegant.github.io//jguegant.github.io/blogs/tech/meta-crush-saga.html
======
ballenf
> the famous snake game we could play on our Nokia 3310

I typed in the snake code from Home Computer Magazine (v.4 no. 5). If anyone
cares to reminisce with me, see page 69 (or 123 for the actual code) at this
link:
[https://archive.org/details/HomeComputerMagazine_Vol4_05](https://archive.org/details/HomeComputerMagazine_Vol4_05)
That version was called "Slither", but there were no ".io" domain names just
yet.

But wikipedia says it goes back even further to 1976:

[https://en.wikipedia.org/wiki/Snake_(video_game_genre)](https://en.wikipedia.org/wiki/Snake_\(video_game_genre\))

Maybe the author knows Nokia didn't invent the game.

~~~
ariehkovler
Everyone who learnt to code in the 80s made a version of the snake game. I did
one in ZX Basic on my old Spectrum as a bored 9yo using ASCII number 8s as my
snake's body

~~~
ChrisSD
Snake and Wall (bouncing ball hitting bricks) were games I made for every
programmable system I owned up until around the mid 90's.

~~~
ariehkovler
I wasn't smart enough for Wall (we still called it Breakout in my day and
played it with paddle controllers). But I was so excited when I got my snake
to move with joystick control

------
rhacker
15 years from now we'll be reading a HN article on meta-compiler 64k demos
written exclusively using obfuscated code that outputs itself.

------
mattbierner
I apologize. Great work though

(For anyone interested, there was also compile time Tetris
[https://blog.mattbierner.com/stupid-template-tricks-super-
te...](https://blog.mattbierner.com/stupid-template-tricks-super-template-
tetris/) and although my mind never fully recovered from the descent into
template metaprogramming, I have been able to find some measure of peace in
the world of JS :)

------
atilaneves
I wonder if anyone would bother in a language that makes this easy, such as
Lisp or D.

------
rurban
Regarding the last items: Just use clang. Its constexpr support is much
better, and you can print user-defined compile-time warnings or errors with
diagnose_if. gcc only has _Static_assert which is not enough. The linux kernel
uses a few tricks, but all are worse than with clang.

------
floor_
If my compile times were greater that 3 seconds I would be livid.

------
Toine
Disclaimer : I'm working on a C++ project and as you will understand I do NOT
enjoy it.

Every new C++ article about the latest "improvements" to the language tends to
confirm my belief that C++ developers are masochists. They don't seem to be
interested in actually improving the language and making in better and easier
at creating useful software. They even seem to laugh at the word "easy". The
entire ecosystem feels like a infinite pile of new features fixing old ones,
bringing new flaws in the process. Sometimes I feel like this is the case for
every language, but C++ is the worst.

    
    
       template <class T>
       std::string serialize(const T& obj)
           if constexpr (has_serialize(obj)) { // Now we can place constexpr on the 'if' directly.
               return obj.serialize(); // This branch will be discarded and therefore not compiled if obj is an int.
           } else {
               return std::to_string(obj);branch
           }
       }
    

I don't want a language with magic. Magic is hell for software. Show me
simplicity and elegance. Show me boring but powerful. Show me how I don't have
to choose between value, reference, raw pointer, unique_ptr, shared_ptr, etc,
to pass a damn parameter to your function. Show me boring but powerful stuff.

~~~
andrepd
I disagree with almost everything you said in that post; what's more, you're
objectively wrong. Let's look at your claims.

>Every new C++ article about the latest "improvements" to the language tends
to confirm my belief that C++ developers are masochists. They don't seem to be
interested in actually improving the language and making in better and easier
at creating useful software. [...] The entire ecosystem feels like a infinite
pile of new features fixing old ones, bringing new flaws in the process.

Can you give a concrete example? C++ evolution can be divided in roughly two
eras: C++98, and Modern C++ (C++11 and above). This latter introduces a new
philosophy, an expansion of features that make it look like a completely new
language indeed. From ubiquitous type deduction, to lambdas, to compile-time
computation without TMP, the language has introduced powerful new
abstractions, always with the trademark "zero cost" that gives C++ its power.

>[Code snippet]

>I don't want a language with magic.

What about that snippet is magic that leaves you so appalled? You got a great
(new) feature: guaranteed compile-time conditionals with the same syntax as
regular if statements. Depending on whether the object has serialize or not,
the code will instantiate to each of the branches, guaranteeing elision of a
potentially expensive runtime check and branch.

>Show me simplicity and elegance. [...] Show me how I don't have to choose
between value, reference, raw pointer, unique_ptr, shared_ptr, etc, to pass a
damn parameter to your function.

Show me a language that simpler and more elegant than C++, where you don't
have to worry about choosing how to pass parameters, or manage memory, and
that still is as fast as C++. You simply can't, because there's no such thing.
C++ delivers ZERO-COST ABSTRACTIONS, that's its power, that's its raison
d'être. No other language I'm aware of that delivers this, apart from maybe
Rust or D.

\--

I realize it's hip to hate on C++, but please keep it rational.

~~~
vvanders
> Show me a language that simpler and more elegant than C++, where you don't
> have to worry about choosing how to pass parameters, or manage memory, and
> that still is as fast as C++.

As someone who built their career on memory constrained, fast code in C++ I'm
gonna have to say that Rust _is_ simpler and more elegant.

You've got your zero cost abstractions, heck I can decided static vs dynamic
dispatch at compile time. Lots of things are _more_ explicit leading to less
foot-guns that you see with implicit type conversion and the like. Borrow-
checker catches all those nasty dataraces and ownership problems up front.
I've yet to run across anything I could do in C++ that I can't do in Rust.

Listen, I love C++ but I think you may want to expand your horizons a bit
before declaring it the best language ever in this space.

~~~
kllrnohj
Rust does come with a different bag of complexity. I'm _thrilled_ to see
competition with C++ in this space finally and super excited about Rust, but
let's not pretend Rust is easy, either. It has a notoriously monsterous
learning curve (which is why there's a major focus to fix that curve:
[https://github.com/rust-lang/rust-roadmap/issues/17](https://github.com/rust-
lang/rust-roadmap/issues/17) )

And in this particular context Rust also still forces you to figure out how to
pass parameters and manage memory. It "just" helps you with when to call free,
which std::unique_ptr also helps you with. Rust makes it a compile error to
have a bug, which is awesome, but the "you have to figure out memory
allocation & layout" complexity is still mostly there.

~~~
vvanders
> It has a notoriously monsterous learning curve

That learning curve exists in C++ too, it's just hidden under the iceberg of
'interesting' runtime behavior. There's nothing like trying to printf a data
race only to have the print call insert a fence and make your data race
disappear.

> And in this particular context Rust also still forces you to figure out how
> to pass parameters and manage memory.

This is a lot simpler in Rust. Everything is value type by default, Box<T> for
heap allocations, which even then are managed mostly for you via RAII(yeah I
know there's std::unique_ptr but you still also have new, delete, new[],
delete[], etc). Parameter types are value, mut ref or const ref. Move
semantics are implicit if you have a value type. You don't have
const/volatile/etc function qualifiers.

implicit type conversion(esp constructors) can be a total bastard and trip you
up in nasty ways. Even more annoying if you're trying to bit-pack or do clever
math.

Rust got to learn from a lot of C++'s mistakes and is to me a better language
for it.

~~~
kllrnohj
> This is a lot simpler in Rust. Everything is value type by default, Box<T>
> for heap allocations, which even then are managed mostly for you via
> RAII(yeah I know there's std::unique_ptr but you still also have new,
> delete, new[], delete[], etc). Parameter types are value, mut ref or const
> ref. Move semantics are implicit if you have a value type. You don't have
> const/volatile/etc function qualifiers.

That's not simpler at all - it's basically the same. C++ is also value type by
default and you have to ask for heap allocations. And just like in C++ you
have types in Rust that look like stack allocations but aren't, like Vec.

So no, it's just value type otherwise Box<T>. That's not true in the
slightest. And you need to specify in your function if it's taking ownership
or not in addition to whether or not it's pass-by-value or pass-by-reference.

Other than new/delete it's the same shit, just a different syntax.

Rust got to break backwards compatibility which allowed it to remove a lot of
C/C++'s old mistakes, yes, but the raw complexity is still entirely there, and
very nearly identical.

~~~
vvanders
> That's not simpler at all - it's basically the same

Oh, you mean like how you have rvalues, lvalues, xvalues, glvalues and
prvalues[1]. In Rust you have value, ref and mut ref.

You also don't specify ownership of passed pointers. So you need to encode
that into your function documentation, as opposed to Rust where only value
types can change ownership.

> And just like in C++ you have types in Rust that look like stack allocations
> but aren't, like Vec.

Never said anything about that, we're just talking about parameter types here,
of course you can have value types take ownership of stack allocations(Box<T>,
std::unique_ptr and their variants).

> And you need to specify in your function if it's taking ownership or not in
> addition to whether or not it's pass-by-value or pass-by-reference.

Not in the case of Rust, value types are the only thing that can take
ownership(even Arc/Rc return value types when you clone()). Refs just allow
temporary access in a mutable/const way for the lifecycle of the function.

> Rust got to break backwards compatibility which allowed it to remove a lot
> of C/C++'s old mistakes

Thanks for proving my point. I've never seen a C++ codebase that didn't use
some of C++'s mistakes.

[1] [https://stackoverflow.com/questions/3601602/what-are-
rvalues...](https://stackoverflow.com/questions/3601602/what-are-rvalues-
lvalues-xvalues-glvalues-and-prvalues)

~~~
kllrnohj
> Oh, you mean like how you have rvalues, lvalues, xvalues, glvalues and
> prvalues[1]. In Rust you have value, ref and mut ref.

That is not a reasonable comparison and you know it. rvalues & lvalues are the
only things in C++ that you ever care about in the slightest unless you're
writing a compiler, and if you're writing a compiler then Rust has things like
xvalues, too (how do you think the Drop trait works?)

~~~
vvanders
Answer me a simple question, why does std::move exist then?

(here's a hint, Rust doesn't need xvlaues because every value already has an
associated lifetime)

------
MikkoFinell
Trigger warning: Rust programmers should be careful about clicking the link.
The article contains a controversial joke that might shock some readers.

~~~
z3phyr
I really like Rust; But I see an annoying cult forming around it, and they try
really hard to proselytize (Not to say that I was not a part of it in the
past, but I am woke now)

~~~
ChrisSD
It is necessary to "proselytize" a young language in order to gain any
traction with wider audiences. This is especially true when you're going up
against decades old, entrenched languages like C++.

If you don't want your language to die from indifference the only other option
is to find a poorly served niche and claim it as your own.

~~~
w8rbt
Just do something really technically awesome in Rust that becomes hugely
popular and the language will speak for itself. I've not seen that yet. But I
do hear a lot of fanaticism. That's normally a bad sign.

Docker is written in Go. It works great and is changing how corporations
compute. Where is Rust's Docker? It needs that sort of thing to stand on its
own.

C++ has nothing left to prove. Yes, it has warts, but those have been and are
being fixed (C++11, 14 and 17). Go is 75% there, as far as proving itself
technically, and is approaching C++ in the, "what more is there to prove"
department. Rust is more like D. Lots of hype and talk but only a few very
notable, wildly popular things have been built with it. It has a long way to
go.

~~~
pjmlp
It has some Firefox, GNOME and Visual Studio Code components, today. And some
parts of Fuchsia tomorrow.

Sure it still cannot match C++ in many areas, I have stated multiple times I
rather use C++ alongside Java/.NET workflows than trying to fit Rust in its
current state.

But it will get there, given that it already got the attention of Oracle,
Dropbox, Microsoft, Google and a few others.

Sadly I cannot say the same for D, the language is nice, the community has
great people, but trying to be a better C++ without a strong domain focus has
made it behind all alternatives, including Rust.

