Leaving C++03: for real this time
Welcome. It's been a while. 2020, right?
You might remember that two or three years ago I experimented with migrating to C++11, and I did some preparation (a short recap is in the first part of this post). Well, we’re finally doing it for real. This post lists a few bumps you might find and tells you how to avoid them.
This comes after some setup work. During the past year, I deprecated compilers that wouldn’t be able to compile C++11 code; and in the latest release, I removed support for the last of them (Visual C++ 2012). After which, I started the actual work of taking what I learned from the experiment and migrating the library.
As usual, I’m making an effort to keep the whole thing backward-compatible. You shouldn’t have to modify your own code to work with the next version of the library—not right away, at least. Depending on your code, you might have to change a couple of configuration flags to enable full backward compatibility. Similar to what happens when we deprecate some class or function, those flags are going to be available for the next few releases: this will give you some time to do the required changes. (Oh, in case I wasn’t clear: these changes are being made now in the git repository and will be in version 1.22, so you don’t have to worry about them yet. I’m posting about them now while I still remember what I did.)
So without further ado, here’s your migration guide.
Check your compilation flags
For instance, clang on Mac OS X still compiles in C++03 mode by
default with some C++11 extensions but not all. Make sure you pass at
least the -std=c++11
flag to clang and gcc to enable newer features.
You can pass later standards too, if you want: the library also
compiles correctly in both C++14 and C++17 mode.
Out with auto_ptr, in with unique_ptr
If you used a recent compiler to compile the library, there’s a chance
that it warned you about std::auto_ptr
being deprecated. We already
had a configuration flag allowing you to switch to std::unique_ptr
already (here is the GitHub pull
request that
introduced it), and now it’s enabled by default so that QuantLib uses
std::unique_ptr
.
If you relied on some QuantLib function explicitly returning
std::auto_ptr
, your code might not compile with next release.
Short-term fix for backward compatibility:
You can add the --disable-std-unique-ptr
flag when you run
configure
, or you can undefine QL_USE_STD_UNIQUE_PTR
in
ql/userconfig.hpp
if you’re not running it. This causes the library
to use std::auto_ptr
again.
Long-term fix:
Change your uses of std::auto_ptr
to std::unique_ptr
. If you used
the QL_UNIQUE_OR_AUTO_PTR
macro we provided, you should be ok for
the time being. In the long run, that macro will disappear and we’ll
use std::unique_ptr
explicitly.
Out with Disposable, in with copy elision and move semantics
The Disposable
class was an attempt to implement move semantics in
C++03 code. This is no longer necessary; classes in the library that
needed it now have move
semantics, and in
any case most compiler nowadays implement RVO and NRVO and
Disposable
would get in the way of that. Therefore, it is now
defined as
template <class T>
using Disposable = T;
that is, an alias to the underlying type.
If you wrote functions or methods overloaded on T
and
Disposable<T>
, those functions will not compile now since the two
types are the same.
Short-term fix for backward compatibility:
You can add the --enable-disposable
flag when you run
configure
, or you can define QL_USE_DISPOSABLE
in
ql/userconfig.hpp
if you’re not running it. This causes the library
to use the old implementation of Disposable
again.
Long-term fix:
Remove Disposable<T>
from your code. If you used it to implement a
pseudo-move constructor or assignment, define a proper one by using an
rvalue reference instead, so for instance
Array(const Array&);
Array(const Disposable<Array>&);
will become
Array(const Array&);
Array(Array&&) QL_NOEXCEPT;
(oh, yes, move constructors and assignment should be declared as
noexcept
to allow more optimizations. We have to use a QL_EXCEPT
macro in the library because it’s one of the few features Visual
Studio 2013 doesn’t support.)
Out with virtual, in with override
In C++11, virtual method should only be marked with virtual
in the
base class that declares them. In derived classes that define them,
they should be marked as override
or final
; this allows compilers
to possibly detect and warn against unintentional overrides. In order
to automate this change, I added a clang-tidy check to the GitHub
actions we’re running on each push to the repository.
This shouldn’t give you problems in compilation, but it might cause
warnings such as inconsistent-missing-override
in clang or
suggest-override
in gcc. You might be bothered by
them—possibly because you’re using -Werror
(good practice,
folks).
Short-term fix:
Disable those particular warnings if your code triggers them.
Long-term fix:
You can do as we did and get clang-tidy to fix that for you. Our automated workflow is here: it will check the code, make fixes and open a pull request with them. It relies on this script and this configuration file, both in the QuantLib repository.
I’ll be adding more gotcha to the blog as I use more C++11 features; stay tuned. If you have comments or experiences on the above, the right place to post them is probably the QuantLib mailing list so they reach and benefit the largest possible number of people.
Subscribe to my Substack to receive my posts in your inbox, or follow me on Twitter or LinkedIn if you want to be notified of new posts, or subscribe via RSS if you’re the tech type: the buttons for all that are in the footer. Also, I’m available for training, both online and (when possible) on-site: visit my Training page for more information.