Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Meta Crush Saga: a C++17 compile-time game (jguegant.github.io)
172 points by ingve on May 21, 2018 | hide | past | favorite | 47 comments


> 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 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)

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


Snake is certainly older than the 3310, but I'd bet there's an entire generation of people who first saw it there. They're very closely associated- the rebooted 3310 made sure to include a Snake clone too.

https://www.theverge.com/2017/2/27/14749896/nokia-3310-snake...



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


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


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


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


I apologize. Great work though

(For anyone interested, there was also compile time Tetris https://blog.mattbierner.com/stupid-template-tricks-super-te... 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 :)


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


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.


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


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.


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.


> 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.


I believe the only thing that can be said with certainty about Rust when compared to C++ is that it's safer.

Simplicity in the sense of easy to understand is an adjective I would apply to languages such as golang, Swift or even Java. C++ and Rust are complicated languages, because they allow low-level control of the implementation and they insist on the programmer thinking deeply about resource management.

Elegance is likewise a property I would not associate with Rust: resource management is clumsy and leaks into the syntax everywhere, it ends up defining how the language looks like.


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 )

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.


> 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.


> 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.


> 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...


> 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?)


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)


Even if Rust is better than C++, that doesn’t mean C++ is bad.


Yes, actually, it does. Because C++'s mantra has always been zero cost abstractions. And people have defended the language because "you can't do better and still have the same performance". Well, it seems you can.


Languages are never compared across a single dimension (except to prove a point on the internet).


This is the paradox: Every new language feature in C++ has, as you say, a rational argument for inclusion. Yet when you combine them, you get a language that I will never use if I have a choice.


> No other language I'm aware of that delivers this, apart from maybe Rust or D.

Reminds me of a great campaign speech from Donald Trump where he praises the border wall, saying there will be no way anyone could get down once they managed to climb it. Except maybe a rope. Good job proving your point by shooting yourself in the foot.


C++ has very little "magic" once you get an okay mental-model for how the compiler looks at things. And in fact you can write okay but sub-optimal/"unsafe" C++ that looks kinda like C without that much headache and without using things like unique_ptr (or even really refs to a certain extent).

However C++ does let you get as low-level as you want and micro-optimize things and write extremely precise types that other languages can really only dream about.

This said: the language is quite complicated which leads to it being "hard" to compile (and hard to learn if you care about some of the more interesting aspects).

> C++ developers are masochists

In my experience you have to have a very different mindset when writing production-quality C++. It's a lot "slower" to write (at least for me), but in the end you can end up writing some extremely expressive and efficient code that scripting languages can't even express very well (let alone run quickly).

(The C++ tooling around build systems and compilers and linkers / arcane black-magic is a real tragedy. I'm holding out for a sane C++ build-and-module system but the language-designers don't seem interested in standardizing these things.)

> Show me simplicity and elegance. Show me boring but powerful.

Many languages value these as design-goals and you may want to use those languages instead. C++ makes a different set of tradeoffs. It has its own sense of elegance.


This is a very romantic view of C++, but not (in my opinion) an accurate one. C++ feels like a language developed without a road map. I agree with the original comment. Many C++ updates are full of band aids to address issues so blatant that they had to fixed by others a long time ago (see boost).

> C++ has very little "magic" once you get an okay mental-model for how the compiler looks at things.

This is a meaningless statement. I could make the worlds most obtuse and complicated language and you could still say this. Just because after years, and I do mean years, of experience C++ stops regularly blindsiding you does not mean it is a well designed language.

There are parts of C++ that are good, beautiful even, but that does not give it a free pass from being among the messiest languages in widespread use.


I don't give C++ a free-pass for its warts, I just think it's important to realize where those warts actually live so that I can direct frustration appropriately :)

> C++ feels like a language developed without a road map.

Many of the warts are there due to the committee's very strong stance on requiring backward-compatibility. This does detract from the language sure, but the tradeoff is code that will continue to compile for many years which is important for huge codebases that want to continue to adopt the latest features.

Other languages tend to solve this with breaking-changes (python3, perl6, even java8 or ES* to some extent which arguably changed the whole paradigms of their languages).

Ultimately the roadmap is dictated by time-tested experience with existing features and what the community deems the most necessary and practical (see boost which is in many ways an incubator for language ideas).

Recent releases are explicitly around new roadmap items like better RAII-pointers and (finally) a standard threading API.

There are also new language-features that make the language easier (lambdas, more auto, etc) and faster (rvals/move operations). These are "optional" and can be ignored if you prefer.

> Just because ... C++ stops regularly blindsiding you does not mean it is a well designed language.

I'm still blind-sided when the compiler does crazy things I hadn't expected, but almost always I end up cursing the tooling not the language.

