Moving rocks

Or why old dogs should learn new tricks

Posted by Roy on October 29, 2014

Like quite a few developers of my generation I started programming on my Commodore 64. Initially BASIC, then 6502 assembly. I never got around to doing anything serious with it, but still. I eventually got a PC (briefly interrupted by an Amiga 600) and experimented with Turbo Pascal. However, in order to do anything cool I had to learn C.

I found a copy of Teach Yourself C in 21 days and got cracking. Sure enough, it worked out pretty well. I even eventually learnt just enough C++ to become really dangerous and design elaborate class structures in places where composition would've been much better. I digress, though. Sorry.

Leaks everywhere

One thing I wasn't particularly good at, though, was keeping track of memory. Back then I was using Watcom C/C++ with DOS/4GW and it wouldn't complain about buffer overruns or use-after-free at all. Sure, it might eventually crash my application but it was usually in a place that had absolutely nothing to do with the code that caused the problem. Mostly it'd just silently corrupt some vtable and work on 50% of the runs of my app.

I got better over time, I guess. Initially by just Refcounting All The Things manually using tons of AddRef() and Release() calls everywhere. This at least solved a few use-after-free issues but it didn't stop me from forgetting to call Release() in the first place. Heck, if I didn't remember to delete properly (or deleteing when I should have delete[]d....) why would I make sure I manage my refcounts?

Moving to Visual C++ helped somewhat. The VC CRT tries to be more helpful by crashing more often when you do stupid stuff and there was also a feature of the CRT that dumped a list of unfreed memory on program exit. Couple that with the D3D debug runtime I'd usually get everything to work.

It still sucked though.

So smart

Through a twist of fate I found people willing to hire me to write C++ for a living. This brought me in contact with the more...well, enterprisey side of C++. People who weren't afraid of Boost mostly. That's where I learned of smart pointers. When I asked a colleague there what these things with the funny angled brackets were and how they worked internally, I got the helpful answer "It's a pointer that's smart". Thanks.

It was my reference counting code! Except IT WAS COMPLETELY AUTOMATIC. What the what? This I gotta see! (etc.)

Turns out they were still a bitch to work with. The codebase there used smart pointers, sure, but it also used raw pointers interchangeably. They also enjoyed using references. Sometimes. Not always. Sometimes. Maybe. When I called into an API I had absolutely no idea what was going on under the hood since their system libraries were built by a different team with the code helpfully not-shared. Either they were too embarassed about it or they thought it would confuse the poor application developers.

As a junior dev it certainly did the latter. I had no idea where memory was coming and going. Later on in different companies I had more understanding and also more control over the codebase so smart pointers like auto_ptr and the Boost smart_ptr started to become workable. Even then it was hard to use though. Instead of smart pointers I learnt to use RAII properly and that was even more elegant. Until I tried to put a reference into a container or I had to transfer ownership of the reference from one class to another.

Anyway, I stopped paying attention for a while. I started doing C# and Javascript for a living and we all know that you can throw data around there without a care in the world. Well, unless you care about people keeping their browser tabs open for longer than 10 minutes. I digress, though. Sorry.

std::move

Because then there was C++11. Much like a Terrence Malick movie it took way too long to materialize and has such a wide scope it initially confuses the hell out of a lot of people. But that's okay. You don't need to understand all of it in order to use it.

For me the eye opener has been the combination of std::unique_ptr and std::move. In a hobby application of mine I have this concept of a UiNode that holds a reference to a Node. It's basically just a GUI class that holds some additional properties and delegates a few things to the Node it owns. However sometimes I want to transfer the Node to another UiNode. So UiNode cannot have a Node& node_ field. Normally I'd have used a shared pointer for this. Maybe passed a raw pointer into that other UiNode to indicate it shouldn't delete it. Ownership has transferred, though.

That's what std::move does. It allows me to move the Node from one UiNode to another UiNode. The original owning unique_ptr becomes a nullptr so I can't use-after-free (and if I do, my program crashes immediately) and the new owner holds the reference and will automatically delete the Node when it goes out of scope.

Gee. Why are you telling me this?

No reason in particular, I guess. But it's just delightful to me (and I realize this sounds way more geeky than should be allowable in a public forum) that a seemingly unsolvable problem I came across nearly 20 years ago is finally fixed in the language that I did so much of my coding in. Why am I even asking myself these rhetorical questions, by the way? That just seems off somehow. I digress, though. Sorry.



comments powered by Disqus