Greetings.

This week, back to my series about taking QuantLib to C++11. I’ll tackle std::auto_ptr 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 boost or std (details here).

With some encouragement, though, it can be made to work: I replaced all ext::shared_ptr with std::shared_ptr (which, luckily, is a one-line invocation of sed from the command line), ran the check, and replaced std::shared_ptr and 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 boost::make_shared (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:

    DigitalIborCoupon(
        const ext::shared_ptr<IborCoupon> &underlying,
        Rate callStrike = Null<Rate>(),
        Position::Type callPosition = Position::Long,
        bool isCallATMIncluded = false,
        Rate callDigitalPayoff = Null<Rate>(),
        Rate putStrike = Null<Rate>(),
        Position::Type putPosition = Position::Long,
        bool isPutATMIncluded = false,
        Rate putDigitalPayoff = Null<Rate>(),
        const ext::shared_ptr<DigitalReplication> &replication =
                ext::make_shared<DigitalReplication>());

I didn’t bother finding out what was wrong, and I reverted the changes. Finally, Visual C++ 2012 doesn’t really support variadic templates, and 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

    std::shared_ptr<Base> p = std::shared_ptr<Base>(new Derived);

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 std::auto_ptr unconditionally. 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 well.

This means providing the means to choose between std::auto_ptr and 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:

(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 either to std::auto_ptr or 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,

    class CurveState {
      public:
        ...
        #if defined(QL_USE_STD_UNIQUE_PTR)
        virtual std::unique_ptr<CurveState> clone() const = 0;
        #else
        virtual std::auto_ptr<CurveState> clone() const = 0;
        #endif
        ...
    };

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

    #if defined(QL_USE_STD_UNIQUE_PTR)
    std::unique_ptr<CurveState>
    #else
    std::auto_ptr<CurveState>
    #endif
    CMSwapCurveState::clone() const {
        #if defined(QL_USE_STD_UNIQUE_PTR)
        return std::unique_ptr<CurveState>
        #else
        return std::auto_ptr<CurveState>
        #endif
        (new CMSwapCurveState(*this));
    }

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

  QL_UNIQUE_OR_AUTO_PTR<CurveState> CMSwapCurveState::clone() const {
      return QL_UNIQUE_OR_AUTO_PTR<CurveState>(new CMSwapCurveState(*this));
  }

In a couple of cases, I had to go beyond a simple replacement. One was a class with a constructor (among others) taking a std::auto_ptr and storing it in a data member: something like

    template <class T>
    class Clone {
        std::auto_ptr<T> ptr_;
      public:
        Clone();
        Clone(std::auto_ptr<T> p) : ptr_(p) {}
        // ...other constructors and methods
    };

The alternate version using std::unique_ptr gave us the first foray into proper modern C++, using rvalue references and moves:

    template <class T>
    class Clone {
      public:
        #if defined(QL_USE_STD_UNIQUE_PTR)
        Clone(std::unique_ptr<T>&& p) : ptr_(std::move(p)) {}
        #else
        Clone(std::auto_ptr<T> p) : ptr_(p) {}
        #endif
        // ...other constructors and methods
    };

In the other tricky case, I tried to accommodate both kind of pointers and I ended up with something like this:

    class Bar {
      public:
        std::auto_ptr<Bar> clone();
    };

    int main() {
        Bar b;
        Clone<Bar> f;
        // here be dragons
        f = b.clone();
    }

The final assignment didn’t compile: the copy constructor of std::auto_ptr, which is used to initialize the argument passed to the Clone constructor, takes a non-const reference—the passed pointer has to be modified—and g++ couldn’t bind it to the rvalue returned by 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 takes a Bar instance, as in:

    template <class T>
    class Clone {
        std::auto_ptr<T> ptr_;
      public:
        Clone(const T& t) : ptr_(t.clone().release()) {}
    };

The above works with both std::auto_ptr and std::unique_ptr and 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 auto_ptr and unique_ptr, so be ready to retouch your code manually.

Other features

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 were std::bind1st, std::bind2nd, std::ptr_fun, std::mem_fun and (in one implementation file) std::random_shuffle.

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 boost::bind or 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

    std::transform(v.begin(), v.end(), w.begin(),
                   std::bind2nd(std::minus<Real>(), 1.0));

into something like

    std::transform(v.begin(), v.end(), w.begin(),
                   std::bind(std::minus<Real>(), _1, 1.0));

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

    std::transform(v.begin(), v.end(), w.begin(), subtract(1.0));

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

    std::ptr_fun<Real, Real>(std::exp)

and could be replaced by a cast such as

    static_cast<Real(*)(Real)>(std::exp)

Finally, 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:

    template <class I>
    void randomize(I begin, I end) {
        #if defined(__cplusplus) && __cplusplus >= 201402L
        static std::mt19937 _mt = std::mt19937(std::random_device()());
        std::shuffle(begin, end, _mt);
        #else
        std::random_shuffle(begin, end);
        #endif
    }

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: 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.