Home > Uncategorized > Arguing with a Decade-Old Eric Lippert Post

Arguing with a Decade-Old Eric Lippert Post

But it’s on the first page of Google results for c++ smart pointer addref release so I have to do this, dammit!

The post in question is: Smart Pointers are Too Smart.

Eric asked:

Does this code look correct?

map[srpFoo.Disown()] = srpBar.Disown();

It sure looks correct, doesn’t it?

Nope, it looks like a disaster waiting to happen. The map ought to have keys and values that are smart pointers, so the correct code would be:

map[srpFoo] = srpBar;

If the map holds raw pointers, that in itself is the error, because it will necessitate some complicated dance to transfer ownership in and out, which means bandying around raw pointers casually, and in C++ that leads to disaster, because it can only be done safely in code paths that can be proven to not throw exceptions.

In fact its even more general and simple than that. If you write some code in your C++ program that initiates any state at all, and the previous state will not be restored automatically during stack unwinding, and you haven’t taken the time to prove that no exceptions will be thrown during this state, you’re doing it wrong.

Far from showing what goes wrong when you use smart pointers, Eric showed what goes wrong when you stop using them, even for a nanosecond. If you never use them, the wrongitude will be uninterrupted.

It’s quite possible that forcing yourself to put all the AddRef and Release calls in the right places by hand will have a sort of positive side-effect: you will be so distracted by this effort that you will not have sufficient time or patience to be productive, and so this will force you to write only very simple programs, which is definitely a good thing, but perhaps arrived at for the wrong reasons.

And conversely, when a problem becomes rarer, it seems more egregious. “I remember the good old days, in the Blitz, when we were being bombed every day, but at least we were used to it!”

The real problem underlying all of COM is probably that IUnknown doesn’t provide a way to attach a listener for when the object is destroyed. If it had this, we could create a safe “weak” pointer, one that doesn’t keep the object in existence but is automatically set to nullptr when the object expires. This is addressed in the more recent WinRT variety of COM, where all sensible objects should implement IWeakReferenceSource, and in fact classes created with C++/CX do this automatically.

And ultimately this is perhaps the simplest response to Eric’s blog post: with C++/CX, smart pointers are baked into the language. Reference count cycles will still occur, no question, which is why the whole exercise is a silly step backwards, but only relative to something much better: pick just about any managed GC-enabled runtime.

Categories: Uncategorized Tags: ,
  1. February 19, 2013 at 11:59 pm

    Any system where “if you stop using it for a nanosecond everything goes to hell” is not a system that integrates nicely with legacy code. Rewriting legacy code to work with the new system can’t possibly be the answer; that’s boiling the ocean.

    • earwicker
      February 20, 2013 at 10:03 am

      To an extent this problem space inherently involves boiling an ocean: an ocean of objects with reference counts that must go up and down according to rules.

      Every time you rework one single container class to use smart pointers, you will probably eliminate a few bugs. That’s not “boiling the ocean” – it’s an eminently practical, pragmatic, step-wise, evolutionary solution. No need to stop the world while you re-write everything. Getting the ref-counting rules right in one place cannot cause bugs elsewhere. The rules have to be followed everywhere (whether manually or automatically).

      Indeed that is C++’s great (perhaps only!) strength, that it gives you tools like this to apply to specific spots within a raggedy old C program… with one major “exception” (the clue is in the name…)

      As you identified in your post, the real “boil the ocean” culprit across all of C++ is exceptions. Because practically anything can throw an exception, exceptions force us to reason differently about every line of code in C++ than we would have in pure C. Every temporary state simply MUST be uniquely owned by some kind of RAII wrapper. The rvalue reference feature, std::unique_ptr and std::shared_ptr in C++11 are all enhancements to the language to support this. It’s why in C++/CX ref-counting smart pointers are baked into the language itself.

      Assuming we aren’t going to switch off exceptions (which in a mature C++ codebase would itself require us to boil the ocean by rewriting our code with a different error handling strategy), we have to use RAII wrappers. A ref-counting smart pointer is simply the RAII wrapper for a ref-counted object, and as such it is the completely indispensable tool to stamp out ref-counting inconsistency caused by exceptions.

      And it can definitely be applied one function, one class, one library at a time. Smart pointers don’t require us to boil the ocean at all. They make it practical to start boiling – one puddle at a time – the ocean that is already there due to ref-counting and exceptions.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: