NB: This is the original design document for ObjectHandler. The ObjectHandler project is now live and can be found at the following link:

http://www.objecthandler.org/index.html

This QuEP is superseded by the ObjectHandler design document:

http://www.objecthandler.org/design.html

QuEP 11: ObjectHandler

Eric Ehlers

Abstract

QuantLib integration into spreadsheets and other end user tools requires a new standalone ObjectHandler component, a repository allowing objects to be stored, shared, updated, interrogated, and destroyed.

Contents

1. Design
1.1 ObjectHandler
1.1.1 Object
1.1.2 ObjectHandler
1.1.3 boost::any
1.1.4 Property
1.2 Client
1.2.1 Objects
1.2.2 Interface
1.2.3 Application
2. Implementation
2.1 ObjectHandler
2.1.1 Object
2.1.2 ObjectHandler
2.2 Client
2.2.1 Objects
2.2.2 Interface
2.2.3 Application
3. Notes
4. Feedback

1. Design

1.1 ObjectHandler

ObjectHandler maintains a repository of objects, each identified by a unique Handle provided by the client. ObjectHandler facilitates:

1.1.1 Object

Abstract base class Object implements the interface required by Object Hander for managing Objects - constructor, copy constructor, assignment operator, destructor.

Client objects retained in the ObjectHandler repository are represented by classes derived from Object. Typically the constructor of the derived Object class invokes the constructor of the corresponding client class, and the resulting client object is retained in a member variable of the derived Object class. The derived Object class may also wrap member functions of the client class.

The Object class supports two interfaces for the client application to interrogate the stored object:

1.1.2 ObjectHandler

A single global instance of class ObjectHandler implements the interface required by the client to store and retrieve objects in the repository. Global scope is achieved by deriving ObjectHandler from Singleton.

1.1.3 boost::any

ObjectHandler uses Boost class boost::any to represent a scalar, vector, or matrix of native datatypes, Objects, or other boost::anys. Class boost::any serves as a proxy for similar constructs in target client environments (uno::Any in Calc, XLOPER in Excel, etc.)

1.1.4 Property

Object properties are implemented as a vector of template class Property (Daniel Duffy, Financial Instrument Pricing Using C++) which stores attributes as key/value pairs. Use of the Property vector allows derived Objects to maintain their attributes dynamically, while the client interrogates objects through the standard interface in the base class.

1.2 Client

A Client application using ObjectHandler implements the components described below.

1.2.1 Objects

Classes of Client objects to be stored in ObjectHandler are wrapped in classes derived from Object. The derived Object class overrides the base Object class's member functions as appropriate for the corresponding Client class:

1.2.2 Interface

It may be convenient to contain all calls to ObjectHandler in an interface layer, particularly where the Client application is a procedural environment such as a spreadsheet. Typically each function in the interface (e.g. each formula in a spreadsheet) invokes a constructor or member function of an Object class.

The first parameter of each interface function is the Handle that ObjectHandler will use to identify the object which is to be constructed / amended. Wherever a function requires as an input a reference to an Object, the interface accepts a Handle which is used to retrieve from ObjectHandler a reference to the corresponding Object.

Class Object supports interfaces for high- and low-level interrogation. For expediency, the return value of each interface function is the Property vector of the corresponding Object. Rather than interrogate a newly constructed Object, the client application can simply grab the Property vector from the return value of the interface function. For example, in a spreadsheet, a formula array invokes an interface function which instantiates an Object in ObjectHandler, and the return value of the interface function - attributes of the Object - are displayed across a range of cells in the spreadsheet.

1.2.3 Application

The Client application interacts with ObjectHandler via the interface layer.

2. Implementation

Pseudocode is provided for ObjectHandler and for an example Client application. A complete copy of the latest code can be checked out from the QuantLib CVS (module ObjectHandler), or browsed on line.

2.1 ObjectHandler

2.1.1 Object

object.hpp

typedef boost::shared_ptr<boost::any> any_ptr;
typedef Property<string, any_ptr> ObjectProperty;
typedef vector<ObjectProperty> Properties;

class Object {
public:
   // constructor, destructor, copy constructor, assignment operator
   // high-level interrogation
   Properties getProperties();
   // low-level interrogation
   virtual boost::shared_ptr<void> getReference() const = 0;
   // future enhancements
   //Coerce();
   //Load();
   //Unload();
   //Serialize();
   //Deserialize();
   //Compress();
   //Uncompress();
protected:
   Properties properties_;
};

object.cpp

#include "object.hpp"

Properties Object::getProperties() {
   return properties_;
}

2.1.2 ObjectHandler

objecthandler.hpp

#include "object.hpp"

typedef boost::shared_ptr<Object> obj_ptr;
typedef map<string, obj_ptr> ObjectList;

class ObjectHandler {
public:
   // constructor, destructor
   void storeObject(const Handle &handle, const obj_ptr &object);
   obj_ptr retrieveObject(const Handle &handle);
private:
   ObjectList objectList_;   // repository of objects
};

objecthandler.cpp

#include "objecthandler.hpp"

void ObjectHandler::storeObject(const Handle &handle, const obj_ptr &object) {
   objectList_[handle] = object;
}

obj_ptr ObjectHandler::retrieveObject(const Handle &handle) {
   return objectList_[handle];
}

2.2 Client

The native client object which is to be stored in ObjectHandler:

foo.hpp

class Foo {
public:
   Foo(const string &s, const int &i) : s_(s), i_(i) {};
   void update(const string &s, const int &i) {
      s_ = s;
      i_ = i;
   }
   string s() { return s_; };
   int i() { return i_; };
private:
   string s_;
   int i_;
};

2.2.1 Objects

Implementation of the Object corresponding to the Foo class:

objectfoo.hpp

class ObjectFoo : public Object {
public:
   ObjectFoo(const string &s, const int &i);
   virtual boost::shared_ptr<void> getReference() const;
   void update(const string &s, const int &i);
private:
   boost::shared_ptr<Foo> foo_;
};

objectfoo.cpp

ObjectFoo::ObjectFoo(const string &s, const int &i) {
   // construct foo object
   foo_ = boost::shared_ptr(new Foo(s, i));
   // populate base class Property vector
   any_ptr anyString(new boost::any(foo_->s()));
   any_ptr anyInt(new boost::any(foo_->i()));
   ObjectProperty propString(PROPERTY_STR, anyString);
   ObjectProperty propInt(PROPERTY_INT, anyInt);
   properties_.push_back(propString);
   properties_.push_back(propInt);
}

// wrapper for underlying member function
void ObjectFoo::update(const string &s, const int &i) {
   foo_->update(s, i);
   // update Property vector
   *properties_[IDX_STR]() = s;
   *properties_[IDX_INT]() = i;
}

boost::shared_ptr<void> ObjectFoo::getReference() const {
   return boost::static_pointer_cast<void>(foo_);
}

2.2.2 Interface

Wrapper for client calls to ObjectHandler:

interface.hpp

Properties FOO_MAKE(
   const Handle &handle,
   const string &s,
   const int &i);

Properties FOO_UPDATE(
   const Handle &handle,
   const string &s,
   const int &i);

interface.cpp

Properties FOO_MAKE(
      const string &handle,
      const string &s,
      const int &i) {
   obj_ptr object(new ObjectFoo(s, i));
   objectHandler.storeObject(handle, object);
   return object->getProperties();
}

Properties FOO_UPDATE(
   const string &handle,
   const string &s,
   const int &i) {
   boost::shared_ptr<ObjectFoo> object =
      boost::dynamic_pointer_cast<ObjectFoo>
      (objectHandler.retrieveObject(handle));
   if (!object)
      throw Exception("FOO_UPDATE: unable to retrieve object " + handle);
   object->update(s, i);
   return object->getProperties();
}

2.2.3 Application

example.cpp

int main() {
    try {
        cout << "hi" << endl;

        // construct some objects and store them in ObjectHandler
        FOO_MAKE("foo1", "abc", 123);
        FOO_MAKE("foo2", "def", 456);

        // high level interrogation
        cout << endl << "high level interrogation - after constructor" << endl;
        // get object from handler and retrieve its properties -
        // (properties also returned by FOO_MAKE)
        obj_ptr object = objectHandler.retrieveObject("foo1");
        Properties properties = object->getProperties();
        Properties::const_iterator i;
        for (i = properties.begin();
            i != properties.end(); i++) {
            ObjectProperty property = *i;
            any_ptr any = property();
            cout << "property = " << property.name() << "\tvalue = " <<
                AnyToString(any) << endl;
        }

        // update an object
        FOO_UPDATE("foo2", "ghi", 789);

        // low-level interrogation
        cout << endl << "low-level interrogation - after FOO_UPDATE" << endl;
        boost::shared_ptr<ObjectFoo> const objectFoo =
            boost::dynamic_pointer_cast<ObjectFoo>
            (objectHandler.retrieveObject("foo2"));
        boost::shared_ptr<Foo> foo =
            boost::static_pointer_cast<Foo>
            (objectFoo->getReference());
        cout << "value of property s() of underlying foo = "
            << foo->s() << endl;

        cout << endl << "bye" << endl;
    } catch (const exception &e) {
        cout << "Error: " << e.what() << endl;
        return 1;
    }
}

3. Notes

4. Feedback

Feedback on this proposal should be posted to the QuantLib-dev mailing list.