Why did QuantLib introduce the Handle class?
Asked Answered
D

1

9

I check this slides but still didn't get :

1) what problem does Handle sovled?

2) what is the benefit to add the Handle class?

From its source code, I cannot get any clue either:

   template <class Type>
    class Handle {
      protected:
        class Link : public Observable, public Observer {
          public:
            explicit Link(const shared_ptr<Type>& h =
                                         shared_ptr<Type>());
            void linkTo(const shared_ptr<Type>&);
            bool empty() const;
            void update() { notifyObservers(); }
          private:
            shared_ptr<Type> h_;
        };
        boost::shared_ptr<Link<Type> > link_;
      public:
        explicit Handle(const shared_ptr<Type>& h =
                                         shared_ptr<Type>());
        const shared_ptr<Type>& operator->() const;
        const shared_ptr<Type>& operator*() const;
        bool empty() const;
        operator boost::shared_ptr<Observable>() const;
    };

    template <class Type>
    class RelinkableHandle : public Handle<Type> {
      public:
        explicit RelinkableHandle(const shared_ptr<Type>& h =
                                         shared_ptr<Type>());
        void linkTo(const boost::shared_ptr<Type>&);
    };

Could someone give a better example?

Thanks.

Detraction answered 19/3, 2017 at 17:44 Comment(0)
L
10

The short answer: the Handle class is a smart pointer to pointer.

What is it good for? Take, for example, an instance of an interest-rate index such as Euribor6M. Right now, its constructor takes a handle to a yield term structure from which it forecasts its future fixings. What would change if we used a shared_ptr instead?

Let's look at a use case. Warning: I'm simplifying things to avoid writing too much code, but it's a legitimate use case we have. Let's say we initialized the index with a flat curve to begin with:

shared_ptr<SimpleQuote> r = make_shared<SimpleQuote>(0.01);
shared_ptr<YieldTermStructure> curve =
    make_shared<FlatForward>(today, r, Actual360());

shared_ptr<InterestRateIndex> index = make_shared<Euribor6M>(curve);

(in a real use case, curve would be an Euribor curve bootstrapped over a set of quoted rates). The constructor of index takes a copy of the passed shared_ptr<YieldTermStructure> and makes a copy of it to store as a data member. After it's built, we'll pass the index to other instruments (swaps, floating-rate bonds, whatever).

In case the interest rate changed, and given we still have a hold of the r quote, our client code can write

r->setValue(0.015);

Since r and curve are shared pointers, this will also change the rate in the copy of the curve inside the index (because both curve and its copy inside index point to the same object). As a result, the index fixings and the depending instrument values will also change.

However, let's say we want to start using another curve. In this case, we might want to switch to an interpolated curve instead of a flat one:

vector<Date> dates = ... ;
vector<Rate> rates = ... ;
shared_ptr<YieldTermStructure> curve2 =
    make_shared<ZeroCurve>(dates, rates, Actual360());

(in a real case, we might want to bootstrap the curve on a different set of quotes or to use another way to model rates).

How can we tell index that it should start using curve2? Using shared_ptr, we can't. Even if we say:

curve = curve2;

this will cause curve to point to the interpolated curve, but the copy of curve inside index will keep pointing to the old one. This is not a problem with shared_ptr, but with pointers in general. How do you solve it? By adding another layer of indirection. If you were using raw pointers, you'd start using pointers to pointers. In our code, Handle does the same thing: it "points" to a shared_ptr, and is implemented so that two copies of the same handle point to the same shared_ptr. This way, if you write:

shared_ptr<SimpleQuote> r = make_shared<SimpleQuote>(0.01);
shared_ptr<YieldTermStructure> curve =
    make_shared<FlatForward>(today, r, Actual360());
RelinkableHandle<YieldTermStructure> h(curve);

shared_ptr<InterestRateIndex> index = make_shared<Euribor6M>(h);

you can later write:

h.linkTo(curve2);

and both the handle you hold and its copy inside index will point to the new curve.

As for the difference between RelinkableHandle and Handle: you can only call linkTo on the former. The idea is that you instantiate a RelinkableHandle, pass around copies as just Handle, and so you ensure that nobody but you can change what it points to (using const wouldn't work, since constness can be cast away by a simple copy).

Levity answered 20/3, 2017 at 15:16 Comment(3)
Thank you very much for your clarification, Luigi!Detraction
Thanks for your clarification. One more question on this. What does the class Link do and how should I think about the shared pointer h passed into link's constructor.Haggle
The shared_ptr inside the link is the object currently pointed. The Link class adds observability on top of it so that you get notifications when the pointed object changes.Levity

© 2022 - 2024 — McMap. All rights reserved.