10 ms·
Modern C++ – RAII
- growlNark 1y ago> Embracing Resource Acquisition Is Initialization (RAII) makes C++ the safest, most productive and fun C++ has ever been. This seems like an extremely low bar. Anyway, what use is there for C++ in 2025 aside from maintenance of legacy codebases and game engines? Off-hand I'd say C++ programmers are twice as expensive as rust programmers for the same semantics, and then you're still stuck with a C++ programmer you need to figure out how to socialize with.
- motorest 1y ago> This seems like an extremely low bar. Is it, though? Most mainstream languages fail to support anything resembling RAII, at least as first-class support. Do you actually have an example of a language that does a better job at resource management than C++?
- asyx 1y agoRust simply because the constructor being a static method on the type that can return basically anything including optional and result types and copy / move semantics are handled very easily through macros instead of constructor / assignment operator overrides.
- deleted 1y ago[deleted]
- motorest 1y ago> Rust simply because the constructor being a static method (...) This assertion makes no sense. RAII is not defined by whether you use factory functions or special member functions to initialize an object. In fact, RAII is only incidentally related ro memory management. RAII is a strategy to manage any and all types of resources, ranging from memory management to even files and TCP connections, by leveraging assurances that the runtime provides regarding scopes and object life cycles. None of this changes if you employ factory functions to initialize resources.
- palata 1y agoNot that I agree with the condescending parent (e.g. "you're still stuck with a C++ programmer you need to figure out how to socialize with"), but... > Most mainstream languages fail to support anything resembling RAII Wikipedia says: "RAII is associated most prominently with C++, where it originated, but also Ada,[3] Vala,[4] and Rust" > Do you actually have an example of a language that does a better job at resource management than C++? They don't necessarily offer a pattern like RAII, but what about "try-with-resources" in Java, or "use" in Kotlin that goes with `AutoCloseable`? And what about "using" in C#? What about "defer" in Swift? I find those simpler than RAII.
- pjmlp 1y agoOne caveat though, you need to pair those with static analysis tooling, if you want to ensure developers don't forget to make use of them.
- palata 1y agoHow does RAII solve that? Developers can "forget" to use RAII, right? Or are you saying that it's easier to spot because RAII requires quite a bunch of boilerplate (whereas a one-liner like "defer" is easier to forget)? Not criticising, really trying to understand :-).
- pjmlp 1y agoRAII when supported natively by the language, is done at the implementation level, not the usage point. So in languages like Rust, D, Ada, Swift, C++, the compiler will do the rest unless you go out of your way to avoid the call to take place, like placing a value type on the heap using plain pointers. With the other approach, even if you implement IDisposable, AutoCloseable, ContextManager and similar, you have to remember to manually write the code pattern that takes care of calling close(), or whatever the method/function happens to be called. In languages with good support for FP patterns, like trailing lambdas, currying and such, there is another pattern, that is much safer, in case you don't want a static analysis tool to track resource usage, the with pattern. You do something like withDBConnection connection (fun db -> all related db operations). Assuming the lambda doesn't do naughty things to have db parameter escape the scope, the withDBConnection function will take care of handling the whole connection lifecycle.
- tialaramex 1y agoObviously a language with linear types is just better at this, such as Austral. In terms of languages you'd actually deploy today Rust is better both at this narrow feature and more broadly.
- znkr 1y agoWhen I switched from C++ to a bunch of other languages, I missed RAII initially. However, I quickly learned that other languages just do it differently and often even better (ever had to check an error when closing a resource?). Nowadays, I think that RAII is a solution to a C++ problem and other languages have a better solution for resource management (try-with-resources in Java, defer in Go, with in Python).
- motorest 1y ago> However, I quickly learned that other languages just do it differently and often even better (ever had to check an error when closing a resource?). I don't think that's even conceptually the same. The point of RAII is that resource deallocation is ensured, deterministic, and built into the happy path. Once you start to throw errors and relying on those to manage resources, you're actually dealing the consequences of not having RAII.
- oytis 1y ago> try-with-resources in Java, defer in Go, with in Python Or 'goto error8;' in C. Still RAII is much more convenient, especially for cases where you allocate a lot of interdependent resources at different time points. It keeps deallocation logic close to allocation logic (unlike, say, defer), makes sure deallocation always happens in the reverse order of allocations and doesn't force you to create a nested scope each time you allocate a resource
- pjmlp 1y agoAda/SPARK, with better ergonomics than Rust, but its use will stay niche in high integrity computing.
- pjmlp 1y agoFor one, the compiler and runtime implementation of the language you might happen to use, that that includes Rust.
- oytis 1y agoWhere are C++ programmers paid better than Rust programmers? I thought Rust salaries are being driven high by all the crypto/fintech companies + scarcity
- bluGill 1y agoproductivity counts not hours. If you can get the same work done faster then you cost to the company is lower even - possible even if your salery is higher. though it isn't clear how much of rust's increased productivity is caused by being a new language where the architecture mistakes of the past decades are not slowing you down. We will need several more decades to answer that.
- flohofwoe 1y ago> stuck with a C++ programmer you need to figure out how to socialize with. You're obviously trolling, but: IME Rust attracts the same 'difficult' characters that are also attracted to C++ (because both are 'puzzle solving languages' instead of 'problem solving languages') the typical Rust coder may be even worse because of the missionary fervor and 'holier than thou' attitude.
- oytis 1y agoPuzzle-solving languages are rather Lisp or Haskell. Both C++ and Rust are very practical for their problem domain.
- djmips 1y agoOh no, C++ has template meta programming and the ability to mask a DSL as advanced architecture, and then you can even implement Lisp or pick your favorite while claiming you're coding in C...++
- sesm 1y agoIDK about Haskell, but iterating in REPL with Lisp is the most practical form of programming I've experienced. In other mainstream practical languages this approach is reintroduced as a productivity tool like Quokka, etc. C++ was practical some decades ago (hardware-friendly variant of OOP for GUI), but it failed as a library language and the domain where it's practical on modern hardware is much smaller. I will not say anything about Rust.
- delta_p_delta_x 1y ago> but it failed as a library language This is very inaccurate. Essentially every high-performance library, user-mode driver, desktop application, and more is written in nothing but C++. Give me any library you can think of, and I assure you it is written in C++ (or maybe C, but this is masochism on the part of the developers). Even libraries for other languages like numpy, pandas, pytorch, etc are written in C++.
- BoingBoomTschak 1y agoOnce Qt is properly accessible in Rust, I think your claim will be a lot more realistic. This goes for other important C/C++ libraries (e.g. libavcodec, vips/ImageMagick, VTK) without a solid (maintained, documented, etc...) Rust interface.
- tialaramex 1y agoQt is a C++ kitchen sink library - perhaps it feels very important to C++ programmers but I can't imagine Rust people are looking forward to having more redundant types that do exactly the same thing as a type they've used for years but like, now with a Q in their name. The fact that lists like yours so often end up being "Look at all these C libraries" ie not actually about C++ at all is revealing. It's an endorsement of Bjarne's position that he needed that C compatibility, decades later C++ alternatives remain unpopular but it also tells you that you're never going to raise the bar this way. C++ is not a route out. AFAIK there is no equivalent of rustls-openssl-compat for C++. The knowledge that this library (OpenSSL) is trash never spurred any C++ programmers to do better and provide the same ABI but with a C++ implementation.
- BoingBoomTschak 1y agoWhat's your point? There's a lot to criticize about these libraries, but the fact remains that they're considered important and basically impossible to rewrite from scratch. Of course, Rust could get a rock solid portable GUI toolkit that doesn't rely on a webview (probably not much harder than wrapping Qt, to be honest), but it's not there yet. I'm not in love with them or the abomination known as C++, if that's what you're implying.
- tialaramex 1y ago> basically impossible to rewrite from scratch How so? This definitely needs at least a citation of somebody who can explain in detail why this code is "impossible to rewrite". Who wrote the one we have now, Martians?
- ogoffart 1y ago
- green7ea 1y agoAuthor here, I do prefer a language like Rust that makes all of this automatic but that isn't always an option. There are other parts of Rust that can be hard as well. I wrote this article for a few friends who have recently started working with an existing, large C++ code base. Some industries like the video game industry have also stuck to C++ (they have their reasons). A good programmer can work within the given constraints to make a useful program — sometimes, one of those constraints is the choice of language.
- kjuulh 1y agoI am by no means a C++ expert, a noob rather. It might be possible to make this generic, but it seems quite easy to forget a small detail here, and then be kneecapped because you forgot one of the overloads. So that in very few amount of edge cases your abstraction isn't actually cleaned up. Better than nothing, and might be the most preferred way of doing things in C++, but it does seem dangerous to me. ;)
- znkr 1y agoI used to program a lot in C++ but switched to a number of different programming languages since then. Everything in C++ is this way and it’s hard to understand that things don’t have to be this way when you’re in the trenches of C++.
- jiggawatts 1y agoI distinctly remember what a breath of fresh air it was to switch to Java and then later C# where in both languages an "int" is the only 32-bit signed integer type, instead of hundreds of redefinitions for each library like ZLIB_INT32 or whatever.
- pjmlp 1y agoNow you can enjoy native ints on C#, as they improved C# for low level coding tasks.
- jiggawatts 1y ago"Native" types like usize and isize as used in Rust I'm totally fine with. What I got frustrated with in C/C++ is the insanity of each and every third-party library redefining every type. I understand the history and reasoning behind this, but it's one of those things that ought to have been fixed in the early 2000s, not decades later when it's too late.
- orwin 1y ago
- 90s_dev 1y agoWhy don't we just use shared_ptr most of the time? Is it really that inefficient?
- ajross 1y agoFor the same reason that you don't use Rc for everything in Rust. Putting all your heap management behind reference counted pointers isn't "that inefficient", no. But if you don't need the direct control over heap behavior, you shouldn't be using C++ (or Rust) in the first place. Languages with GC-managed runtimes (Java, C#, Go, Swift, et. al.) are actually significantly more performant for almost all heap-bound use cases, actually. Reference counting kinda sucks for typical code.
- pjmlp 1y agoManual reference counting to be more precise. When a GC-managed language uses reference counting as implementation algorith, the compiler might be able to optimize the reference counting to only occur when it is unavoidable or too costly to reason about, in a similar vein to bounds checking. When using library types for reference counting, there is no way for the compiler to implement such optimizations, unless the types are somehow tagged with compiler intrisics so that they could apply the same kind of optimizations.
- ninkendo 1y ago> When using library types for reference counting, there is no way for the compiler to implement such optimizations Aren’t refcounts in shared_ptr done as part of the copy constructor and destructor? It seems like vanilla copy elision would count as optimizing away the refcounts.
- pjmlp 1y agoCopy constructor, destructor, move constructor, copy constructor. All of them have to do counter booking, value count and accessors, also possibly handle weak_ptr booking. Plus how it actually works isn't part of ISO C++, so you're betting your luck on how a specific C++ is actually going to implement it, and possible issues when compiler versions change, same compiler or to another one.
- yahoozoo 1y agoThe color/holding all over this article is annoying.
- green7ea 1y agoAuthor here, I put that in place for people who skim articles or paragraphs — this is a surprising amount of people. Would it be better if it was more subtle? Were you reading in dark or light mode?
- yahoozoo 1y agoProbably just less of it. I see bolding like that and find myself clicking on them thinking they are links. It seems like a few of them actually do have links though.
- green7ea 1y agoThanks for the feedback, I removed the text highlighting. Hopefully it reads better. You might have to control+f5 to see the changes, I had to.
- pjmlp 1y agoIt is a diservice to C++, to paint RAII as modern C++. We were already making use of RAII in C++ compilers for MS-DOS during the C++ARM (the C++ equivalent of K&R C book), one such example would be Turbo Vision C++ variant and Borland International Datastructures Library (BIDS). If anything, it is a pity that 30 years later, it is still something we need to educate people about, as it isn't as adopted as it should be. EDIT: If this is supposed to be Modern C++, at the very least provide C++23 example in 2025, instead C++ from 2011. Meaning import std, using a custom deleter in std::unique_ptr (it doesn't manage only heap types), making use of =default to get back compiler generated member functions, as it would do the right thing in the example for the fd handle.
- grues-dinner 1y agoModern C++ does give you move semantics, which makes RAII a lot more expressive, especially with smart pointers. One things about RAII that I find is that, a bit like inheritance, it's actually not something you have actually do nearly as much as the emphasis it gets in the first chapters of all the books implies. How often do you actually write a move constructor or even a destructor in workaday code? Most of it is done for you by the stdlib (unique_ptr, say) or libraries. Of course it needs to be understood, and understood well, but it feels like landing a plane: you have know it well and it's not unusual and certainly not wrong, but also flying is mostly doing things other than landings.
- tialaramex 1y agoThe C++ 11 move is terrible because it's not the move semantic people actually wanted - which is what people took to calling "destructive" move, the feature Rust provides - instead it's this weird confection where we're scooping the guts out of one object and transferring them into a new object, leaving something hollow behind in order to satisfy the requirements of 1980s C++ programs. In some cases this can be optimised to the same machine code, but not always, and semantically it's not what we actually wanted anyway. In terms of RAII this means arranging that after the move the "moved from" object remembers it no longer has any resources and so when it is destroyed it won't clean up - much easier to just design the language properly.
- deleted 1y ago[deleted]
- nathell 1y agoMy pet peeve with C++ RAII is that it uses destructors for cleanup, so it can’t throw an exception from within the destructor if that fails somehow. E.g. from the manual page of close(2): > A careful programmer will check the return value of close(), since it is quite possible that errors on a previous write(2) operation are reported only on the final close() that releases the open file description. Failing to check the return value when closing a file may lead to silent loss of data. This can especially be observed with NFS and with disk quota. Last time I checked, GCC’s implementation of ~ofstream() ignored failures from the underlying close().
- flohofwoe 1y agoThis is also a problem when an object referenced by a shared_ptr is destroyed much later than expected because some obscure part of the code still holds a reference to the object. That's why in many situations it makes sense to have an explicitly called discard() method, while the destructor is 'empty' and only checks that discard has actually been called. Of course that also means that RAII isn't all that useful for such situations.
- bluGill 1y agoOne more reason to aviod shared pointers. They are sometimes useful enough as to be worth it, but they make code analisys harder.
- 1718627440 1y agoWhy can't you simply call the destructor, where you would call discard?
- flohofwoe 1y agoBecause there may still be shared pointers in the wild holding a reference to the object and the last of those will call also attempt to call the destructor. With an explicit deinit() method you'd basically get an object that's in an deinitialized, but not destructed state (quite similar to the state a moved-from object is in), the object still exists in memory and can react with proper errors to attempts to access.
- 90s_dev 1y agoAbout a year ago I bought the official C++ book and read through it fully (including most of the reference part) after finding out that Animal Well was written in it. It honestly seems like a very good and interesting langauge, if carefully used, especially for games where memory can be allocated at startup and held forever. Kinda wish I had an excuse to write some C++.
- ninkendo 1y ago> Kinda wish I had an excuse to write some C++. I had the same feeling in grad school back ~15 years ago… I could pick any language for my coursework, so I picked C++ purely because it seemed an important language to learn. I was also tracking C++0x (later C++11) very carefully just for academic purposes. I’ve still never used C++ at work but I don’t regret using it in grad school, learning it gives a lot of insight into tradeoffs when designing a language, and I felt it was a lot easier to pick up Rust later on having already known C++.
- zokolatte 1y ago[flagged]