Greetings.

This week, the last design pattern covered in Implementing QuantLib: the Visitor pattern. Also, a couple of news items. The first is that this past Monday I have released QuantLib 1.11; you can go and download it—after you’re done with this post, hopefully. Some release notes are available, as well as the list of issues and pull requests for this version.

Second bit of news: Hacktoberfest 2017 is underway, and it’s your occasion to contribute to QuantLib (or any other open-source project on GitHub) and get a t-shirt for your effort. It looks like a good deal to me; then again, as you can see, I’m partial to t-shirts myself.

The Visitor pattern

Our implementation, shown in the next listing, follows the Acyclic Visitor pattern [1] rather than the one in the Gang of Four book [2]: we defined a degenerate AcyclicVisitor class, to be used in the interfaces, and a class template Visitor which defines the pure virtual visit method for its template argument.

    class AcyclicVisitor {
      public:
        virtual ~AcyclicVisitor() {}
    };

    template <class T>
    class Visitor {
      public:
        virtual ~Visitor() {}
        virtual void visit(T&) = 0;
    };

The pattern also needs support from any class hierarchy that we want to be visitable; an example is shown in the listing that follows.

    void Event::accept(AcyclicVisitor& v) {
        Visitor<Event>* v1 = dynamic_cast<Visitor<Event>*>(&v);
        if (v1 != 0)
            v1->visit(*this);
        else
            QL_FAIL("not an event visitor");
    }

    void CashFlow::accept(AcyclicVisitor& v) {
        Visitor<CashFlow>* v1 =
            dynamic_cast<Visitor<CashFlow>*>(&v);
        if (v1 != 0)
            v1->visit(*this);
        else
            Event::accept(v);
    }

    void Coupon::accept(AcyclicVisitor& v) {
        Visitor<Coupon>* v1 = dynamic_cast<Visitor<Coupon>*>(&v);
        if (v1 != 0)
            v1->visit(*this);
        else
            CashFlow::accept(v);
    }

Each of the classes in the hierarchy (or at least, those that we want to be specifically visitable) need to define an accept method that takes a reference to AcyclicVisitor. Each of the methods tries to cast the passed visitor to the specific Visitor instantiation for the corresponding class. A successful cast means that the visitor defines a visit method taking this specific class, so we invoke it. A failed cast means that we have to look for an fallback. If the class is not the root of the hierarchy (like CashFlow or Coupon in the listing) we can call the base-class implementation of accept, which in turn will try the cast. If we’re at the root, like the Event class, we have no further fallback and we raise an exception. (Another alternative would be to do nothing, but we preferred not to fail silently.)

Finally, a visitor is implemented as the BPSCalculator class shown in this post. It inherits from AcyclicVisitor, so that it can be passed to the various accept methods, as well as from an instantiation of the Visitor template for each class for which it will provide a visit method. It will be passed to the accept method of some instance, which will eventually call one of the visit methods or raise an exception.

I already discussed the usefulness of the Visitor pattern in the post I mentioned above, so I refer you to it (the unsurprising summary: it depends). Therefore, I will only spend a couple of words on why we chose Acyclic Visitor.

In short, the Gang-of-Four version of Visitor might be a bit faster, but it’s a lot more intrusive; in particular, every time you add a new class to the visitable hierarchy you’re also forced to go and add the corresponding visit method to each existing visitor (where by “forced” I mean that your code wouldn’t compile if you didn’t). With Acyclic Visitor, you don’t need to do it; the accept method in your new class will fail the cast and fallback to its base class. (In fact, you’re not even required to define an accept method; you could just inherit it. However, this would prevent visitors to target this specific class.) Mind you, this is convenient but not necessarily a good thing (like a lot of things in life, I might add): you should review existing visitors anyway, check whether the fallback implementation makes sense for your class, and add a specific one if it doesn’t. But I think that the disadvantage of not having the compiler warn you is more than balanced by the advantage of not having to write a visit method for each cash-flow class when, as in BPSCalculator, a couple will suffice.

Bibliography

[1] R.C. Martin, Acyclic Visitor. In Pattern Languages of Program Design 3. Addison-Wesley, 1997.

[2] E. Gamma, R. Helm, R. Johnson and J. Vlissides, Design Patterns: Element of Reusable Object-Oriented Software. Addison-Wesley, 1995.

Follow me on Twitter if you want to be notified of new posts, or add me to your Google+ circles, or subscribe via RSS or email: the buttons for that are in the footer. Also, make sure to check my Training page.