Leaving C++03: more STL classes
Welcome back.
A couple of posts ago, I
mentioned that two new pull requests made it possible to use
std::function
and std::bind
in QuantLib instead of the
corresponding classes from Boost. Here are a few more
details.
But first, a PSA. I’m thinking of revising these posts about my Leaving C++03 experiment and of publishing them as a book on Leanpub. If you want that to happen, please go to https://leanpub.com/goingtocpp11 and show your interest. Thanks.
Back to the subject of the post. The change was similar to the one
that I previously described for shared_ptr
and its related
classes; that is, I added a file with code like
#if defined(QL_USE_STD_FUNCTION)
#include <functional>
#else
#include <boost/function.hpp>
#endif
namespace QuantLib {
namespace ext {
#if defined(QL_USE_STD_FUNCTION)
using std::function;
#else
using boost::function;
#endif
}
}
and I replaced boost::function
with ext::function
in the library
code. A configure switch optionally sets the QL_USE_STD_FUNCTION
define, thus effecting the change to std::function
. The same went
for boost::bind
(which also took boost::cref
along for the ride),
and since we’re starting to have quite a few of these switches, I also
added a global configure switch --with-std-classes
as a shortcut for
--enable-std-pointers --enable-std-unique-ptr --enable-std-function
--enable-std-bind
.
Of course, it wasn’t all as simple as sketched above.
The first problem came even before starting the convention. Our use
of boost::function
goes way back; in fact, back to ancient times
where not all compilers could parse the boost::function<R(T)>
syntax. Because of this, Boost provided an alternate syntax
boost::function1<R,T>
(with similar function0
or function2
classes for different number of arguments) and used to suggest its use
for greater compatibility. The need for this is gone, but we still
had a few instances of those classes in the code; and of course
there’s no such alternate syntax for std::function
, so they had to
go and be replaced by their more modern equivalent.
Unfortunately, this breaks backwards compatibility. The older
boost::function1<R,T>
class is convertible to
boost::function<R(T)>
, but it’s not the same class (at least, not in
C++03; in C++11, it could be aliased to the newer class via the
using
syntax). And we had a couple of methods using
vector<function1<R,T> >
, which is not convertible to
`vector<function<R(T)> >.
This is a problem. Since version 1.0, I tried to avoid breaking changes and to uphold the promise that if you code against a version of QuantLib, your code will still compile against later versions. Usually, I can manage it by overloading methods and deprecating old ones so that they could be phased out in future releases; but I didn’t have a chance here. One of the methods had the old class in the return type, over which we can’t overload.
In the end, I went ahead with the change. I’ll document the break of compatibility in the release notes for next version, and hope that not many people had inherited the affected classes (which are not fundamental ones).
The conversion had a few other glitches, but nothing major. One was
that we had used in a couple of places the syntax if (f.empty())
,
which works for boost::function
but not for the std
version.
Moving to if (!f)
, which works for both, fixed the problem.
Another was that, if a method m
if a class C
is overloaded,
boost::bind(&C::m, this, _1)
will select the correct overload but
std::bind
won’t—at least in the implementation shipping with
my compiler. To fix this, I had to specify the overload explicitly,
as in:
typedef Real(C::*T)(Real) const;
... ext::bind(T(&C::m), this, _1) ...
In a particularly hairy case involving the template class
boost::math::pdf
, I couldn’t get std::bind
to work and I had to
write a function object instead. I don’t complain, though, as I feel
that this improved readability; the code went from
ext::bind(std::multiplies<Real>(),
ext::bind(&CLVModelPayoff::operator(), &clvModelPayoff, _1),
ext::bind<Real>(boost::math::pdf<chi_squared_type, Real>,
dist, _1) )
to
class integrand {
...
Real operator()(Real x) const {
return payoff(x) * boost::math::pdf(dist, x);
}
};
Next difference: the std
implementation of bind
also specifies an
inner namespace std::placeholders
to contain the placeholders _1
,
_2
and so on, while in the Boost implementation the placeholders are
in the same namespace as bind
. Both can be accommodated by adding a
namespace ext::placeholders
that contains _1
etc. in the std
case and that remains empty in the boost
case. Writing
using namespace ext::placeholders;
...
ext::bind(f, _1);
works in both cases.
Also, once everything was working, I went the extra mile and removed a
couple of uses of Boost.Lambda. On the one hand, it used _1
for a
different object, which I felt to be confusing; and on the other hand,
removing them goes in the direction of eventually phasing out most
Boost classes in favor of C++11 features.
Finally, we need to make sure that explicit references to
boost::function
, or other code not compatible with both
implementations, won’t come crawling back in the library. This is
checked by The same daily Travis CI run that checks that our C++11 code
stays integrable with the newest changes on the master branch; it uses
the std
classes, and should fail as soon as old syntax enters the
build.
—
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 or LinkedIn if you want to be notified of new posts, or subscribe via RSS: the buttons for 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.