Design
NB: This document is out of date. The ObjectHandler implementation still reflects the high-level ideas in this document but many of the details have changed. For further information please refer to the ObjectHandler source code or send email to quant. lib- users @lis ts.sf .net |
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
- ObjectHandler maintains a repository of objects, each identified by a unique
Handle
provided by the client. ObjectHandler facilitates:- persistence: Objects can be maintained and modified throughout the life of the client application.
- sharing: Each client application instantiates a single global instance of ObjectHandler. Objects created in one area of the application can be accessed in another - for example, in a spreadsheet, one sheet can refer to objects instantiated on another.
1.1.1 Object
- Abstract base class
Object
implements the interface required by ObjectHander for managingObject
s - 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 derivedObject
class invokes the constructor of the corresponding client class, and the resulting client object is retained in a member variable of the derivedObject
class. The derivedObject
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:- high-level access implemented by member functions which allow the client to query the object at run-time with no prior knowledge of its implementation.
-
low-level access implemented by a member function which returns a const reference to the underlying client object stored within the derived
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 derivingObjectHandler
fromSingleton
.
1.1.3 boost::any
- ObjectHandler uses Boost class
boost::any
to represent a scalar, vector, or matrix of native datatypes,Object
s, or otherboost::any
s. Classboost::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 theProperty
vector allows derivedObject
s to maintain their attributes dynamically, while the client interrogates objects through the standard interface in the base class.
1.1.5 Factory
- A class template implements function
makeObject
which instantiates anObject
inObjectHandler
given a classname and handle. FunctionmakeObject
accepts an argument list, implemented asstack < any >
, which is passed to theObject's
constructor. Where the constructor of oneObject
requires access to another existingObject
, the Handle of the existingObject
can be pushed onto the argument stack, allowing the newObject
to retrieve the existing one from theObjectHandler
repository.
1.2 Client
- A Client application using ObjectHandler implements the components described below.
1.2.1 Object
- Classes of Client objects to be stored in ObjectHandler are wrapped in classes derived from
Object
. The derivedObject
class overrides the baseObject
class's member functions as appropriate for the corresponding Client class:-
constructor constructs the corresponding Client object, which is retained as a member variable of the
Object
. - copy constructor, assignment operator, destructor are customized for the particulars of the derived class.
-
member functions call corresponding member functions of the underlying Client object. The state of the Client object stored in the derived
Object
may change accordingly. - high- and low-level interrogation methods are supported appropriately for the derived class.
-
constructor constructs the corresponding Client object, which is retained as a member variable of the
1.2.2 Factory
- The client application makes calls to the Factory function in order to instantiate
Objects
inObjectHandler
. AdditionalObjectHandler
functions allow the client to delete selectedObjects
, or to empty the entire repository.
- Class
Object
supports interfaces for high- and low-level interrogation. For expediency, the return value of functionmakeObject
is the Property vector of the correspondingObject
. Rather than interrogate a newly constructedObject
, the client application can simply grab the Property vector from the return value of the factory function. For example, in a spreadsheet, a formula array invokes the factory function which instantiates anObject
in ObjectHandler, and the return value of the factory function - attributes of theObject
- are displayed across a range of cells in the spreadsheet.
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 interrogationconst Properties &getProperties() const;// low-level interrogationvirtual boost::shared_ptr<void> getReference() const = 0;// future enhancements//Coerce();//Load();//Unload();//Serialize();//Deserialize();//Compress();//Uncompress();protected:Properties properties_;};
- object.cpp
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, destructorvoid storeObject(const Handle &handle, const obj_ptr &object);obj_ptr retrieveObject(const Handle &handle);void deleteObject(const std::string &handle);void deleteAllObjects();std::vector < std::string >getHandles();void dump(std::ostream&);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) {ObjectList::const_iterator result = objectList_.find(handle);if (result!=objectList_.end())return result->second;elsereturn obj_ptr();}
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 Object
- Implementation of the
Object
corresponding to the Foo class:
- objectfoo.hpp
- class ObjectFoo : public Object {public:ObjectFoo(ArgStack &args);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(ArgStack &args) {// get arguments from argument stackint i = Args<int>::popArg(args);std::string s = Args<std::string>::popArg(args);// construct foo objectfoo_ = boost::shared_ptr<Foo>(new Foo(s, i));// populate base class Property vectorany_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 functionvoid 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 Application
- example.cpp
- The syntax of certain calls to ObjectHandler has been simplified with
defines
as documented in file utilities.hpp.int main() {try {// specify log fileOH_LOGFILE("example.log");// also direct log messages to stdoutOH_CONSOLE(1);OH_LOG_MESSAGE("begin example program");// construct some objects and store them in the object handlerArgumentStack foo1Arguments;foo1Arguments.push(string("abc"));foo1Arguments.push(123);Properties foo1Properties =OH_MAKE_OBJECT(ObjectFoo, "foo1", foo1Arguments);ArgumentStack foo2Arguments;foo2Arguments.push(string("def"));foo2Arguments.push(456);Properties foo2Properties =OH_MAKE_OBJECT(ObjectFoo, "foo2", foo2Arguments);// high level interrogationOH_LOG_MESSAGE("high level interrogation - after constructor");OH_LOG_OBJECT("foo2");// update an objectFOO_UPDATE("foo2", "ghi", 789);// high level interrogationOH_LOG_MESSAGE("high level interrogation - after update");OH_LOG_OBJECT("foo2");// low-level interrogationOH_LOG_MESSAGE("low-level interrogation - after FOO_UPDATE");boost::shared_ptr<ObjectFoo> const objectFoo =OH_GET_OBJECT(ObjectFoo, "foo2");boost::shared_ptr<Foo> foo =OH_GET_REFERENCE(Foo, objectFoo);OH_LOG_MESSAGE("value of property s() of underlying foo = "+ foo->s());OH_DELETE_OBJECT("foo2");OH_LOG_MESSAGE("log all objects after deleting foo2:");OH_LOG_ALL_OBJECTS();OH_LOG_MESSAGE("end example program");return 0;} catch (const exception &e) {ostringstream s;s << "Error: " << e.what();OH_LOG_MESSAGE(s.str(), 1);return 1;} catch (...) {OH_LOG_MESSAGE("Error", 1);return 1;}}
3 Proposed Enhancements
3.1 ohMethodValue
Function ohMethodValue would be a generic call to any method of the underlying object e.g. the Excel function might appear as
ohMethodValue(handle, "methodName", parameterArray)
And the corresponding C++ source code would be
returnValue = objectPointer->getObject().methodName(parameterArray)
If returnValue is an object the handle is returned.
Implementation would require some kind of message map which would associate strings with member functions. (Unfortunately this is not a straightforward task in C++ compared to many other programming languages.). The code to initialize this container might be generated by srcgen based on metadata.
4 Feedback
- Feedback on this proposal should be posted to the QuantLib users mailing list.
#define OH_GET_REFERENCE(NAME, ID, OBJECT_CLASS, LIBRARY_CLASS)
Get a shared pointer to the library object referenced by an Object.
Definition: ohdefines.hpp:99
#define OH_GET_OBJECT(NAME, ID, OBJECT_CLASS)
Get a boost shared pointer to a class derived from Object.
Definition: ohdefines.hpp:87
Class Object - Define interface for Objects to be stored in the Repository.