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.nosp@m.lib-.nosp@m.users.nosp@m.@lis.nosp@m.ts.sf.nosp@m..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
1.1.1 Object
1.1.2 ObjectHandler
1.1.3 boost::any
1.1.4 Property
1.1.5 Factory
1.2 Client
1.2.1 Object
1.2.2 Factory
2 Implementation
2.1 ObjectHandler
2.1.1 Object
2.1.2 ObjectHandler
2.2 Client
2.2.1 Object
2.2.2 Application
3 Proposed Enhancements
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:
  • 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 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:
  • 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 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.1.5 Factory

A class template implements function makeObject which instantiates an Object in ObjectHandler given a classname and handle. Function makeObject accepts an argument list, implemented as stack < any >, which is passed to the Object's constructor. Where the constructor of one Object requires access to another existing Object, the Handle of the existing Object can be pushed onto the argument stack, allowing the new Object to retrieve the existing one from the ObjectHandler 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 derived Object class overrides the base Object 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.

1.2.2 Factory

The client application makes calls to the Factory function in order to instantiate Objects in ObjectHandler. Additional ObjectHandler functions allow the client to delete selected Objects, or to empty the entire repository.
Class Object supports interfaces for high- and low-level interrogation. For expediency, the return value of function makeObject 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 factory function. For example, in a spreadsheet, a formula array invokes the factory function which instantiates an Object in ObjectHandler, and the return value of the factory function - attributes of the Object - 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 interrogation
const Properties &getProperties() const;
// 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"
const Properties &Object::getProperties() const {
return properties_;
}

2.1.2 ObjectHandler

objecthandler.hpp
#include "object.hpp"
typedef boost::shared_ptr<Object> obj_ptr;
typedef map<string, obj_ptr> ObjectList;
public:
// constructor, destructor
void 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;
else
return 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 stack
int i = Args<int>::popArg(args);
std::string s = Args<std::string>::popArg(args);
// construct foo object
foo_ = boost::shared_ptr<Foo>(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 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 file
OH_LOGFILE("example.log");
// also direct log messages to stdout
OH_CONSOLE(1);
OH_LOG_MESSAGE("begin example program");
// construct some objects and store them in the object handler
ArgumentStack 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 interrogation
OH_LOG_MESSAGE("high level interrogation - after constructor");
OH_LOG_OBJECT("foo2");
// update an object
FOO_UPDATE("foo2", "ghi", 789);
// high level interrogation
OH_LOG_MESSAGE("high level interrogation - after update");
OH_LOG_OBJECT("foo2");
// low-level interrogation
OH_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
ObjectHandler.
Definition: coerce.hpp:30
#define OH_LOG_MESSAGE(message)
Log the given message.
Definition: ohdefines.hpp:123
#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.