1. 26
  1.  

  2. 10

    Reading this, in my mind I was defending C++ because there are reasons behind each of the things the post criticizes. For instance std:: because bringing everything in std into the global namespace can result in interesting errors. The nightmares of C++ initialization brought on by decades of backwards compatibility while attempting to reach a uniform way. Why emplace_back is useful compared to push_back.

    But then I went back and looked over the original post and code, and pondered things like how e.path().c_str() invokes e.path().native() which returns a wstring on Windows, meaning the code does not compile. How the for loop that iterates over the word to convert it to lowercase is undefined behavior if any character is non-ascii (when auto results in c having type char). Or that emplace_back does the same as push_back in this case because there is no constructor to forward arguments for in-place construction to.

    I am not trying to pick at Jussi’s code here, you can take example code from most blog posts and nitpick at the details which are largely irrelevant to the message. Rather, it made me more aware of the amount of details you have to consider when writing C++ code.

    In my opinion, starting with C++14 it is slowly becoming easier and easier to write good C++ code, but that does not take away all the complexity left by 30 years of backwards compatibility on heterogeneous hardware. It is easy to criticize this complexity if you have the luxury of only having to support the three major platforms of today and can assume things like little-endian, two’s complement, and UTF-8.

    1. 22

      tl;dr: I, personally, like Golang more than C++. Ergo, C++ is bad.

      Can we cut it with these fact-free language wars already?

      1. 7

        People can have their opinions right? Doesn’t mean we have to read them, that’s where the hide button is for.

        C++ is changing rapidly, (as rapid as an iso standard can change), I guess in the future we might even ditch types all together. We already can store “anything” without explicitly knowing the type, I recently wrote about that: https://raymii.org/s/articles/Store_multiple_types_in_a_single_stdmap_in_cpp_just_like_a_python_dict.html - recently there was this as well: https://artificial-mind.net/blog/2020/10/10/return-type-overloading and with all the templates and auto everywhere, you can write a dialect of c++ which looks like Javascript or python…

        Most of these “opinions” don’t write c++ professionally, but just in university. If you need raw performance or work with anything embedded, c++ is your best bet. But all these youths only know Javascript and the hip frameworks of today, which reflects in their opinions. Not to say that is wrong, and I do like that they write, I like reading opinions that differ from my own. However, I think that Your argument falls apart quickly if your biggest gripe is “std::” is annoying” or as toy say, it’s not go.

        1. 3

          yes but headlining the own opinion as absolute truth doesn’t hold its promise

          1. 2

            I don’t think that types will ever go away. You can do typeless programming right now with void*. But nobody is doing it, because it’s not useful.

            Python, Ruby, JavaScript (-> TypeScript) all started without types and are heading towards types (at least they’re giving an option to use them).

            My crystal ball tells me that in the future we’re more likely to use conditional types (so, more strict type of types) than to drop types altogether.

          2. 2

            Go is a weird comparison. If you’re in a problem domain where global garbage collection is acceptable in terms of latency and memory overheads, C++ is definitely the wrong choice because there are a lot of languages where you can be more productive if you’re willing to sacrifice some control. Once you’re in that worlds, there are also a lot of languages that are nicer than Go, the only advantage Go has in that space is that it’s easy to build stand-alone statically-linked binaries.

          3. 7

            For a simple form of this, take the input file stream (ifstream) declaration for reading from each file:

            std::ifstream current_file(e.path()); In this statement, std::ifstream is the type, current_file is a variable name, and (e.path()) is… an argument list to std::ifstream’s default constructor. Previously, this would look like:

            std::ifstream current_file = std::ifstream::ifstream(e.path())

            This is not “new” for C++11, and the “stuttery” version is not how someone would typically write it in “old” C++.

            1. 4

              “Previous” or older would be, here’s a FILE* (pointer) and good luck with it. If you were on Linux at least. Not sure how verbose windows would be.

              1. 4

                On Windows it would be: here’s an opaque value HANDLE and good luck with it. Oh by the way, you can open an existing file by calling the CreateFile() function. It’s signature:

                HANDLE CreateFileA(
                  LPCSTR                lpFileName,
                  DWORD                 dwDesiredAccess,
                  DWORD                 dwShareMode,
                  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
                  DWORD                 dwCreationDisposition,
                  DWORD                 dwFlagsAndAttributes,
                  HANDLE                hTemplateFile
                );
                

                Example:

                HANDLE hFile = CreateFileA("path", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL)
                

                By the way, if there’s a Windows programmer who wants to tell me to drop the A function in the favor of W, then please read this document, which tells that MS doesn’t recommend using W anymore. They want to push UTF8 support through the A family of functions. I think that UTF8 is superior to UTF16, that’s why I’ve pasted the A function ;)

                1. 2

                  Thank you, been a couple years since I’ve written C++ on Windows but I’ve always reached for the W functions.

                2. 1

                  To be clear I wasn’t talking about the i/o library so much as the authors claim that X x(arg); was some recent new variable declaration syntax shorthand, and that X x = X(arg) was what it replaced.

              2. 5

                Lots of focus on syntax, some of it just plain wrong (the weird assertion about how C++ constructors “used to look”.) C++ sure won’t win any beauty contests.

                Go is clearly the winner of the Standard Library competition. I’ve slowly come to appreciate the STL, but it’s still awkward and limited. Go’s library is like a big box of chocolates by comparison.

                But despite this, I gave up on Go after a few years, and I’m back to C++ as my daily driver. Go is IMHO just missing too much vital stuff — generics, true OOP with inheritance, exceptions, a sane access-control syntax that doesn’t make you do global renaming just to change a function’s visibility, interoperability with other languages, alternatives to GC when you can’t afford pauses.

                But when I think of how fucking much time I and my co-worker spent in the past year just getting TCP networking to behave properly cross-platform in C++, I really miss using a modern language.

                1. 3

                  Maybe you’d like Rust? It has some of Go’s nice properties (like easy high-level networking), but doesn’t have a GC, and has generics, robust error handling, and C ABI compatibility.

                  1. 4

                    I’ve tried Rust; in theory I love it, but I find it verbose (compared to Swift, which has better syntactic sugar for optional and errors), and I keep losing arguments with the borrow checker for no reason I can understand. Meanwhile, I discovered Nim, which is super fun to use, so that’s become my weekend sports car.

                    1. 2

                      A discussion of a similar blog rebuttal Re: C++ over on HackerNews also has Nim currently winning the performance contest, by a decent margin.

                2. 5

                  I don’t like writing more than 500-1000 lines of C++ by hand, but I like generating it. It has some nice properties for code generation: https://news.ycombinator.com/item?id=24052268

                  The Souffle Datalog Compiler makes good use of it (used to prototype Rust’s type system)

                  1. 1

                    If something can deterministically be generator from whatever format you input your information,behy not making that format the first class citizen? What is the point of the intermediate format, I. This case C++.

                    In the mid 2000s, all thre rage among java developers was how code generation would be all end all. That failed miserably as it became clear it was just a symptom of syntax obesity. The same applies to c++.

                    1. 2

                      Many, many reasons:

                      • the intermediate format is widely understood by tools, while the source format isn’t (in this case it’s like 3 or 4 custom DSLs).
                      • It takes many custom tools to generate the intermediate format from source. The tools aren’t really public; I don’t distribute them.
                      • the intermediate format is architecture-independent. Same reason that people distribute source tarballs. Binaries are a separate step.
                      • C++ has great debugging support, great profiling support, and more. These tools understand the location of statements and the structure of functions / methods.
                      • a C++ compiler is a huge thing that does a huge amount of work for you. It’s kind of a big black box that’s deployed everywhere, and you can twiddle its knobs and get great results. Of course that requires understanding a bunch of C++, but that work pays dividends.
                      • for the Souffle datalog case, they make unique use of templates. Similarly there are some pretty interesting uses of templates in Eigen, a linear algebra library. It’s a tool you can use to express things that are not easily expressed in other ways, i.e. generic algorithms and specialized versions for performance. You don’t get that by writing raw assembly, or by generating LLVM IR (which is not a stable format).
                        • Oil doesn’t really use this; it only makes basic use of templates. But I just point it out as something you’ll be hard-pressed to get any other way.

                      It sounds like you are making a very abstract argument, not one based around engineering…

                  2. 3

                    Go is cleaner, but C++ can combine OO, operator overloading and performance in a way that few other languages can. For instance, writing a vector or matrix library in C++ is a case where operator overloading is helpful and makes code clearer. Additionally, the performance is good enough and the encapsulation is good enough so that it can be used as a building block in larger programs. A similar building block in Go will have less performance and be a more cumbersome building block. Imagine typing out a long mathematical expression involving vectors, but with function calls at every little step.

                    I think Go is superior in anything that has to do with servers, but C++ has advantages for embedded and for graphics programming.

                    The long compile times is a huge disadvantage for C++, though, but it’s always possible to embed a Lisp or a scripting language…

                    1. 2

                      It would be nice if C++ had a way to turn-off its deprecated footguns.