C++ is an absolute blast
C++ has gained popularity due to its evolution since C++11, enhancing usability and creativity. It remains versatile for various applications while avoiding the complications of more fashionable languages.
Read original articleC++ has experienced a resurgence in popularity and usability, according to Zed A. Shaw, who reflects on the joy of programming with the language. He reminisces about the excitement of problem-solving and creativity that initially drew him to programming, contrasting it with the frustrations of modern languages. Shaw highlights the evolution of C++ since the introduction of C++11, which brought significant improvements such as automatic type inference, lambda expressions, and enhanced memory management. These changes have made C++ more accessible and enjoyable for developers, allowing for greater creativity without the hindrance of overly complex syntax. He argues that C++ is now a versatile and powerful language capable of handling a wide range of programming tasks, from game development to AI, while maintaining a focus on developer productivity. Shaw also notes that C++ has avoided the pitfalls of becoming overly fashionable, which often leads to the influx of design pattern enthusiasts who complicate the language. This unique position allows C++ to remain a practical choice for developers seeking both capability and fun in their programming endeavors.
- C++ has evolved significantly since C++11, enhancing usability and developer experience.
- The language allows for creative problem-solving without excessive complexity.
- C++ is versatile, suitable for various applications including game development and AI.
- The current state of C++ avoids the complications often seen in more popular languages.
- Shaw emphasizes the importance of enjoyment in programming, which C++ can still provide.
Related
Weekend projects: getting silly with C
The C programming language's simplicity and expressiveness, despite quirks, influence other languages. Unconventional code structures showcase creativity and flexibility, promoting unique coding practices. Subscription for related content is encouraged.
Objective-C is like Jimi Hendrix (2014)
The author compares Jimi Hendrix's influence on guitar music to Objective-C's impact on programming, noting how perceptions of its features have evolved as newer languages emerged, reflecting on programming history.
The Liberating Experience of Common Lisp
The author critiques modern programming languages for their complexity, praising Common Lisp for its stability, unique developer experience, and creative freedom, making it preferable for software development.
The Two Factions of C++
C++ faces internal conflict between modern tech companies and legacy systems, with concerns over its evolution, safety issues raised by the U.S. government, and a growing shift towards Rust.
C++, Complexity, and Compiler Bugs
Azeem Bande-Ali discusses C++ complexities, highlighting issues like temporary lifetime extension, C and C++ incompatibilities, and template disambiguation, while advocating for a simpler successor language to enhance development.
I abandoned the language about a decade ago and I don't see myself looking back. My projects these days need long-term reliability more than anything and rust + cd/ci hits a sweet spot. That said, I do miss the thrill of designing and executing a program that runs in a certain way exactly as I intended and knowing that it was my expertise and insight that allowed this execution. Would I want to work with someone that was driven by that? Hell no! But it is personally a joy I won't forget.
Other inexcusable pain points: the build systems and package management is an absolute nightmare; the pre-processor feels like a sadistic joke; the syntax is horrible; there's so much cruft in the runtime you need years of experience to not machine-gun your foot off by using the most obvious tool at your disposal. But in a sense this just increases the joy of shipping a working executable with all your cleverness and blood and tears wrapped with a bow.
The simultaneous description of modern C++ as "extremely high quality" and yet pervasive legacy bs, horrible tooling, etc is confusing. They say unique_ptr is great yet they "really hate RAII". Many of the discussed topics are largely irrelevant or incorrect. Metaprogramming used to be everywhere but now isn't (what?? Take a look at concepts)? C++ has the best graphics libraries (surely not true)? Installing Python is harder than C++? These seems like odd obsessions of the author, with little relation to the argument.
I think the author just finds C++ fun for some complex combination of personal factors, which is fine. Perhaps partly due to a "hacker" type personality. Some of the most fun I've had with programming has been C++, due to its performance and technicality. At the same time, C++ is in practice crap for "real" software development where your own short-term enjoyment is not paramount (for a billion reasons we all know). C++ is a deeply conflicted language, scarred by decades of legacy and politics. Ignoring the reality of C++ is the biggest mistake of those who discuss it. My attitude towards C++ these days is of tiredness. I don't want to jump through the hoops C++ has thrown at me for the past many years. We don't need to beat an already long dead horse. It's ok to let C++ be what it is.
I really want to love C++. It gives me a more powerful C that theoretically should improve my output but in the end it carries so much cognitive baggage that I usually end up writing "C with classes" and move on.
If I really need the low-level control, I can wrangle C (warts and all), otherwise Rust, Python, etc just make me happier.
I appreciate the shout outs to some packages and libraries to play with, although I still often find it a pain to incorporate other libraries into my projects. (Single-file headers, anyone?) I'm intrigued by FTXUI.
And boy, howdy, he's right: cppreference.com is amazing. Python's documentation is pretty good, but I've never seen anything as good as cppreference.com.
I had the privilege of working on a codebase that was about 5 years old, written to C++11 standards or later, and I thoroughly enjoyed it, for many of the reasons expressed in the article.
If you are working on an older codebase that has evolved over 20-30 years, or one that has not been maintained by talented people, you will have a very different experience.
Sadly, `std::regex` is anything but good (poor performance, lack of Unicode support) and should be generally avoided.
Having dabbled a bit with Rust recently I can't see any strong reasons to use C++. The combination of strong functional programing inspired type system with performance seems unbeatable. Almost a perfect language.
I'm sure there must be some legacy reasons to use C++ though. Maybe Game Engines, embedded programing, some kind of other legacy tie in?
When I looked at and test drove rust all I saw was heaps of complexity stacked up yet again. Probably didn't help that I smacked headlong into the Arc Mutex and lifetimes mess. I'm really more inclined to go for something like 'zig' which does so much with simple syntax (and awesome comptime) and still gets excellent performance.
I'm baffled by this. Deterministic destruction (necessary and basically sufficient for RAII) was one of the best design decisions in C++, I think. RAII means you can't forget to clean up a resource. You don't have to type anything for it to get cleaned up at the right time, it just happens. It's basically what GC promised developers, except for all resource types, not just memory. Your code gets shorter and more correct.
I'm extremely curious to hear how this could ever be bad.
Every time I write C code.
- C -- No hiding, no safety
- Python, Javascript, LISP, other interpreted languages -- hiding with safety
- Pascal, Ada, Modula, Rust -- hiding with safety
- C++ -- hiding without safety.
C++ is, decades late, trying to get to hiding with safety. But there's too much legacy.
It gives you an appreciation of just how unlikely we're to ever move away from the stuff, short of an LLM innovation that can digest codebases of that size and do an automated port, which I suppose is not outside of the realm of reality these days.
I didn’t use 80% of what’s in the book, but just having a comprehensive way of structuring the code was a massive productivity boost. Looking back, I suspect it was less that it was “the right way”, but just that it was “a way” and most of the benefit was it kept me from overthinking and got me to work.
Later with C++11, I kept having this thought, “in Python this would be way less verbose”, and I started writing C++ that looked more like Python, creating whatever helper functions Python would have (mostly simple stuff, string handling, etc).
That was one of the most productive seasons of programming I ever had, and I still get tempted to write stuff in C++ that Python is better suited for, just because the benefit of not overthinking is that significant (at least for me).
Basically it is like renaming those JavaScript files from .js to .ts, and keep coding as if nothing else is available as productivity and safety improvements.
It's a desirable trait for my personal projects, where I may use Haskell, Ruby, Ocaml, Racket or something more exotic.
At work, I'd rather use languages that are boring, with well defined and uncreative patterns and practices. Professionally I expect to not be surprised often and I want the smallest group cognitive load possible.
Languages that breed too much creativity tend to have a rather short list of killer software (that kind of software that makes it worth learn a specific programming language).
Can anyone point me to a learning reference that will let me jump the meta programming apocalypse and just get to the good stuff?
I've often cursed in C# because something that could be done trivially in C++ if impossible and causes the dev to create convuluted C# while it could be trivialy done in C++ due to its very expressive language features.
Those 0.1% of the time that you need those extreme features are what makes or breaks the language in PRODUCTION.
But there's a downside to C++ as I still see it now: steep learning curve in tooling. I am used to work on legacy projects with long lifespan (mostly infinite, not that I look at it :) ) and kinda got lost when tried to contribute to a modern project (I'd call it a hipster project): the amount of abstractions in build/management tools was terrifying.
CMake, for example, is and abstraction over an abstraction above another abstraction. And they used an abstraction over CMake! It also required Python. And not the one available in my OS.
I admit, I'm more used to cursed MSVC projects and simple Makefiles. But when CMake starts its dirty work, it's like a black hole of weird scripting and endless pulling of hopefully compatible dependencies you can hardly control (yeah, where does MSVS store that pulled and precompiled garbage? Oh, shi- it's in %userappdata%??? what about other users? What if I want it on a different drive, like MY project?). CMakeLists.txt has a syntax of it's own, and a really obscure one: is it declarative? Are those commands? When does the order of lines matter? Why are CMakes so incompatible? What are those vars? How is any of that online documentation useful? How do I make things work as I want?? And it still doesn't because some required repos have moved, are offline or became incompatible.
The infrastructure surrounding my helloworld.cpp was getting SO immense, but it never built as... in the end I was required to upgrade to Windows 10 or 11, which I'd never do, so... This is the sad non-C++ part of C++ programming that I utterly hate.
but, dear author:
> +95% of the compiler errors
this. this is unforgivable
+X means "additional X". As in "I have Y and I add +X to it". When you are invited to an event, your invite states you can take your +1 with you (one more person that will come within the same invitation)
if you want to say "more than", you use X+ !! as in "95%+". Because you have some X amount, and you add some more to get X+...
get it together. you're awesome
No, just no. All of them are complete, utter crap that doesn’t hold a candle to languages that were designed with packages in mind. We’re using Conan at work, I’ve been using vcpkg at home and I loathe both.
> I mean, do you really think Python's package management is top notch? You do? Why are there like 10 package managers then?
Worst Python package manager runs circles around whatever C++ offers.
It all became clear by the end of the post — the author is a Windows user.
My gut feeling wasn't wrong. Give me my 20 minutes back please.
Fun wise though, I get the most fun with dynamic languages that are highly interactive like Forth, Common Lisp and Smalltalk. I don’t have to drop out of the zone to kick the compiler all the time.
But with SwiftUI, Swift has also become "unfun." SwiftUI and Apple's half-assed, broken observation and "reactive" paradigm have made programming a joyless slog.
When I had to script things I chose JavaScript (native JavaScript) since it's way faster to iterate, but I've always missed the static typing (I also know python, but I honestly prefer JavaScript)
Until I learned Kotlin. It's been a blast to use, incredible common libraries, streams everywhere, nulls that you can use without issues...I just love it (so much in fact that I'm in the process of switching project from java to Kotlin).
When I need to do scripts, like for the advent-of-code, I choose Kotlin.
I mean, could you estimate the cost ($ and time) it would take to rewrite the best audio framework in any other language? (https://juce.com/).
While I understand that not everyone gets to work on a John Carmack level codebase, is working on a C++ project really as challenging and unrewarding as it seems? Is the productivity that low, and do every step feel so difficult that they eventually drive developers away?
I will be very impressed and curious if I find a glowing article about C++ from someone who didn’t grow up knowing it as a smaller, simpler language.
The C++ community needs enthusiastic converts who didn’t do it back in the 2000s if it’s going to stay relevant.
If you choose technology for work by what is the most fun - you enter a hedonist treadmill. Stop. JS framework insanity lies that way. No cool technology will save you from burnout.
Ouch...
Preferably for Linux and/or Windows.
I know almost nothing about C++ and have never programmed in it... but I thought that the point of that kind of 'template metaprogramming' was that the code got executed at compile time instead of runtime?
i.e. instead of generating identical assembly output the goal would have been to output a constant value
I’ve even seen developers use it instead of bool, which is pretty laughable as the they are the same number of characters.
Yes, it's way too easy to do dumb stuff in C++, when you're tired or not sure what you're doing. Things like holding raw pointers or references to things you shouldn't like std::vector::data(), or questionable reinterpret casts and many other things. The compiler won't stop you, only your experience.
But he's right about one thing at least: Programming should be fun!
All these layers and rules and concerns about memory safety and security don't offer only advantages. They also have tradeoffs. And it's the same thing with those scrum agile ceremonies. It serves its purposes. But it's also the best invention ever to suck all the joy out of programming.
I think that both C and C++ still have that fun feeling going for them. When you know what you want to work on and how to do it and you just start doing it and get into the flow. And if you're careful and do things right, it just works and it's a blast!
That's my takeaway from the article. That feeling like you're talking directly to the machine, getting it to show you on screen what you saw in your mind, without anything else getting in your way. Now, that is fun!
(it is a good smile)
No, it's one of the worst languages I ever used. Tons of footguns and bad design choices everywhere. Too much cognitive load for less benefit than other languages.
I'm surprised the article didn't mention <iostream>. The f.fail(), f.eof(), f.flags() are confusing and verbose. Even something as simple as f.read() doesn't return the number of elements read, so you need to make a separate call to f.gcount(). And then there are all the opaque types like std::streamsize, std::mbstate_t, etc., where you have no idea how their sizes relate to language types like int/long/etc. or fixed-width types like int32_t/uint64_t/etc. https://en.cppreference.com/w/cpp/string/char_traits
And then there are the redundancies. int x = 0; int x(0); int x{0}; all roughly do the same things but have subtle differences in more advanced use cases. This recent thread ( https://codereview.stackexchange.com/questions/294784/c20-ro... ) reminded me that `typedef` got replaced by `using`. A while ago, I came up with a long list of near-duplicate features: https://www.nayuki.io/page/near-duplicate-features-of-cplusp...
> JavaScript still can't even figure out what a for-loop is
ECMAScript 6 added the for-of loop, which is the more useful alternative to the for-in loop.
> C++ has lambda, and it's not bullshit like Python's lambda
C++ lambdas have a heavier syntax than any other lambda I know of (e.g. Python, Java, JavaScript, Haskell, Rust), because it needs to specify attributes and captures. https://en.cppreference.com/w/cpp/language/lambda
> My thinking is C++ is now about as good as any other language out there
Not by a longshot. Instead of C++, I reach for Java if I want fast design time, safe operations, and a more limited set of tools (e.g. not needing to decide how many layers of pointer indirection I want). I reach for Rust if I want the power of C++ without its footguns.
Heck, my motto for Rust has always been, "C++ done right". Every time I compare analogous features in C++ and Rust, I find that the Rust version is much better designed. As the simplest example, in Rust it's a compile-time error to use a variable whose value is moved out, whereas in C++ the variable is still usable but has an invalid value. Another example is that Rust has traits but C++ relies on instantiating templates and then "duck-typing" to see if the resulting code can actually compile. And let's not forget nullptr, the trillion-dollar mistake - C++ makes nullptr implicitly part of every pointer(*) type, but Rust bans it by default unless you opt in with Option<T>. Rust has other quality-of-life features such as easily declared tuple types, the unit type instead of void (which makes functional programming easier as you don't have to special-case void), pattern matching and unpacking, methods on primitive types (e.g. 456u32.isqrt() instead of sqrt(456)). I just can't look at C++ seriously when Rust is miles ahead, being more expressive and safer.
> The Amazing Comeback of C++11
I will agree with this in a limited sense When I write C++ code (because I'm a masochist), I will not tolerate anything less than C++11, because C++03 and C++98 are much, much worse. I'm talking about things like various types, standard library classes/functions, unique_ptr, and move semantics.
I got another C++ job about 3 years ago but bailed after about a year.
I could write a tome about what I dislike but to start with, any language that lacks a working standard built-in string type, is just a hard no for me at this stage in my life. Life is just too short.
The tooling and IDE support is atrocious, no standard dependency management for 3rd party libraries and CMake makes maven look well designed.
I tried to pull my knowledge up to date. Hmmm, we used to have lvalues and rvalues, what's this prvalue thing?
Surely cppreference can explain:
> a prvalue (“pure” rvalue) is an expression whose evaluation
> - computes the value of an operand of a built-in operator (such prvalue has no result object), or
> - initializes an object (such prvalue is said to have a result object).
> * The result object may be a variable, an object created by new-expression, a temporary created by temporary materialization, or a member thereof. Note that non-void discarded expressions have a result object (the materialized temporary). Also, every class and array prvalue has a result object except when it is the operand of decltype;*
> The following expressions are prvalue expressions:
> a literal (except for string literal), such as 42, true or nullptr;
> a function call or an overloaded operator expression, whose return type is non-reference, such as str.substr(1, 2), str1 + str2, or it++;
> a++ and a--, the built-in post-increment and post-decrement expressions;
> a + b, a % b, a & b, a << b, and all other built-in arithmetic expressions;
> a && b, a || b, !a, the built-in logical expressions;
> a < b, a == b, a >= b, and all other built-in comparison expressions;
> &a, the built-in address-of expression;
> a.m, the member of object expression, where m is a member enumerator or a non-static member function[2];
> p->m, the built-in member of pointer expression, where m is a member enumerator or a non-static member function[2];
> a.*mp, the pointer to member of object expression, where mp is a pointer to member function[2];
> p->*mp, the built-in pointer to member of pointer expression, where mp is a pointer to member function[2];
> a, b, the built-in comma expression, where b is an prvalue;
> a ? b : c, the ternary conditional expression for certain b and c (see definition for detail);
> a cast expression to non-reference type, such as static_cast<double>(x), std::string{}, or (int)42;
> the this pointer;
> an enumerator;
> a non-type template parameter of a scalar type;
> a lambda expression, such as [](int x){ return x * x; };
> (since C++11)
> a requires-expression, such as requires (T i) { typename T::type; };
> a specialization of a concept, such as std::equality_comparable<int>.
> (since C++20)
> Properties:
> Same as rvalue (below).
> A prvalue cannot be polymorphic: the dynamic type of the object it denotes is always the type of the expression.
> A non-class non-array prvalue cannot be cv-qualified, unless it is materialized in order to be bound to a reference to a cv-qualified type(since C++17). (Note: a function call or cast expression may result in a prvalue of non-class cv-qualified type, but the cv-qualifier is generally immediately stripped out.)
> A prvalue cannot have incomplete type (except for type void, see below, or when used in decltype specifier).
> A prvalue cannot have abstract class type or an array thereof.
Yeah, this language is loads of fun. I've worked on compilers, interpreters, implemented extended Hindley-Milner type systems, etc. so normally love reading formal language specs but this is just insane.
Which industry is this referring to?
Love this. This is an old post though right, still talking about C+11?
Glad to find the standard library progressed so much to make C++ more similar to modern languages, but sometimes I wish there was a simpler way to access the std namespace. Any proposal to replace "std::" with $
1. Build and run it with a shell script (because the build systems do suck)
Dependencies may complicate this, but you can still link with them with a shell script
And use Unix – I started with C++ on Windows, and that sucks (also mentioned in the article)
2. Turn on address sanitizer in development, which makes it memory safe – you get a Python-like
stack trace on errors instead of undefined behavior
Example: $ echo 'int main() { return 42; }' > foo.cc
$ c++ -fsanitize=address -o foo foo.c && ./foo; echo $?
42
I use an actual editor, and unit tests, but essentially shell is my REPL for C++. It’s easier to figure out that way.Newer features like constexpr have subtle rules, so it’s easier to just try it (even though I’ve used C++ for many years). I run all the tests with Clang too.
---
Example with ASAN:
$ echo 'int main() { char buf[1]; buf[1] = 42; }' > error.cc
$ c++ -fsanitize=address -o error error.cc && ./error; echo $?
==118199==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffcd94eb431 at pc 0x56217647520f bp 0x7ffcd94eb400 sp 0x7ffcd94eb3f8
WRITE of size 1 at 0x7ffcd94eb431 thread T0
#0 0x56217647520e in main (/home/andy/git/oilshell/oil/error+0x120e)
#1 0x7f7928446249 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
It’s not too hard to learn to read the output, and then basically you can go nuts like C++ is Python. For a small program, the edit/run cycle will be extremely fast, like Python.The silent undefined behavior is a big barrier to learning, and this removes most of it. (You can also pass -fsanitize=undefined for UBSAN, which finds other bugs, but many fewer IME)
ASAN is built into compilers; you don’t need to install anything. A bare Debian or BSD system has all this good stuff :-)
(copy of lobste.rs comment)
The problem of course lies not with header files, but C++ the language, as all public fields and private fields must be specified in the class declaration so that the compiler knows the memory layout. It's kind of useless in that sense. You can move private methods out to a separate source file, but, you don't gain much in doing so, at least in terms of strict encapsulation. And of course, if you use templates at all, you can no longer even do that. Which is its own can of worms.
Unfortunately, none of these problems are problems that modules solve. Implementations very much disagree on interfaces vs implementations, precompiled vs simply included, etc etc. In my own usage of modules I've just found it to be header files with different syntax. Any API implemented via modules is still very leaky - it's hard to just import a module and know what's truly fair for application usage or not. You still ultimately have to rely on documentation for usage details.
At the end of the day I don't really care how the implementation puts together a particular feature, I care about how it affects the semantics and usability of the language. And modules do not really differ in proper usage from headers, even though the whole backend had to be changed, the frontend ends up being the same. So it's net nothing.
All said and done, when it comes to defining library APIs, I prefer C. No public/private, you just have some data laid out a particular way, and some functions to operate on it. The header file is essentially just a symbol table for the binary code - and said code can be a .c file or a .o file or even a .a or .lib or .dll or whatever - C doesn't care. Raw functionality, raw usability. No hoops.
This section is, it seems to me, the linchpin of the "fun" argument. But you've been able to do that with all the other languages too, all this time. The much-loathed and feared Rust Evangelism Strikeforce doesn't actually come to your house and make you use a bunch of generic-heavy code from crates.io. The React people can't stop you from using vanilla Js. The worst they can really do is send you mean tweets, but Shaw thinks this is a lethal threat to his creativity, enough to switch language ecosystems over. For an article written in a superficially rebellious, lone-wolf tone, that's kinda sad.
Related
Weekend projects: getting silly with C
The C programming language's simplicity and expressiveness, despite quirks, influence other languages. Unconventional code structures showcase creativity and flexibility, promoting unique coding practices. Subscription for related content is encouraged.
Objective-C is like Jimi Hendrix (2014)
The author compares Jimi Hendrix's influence on guitar music to Objective-C's impact on programming, noting how perceptions of its features have evolved as newer languages emerged, reflecting on programming history.
The Liberating Experience of Common Lisp
The author critiques modern programming languages for their complexity, praising Common Lisp for its stability, unique developer experience, and creative freedom, making it preferable for software development.
The Two Factions of C++
C++ faces internal conflict between modern tech companies and legacy systems, with concerns over its evolution, safety issues raised by the U.S. government, and a growing shift towards Rust.
C++, Complexity, and Compiler Bugs
Azeem Bande-Ali discusses C++ complexities, highlighting issues like temporary lifetime extension, C and C++ incompatibilities, and template disambiguation, while advocating for a simpler successor language to enhance development.