IMHO this is the fault of (1) the documentation being terse and separated from stdlib headers etc, (2) compilers/linkers being rather nasty and slow to work with, and (3) terrible error messages in general.

These are problems with the ecosystem and not so much with the language-proper. It's fair to say they sour the whole experience (and they really really do sour it), but the language itself is actually not that surprising once you get past the tooling.


> C++ developers .. don't seem to be interested in actually improving the language

In his talks at the 2014 and 2017 conferences for the D programming langue, Scott Meyers noted the difference between what individual C++ committee members say they value, and what the process actually produces. Will a new standard every three years make this better or worse? They do seem open to deprecating things that don't work.

You might enjoy the 2014 talk, called "The Last Thing D Needs." I don't want to spoil the punchline, which actually struck me as kind of bittersweet, but Scott talked about the distinction that Fred Brooks made in "No Silver Bullet" between essential and accidental complexity. C++ does have a lot of the latter.

Edit: link for the 2014 talk:

https://www.youtube.com/watch?v=KAWA1DuvCnQ

and for the 2017 talk:

https://www.youtube.com/watch?v=RT46MpK39rQ


> 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.

This really sounds like your problem isn't with C++, it's with, well, any language that's "low-level" enough to distinguish between values and references.

I grudgingly used C++ for years because at least it was better than C, but since C++11 it's actually been a great experience. Honestly my only complaint about modern C++ development is that the compile times are painfully slow. There are quirks, but overall it's now a wonderfully usable language.

Though obviously you shouldn't bother with C++ if you're working on stuff that could just as easily be written in Python or whatever.


I am working on a C++ project and I enjoy it.

The bread and butter of C++ is balancing relatively good performance with reasonably acceptable overhead and developer time.

I was personally tired of re-writing over and over.

    template <typename Type>
    struct PerformOperation {
      static void
      Op(int&) {} // noop
    };

    template <>
    struct PerformOperation<float> {
      static void
      Op(int& k) {
        // do something
      }
    };

And all of the specific things you're complaining about... well they're in your control. Do you or your company hold a standard for interfaces or memory tenancy? If not, maybe you need one.


I am a C++ dev and I kind of enjoy it. There is a lot of hate towards C++ and a good part of it is justified. Yet what vindicates the language in my opinion is that some of the most complex and interesting software in the world is written in C++ (browsers, search engines, machine learning engines, database management systems, game engines, you name it). So if you want to work on those you'd better know the difference between a reference and a shared_ptr.

As for the masochism... Well, after some time you stop noticing the warts and just work around them without expending much mental energy. Not a big deal.


I usually work in Java and .NET.

Occasionally I drop down to C++, either on private projects, or because I need to integrate some library on the former eco-systems.

The only thing I don't enjoy in C++ is the copy-paste compatibility that makes some devs use it as "C with C++" compiler, not even "C with Classes".

For devs that dislike magic there is always Go.


Agreed completely, speaking as another one whose worked on C++ projects and tried very hard to stop others on the team from cramming as many new features as they could into code they wrote just for the sake of using them. It's a similar attitude to "architecture astronautism", some people just seem to love creating complexity while others (like me) enjoy the complete opposite. IMHO C++ is good when used as a thin layer of syntactic sugar over C, the "C with classes" that so many C++ fans speak derogatorily of.

Then again, my language of choice for general use is C89.


C89 with -pedantic ,-Wall, and -O2 flags, I hope! O2, of course, not for the speed but or the small number of additional warnings it might generate.


There is no real reason not to be using C99 (or C11) unless you work with targets that only have an "ANSI" compiler. The additions that come with each new standard are either sorely needed (stdint.h, stdatomic.h, etc.) or conservative to a fault (_Generic) so you can't even complain about bloat.

Down with "ANSI" C fetishism.


Bad programmers don't like C++ because it often makes them feel stupid. Good programmers like C++ because it sometimes makes them feel stupid.


constexpr in this case seems like a nice and less magic replacement for SFINAE, which always seemed like a bit of a hack


[flagged]


Which is a platitude that doesn't mean anything


Stuff will never get fixed if you don't.


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


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)


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.


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.


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.


The primary difference between Rust & D is that Rust is directly challenging C++'s bread & butter, which nobody else is really doing.

Go's combination of M:N threading & GC make it more of a challenger to Node.js than C++, they are really in fairly different universes. D also bets on the GC, which also makes it unsuitable for a chunk of C++'s users and the interfacing with existing C/C++ code gets messy with the GC lurking in the shadows (it is somewhat terrifying that GC.addRoot() not only exists but that you're supposed to use it)

Rust's excitement is a result of directly challenging C++ in the non-GC language space along with things like attempting to make multi-threaded race conditions a compiler error (which is technically awesome and something nobody else is doing)




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

Search: