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 |
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.
ObjectHandler maintains a repository of objects, each identified by a unique Handle provided by the client. ObjectHandler facilitates:
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:
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.
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.)
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.
A Client application using ObjectHandler implements the components described below.
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:
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.
The Client application interacts with ObjectHandler via the interface layer.
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.
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_; };
#include "object.hpp" Properties Object::getProperties() { return properties_; }
#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 };
#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]; }
The native client object which is to be stored in ObjectHandler:
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_; };
Implementation of the Object corresponding to the Foo class:
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::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_); }
Wrapper for client calls to ObjectHandler:
Properties FOO_MAKE( const Handle &handle, const string &s, const int &i); Properties FOO_UPDATE( const Handle &handle, const string &s, const int &i);
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(); }
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; } }
Feedback on this proposal should be posted to the QuantLib-dev mailing list.