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.

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.