This week, back to my series about taking QuantLib to C++11. I’ll tackle
and other features that were deprecated or removed in recent
standards. But first, a coda on automation.
clang-tidy and make_shared
In my last post on automation, I mentioned that the
modernize-make-shared check didn’t work on our code base; it looks
for instances of
std::shared_ptr, but we’re hiding them inside a
namespace for backwards compatibility so we can choose either
std (details here).
With some encouragement, though, it can be made to work: I replaced
std::shared_ptr (which, luckily, is a
one-line invocation of
sed from the command line), ran the check,
std::make_shared with the same
classes in namespace
ext again. The check touched around 550 lines
in 120 files.
It turned out that a handful of changes had to be reverted, though.
First, users can choose to compile the code in C++03 mode, which means
no variadic templates. The implementation of
(because that’s what we can use in this case) is hard-coded to take a
maximum of 9 arguments. Any invocations taking more than that had to
be reverted. Second, VC++10 didn’t compile a handful of cases in
which the result of
make_shared was used as a default argument, as
in that last argument of:
I didn’t bother finding out what was wrong, and I reverted the
changes. Finally, Visual C++ 2012 doesn’t really support variadic
make_shared is written as a bunch of overloads. The
maximum number of arguments is defined in the value of the
_VARIADIC_MAX macro, which by default is not nearly enough. I had to
define it to 9 so that it could support as many arguments as Boost.
A final note: the check doesn’t modernize all that it could modernize.
It will replace
std::shared_ptr<Derived>(new Derived) with
std::make_shared<Derived>(), but it will not touch
auto_ptr and other deprecated features.
Onwards to the main attraction. Writing on automation I mentioned that, in our case,
the modernize-replace-auto-ptr check was—how can I say?—a dumpster
fire. However, it didn’t bother me; I ran the check for completeness,
but I hadn’t planned on replace
Remember our context: we’re moving to C++11 on an experimental branch,
but our master is still using C++03 and is not making the switch in
the near future. Replacing
auto_ptr on the experimental branch
wouldn’t fix the fact that recent compilers have started issuing
deprecation warnings, and our master is affected by that. I wanted
to address this problem as
This means providing the means to choose between
std::unique_ptr at compile time, so that users can opt in to the
newer class when their compiler is ready. The way to provide the
switch is the usual one: a configure flag or a define, depending on
your build system. But how should we switch between the two classes
in the affected code?
If you follow Zach Holman on Twitter (bear with me for a moment if I seem to wander away from the topic at hand) you might have seen this:
Was talking recently with a friend about how hard it is to write blog posts and give talks these days. The longer you’re in the tech industry the more your opinions end up being “well it depends” and “I’m not sure”. These don’t quite make for good blog posts, hah.— Zach Holman (@holman) January 11, 2018
(Incidentally, this also seems to puzzle younger colleagues coming to you with questions and expecting leadership—whatever that means.)
To return to the task at hand: should we define a macro that resolves
std::unique_ptr, or should we be more
explicit and show the choice? The answer I came up with was, of
course, “it depends.”
In header files, which is where people look for a listing of the features of a class, I preferred to be more explicit and write, for instance,
instead of hiding the thing behind a macro that someone reading the code would have to look up. In source files, instead, the above gets old very quickly: something like
hurts readability so much it isn’t even funny. Alternatives like having two versions of the entire method are a bit better, but in the end I settled on defining a macro and writing
In a couple of cases, I had to go beyond a simple replacement. One
was a class with a constructor (among others) taking a
and storing it in a data member: something like
The alternate version using
std::unique_ptr gave us the first foray
into proper modern C++, using rvalue references and moves:
In the other tricky case, I tried to accommodate both kind of pointers and I ended up with something like this:
The final assignment didn’t compile: the copy constructor of
std::auto_ptr, which is used to initialize the argument passed to
Clone constructor, takes a non-const reference—the passed
pointer has to be modified—and g++ couldn’t bind it to the rvalue
clone(). (Strangely enough, clang compiled the same
code without any problems, leaving me fairly puzzled.)
I could have stored
b.clone() in a variable so that I would pass an
lvalue to the
Foo constructor; but that wouldn’t have worked for
std::unique_ptr. I ended up using another
Clone constructor that
Bar instance, as in:
The above works with both
also simplifies the code at the call site, so it turned out to be a
win after all.
As usual, your mileage may vary. You might have more bits of client
code that require different treatment of
so be ready to retouch your code manually.
std::auto_ptr is not the only deprecated feature in C++11, although
the other ones don’t seem to give compilation warnings in gcc (on the
other hand, clang masquerading as gcc on Mac OS X removes them in
C++17 mode, so the code doesn’t compile). The ones used in our code
and (in one implementation file)
The binders would be replaced naturally by lambdas in C++11 and
beyond. However, as for
std::auto_ptr, I looked for a solution that
could also work in C++03 and be applied to the master branch.
The binders were deprecated in favor of
std::bind, but that’s not
available in C++11 either. However, it would have been possible to do
something similar to what I did with
shared_ptr: use an
ext::bind class that
could be mapped to
std::bind depending on the
standard one is using.
Well, I tried that, but somehow it didn’t fly. Sure, it compiled, but in most cases what I was doing was changing 15-years old code like
into something like
which, in my old age, makes me cringe. Long story short, I made a note of revisiting this for lambdas and in the meantime I created a few reusable functors and wrote the above as
If anybody ever said to me in my youth that
std::bind2nd(std::minus<Real>(), 1.0) wasn’t readable and if I went
“Well actually” on them, I am deeply sorry.
The other wrappers were simpler to replace.
std::mem_fun was used
in a couple of places with one of the binders, and was no longer
needed in the functors I used instead.
std::ptr_fun was mostly used
to disambiguate overloaded functions, as in
and could be replaced by a cast such as
std::random_shuffle. Fortunately, it was used in a single
implementation file. There was nothing to replace it with in C++03
mode, so I used conditional compilation and wrapped it with:
The above switches to
std::shuffle for C++14 and above; in
principle, I could have looked for
__cplusplus >= 201103L (C++11)
but some older compiler might declare it and still have incomplete
support, so I played it safe.
So far, I didn’t find the time (or, indeed, the motivation) to research, install and moderate a comment system. If you want to reach out with suggestions or questions, please send me a tweet or an email. I’ll report on your feedback in future posts.
Follow me on Twitter if you want to be notified of new posts, or subscribe via RSS or email: the buttons for that are in the footer. Also, I’m available for on-site training in Europe: visit my Training page for more information.