Odds and ends: basic types
Hello again.
Today’s post is about the basic types we’re using in the library. Most of its content was prompted by comments made on a previous post; so thank you, Matt.
In last post, I mentioned that I’m looking into publishing Implementing QuantLib as an ebook, but I’m not sure if there’s any interest; please go read the post for details, if you haven’t already, and leave your feedback.
Follow me on Twitter if you want to be notified of new posts, or add me to your circles, or subscribe via RSS: the buttons for that are in the footer. Also, make sure to check my Training page.
Odds and ends: basic types
The library interfaces don’t use built-in types; instead, a number of
typedefs are provided such as Time
, Rate
, Integer
, or
Size
. They are all mapped to basic types (we talked about using
full-featured types, possibly with range checking, but we dumped the
idea). Furthermore, all floating-point types are defined as Real
,
which in turn is defined as double
. This makes it possible to change
all of them consistently by just changing Real
.
In principle, this would allow one to choose the desired level of
accuracy; but to this, the test-suite answers “Fiddlesticks!” since it
shows a few failures when Real
is defined as float
or long
double
. The value of the typedefs is really in making the code more
clear—and in allowing dimensional analysis for those who, like
me, were used to it in a previous life as a physicist; for instance,
expressions such as exp(r)
or r+s*t
can be immediately flagged as
fishy if they are preceded by Rate r
, Spread s
, and Time t
.
Of course, all those fancy types are only aliases to double
and the
compiler doesn’t really distinguish between them. It would nice if
they had stronger typing; so that, for instance, one could overload a
method based on whether it is passed a price or a volatility.
One possibility would be the BOOST_STRONG_TYPEDEF
macro, which is
one of the bazillion utilities provided by Boost. It is used as, say,
and creates a corresponding proper class with appropriate conversions
to and from the underlying type. This would allow overloading methods,
but has the drawbacks that not all conversions are explicit. This
would break backward compatibility and make things generally
awkward. For instance, a simple expression like Time t = 2.0;
wouldn’t compile. You’d also have to write f(Time(1.5))
instead of
just f(1.5)
, even if f
wasn’t overloaded.
Also, the classes defined by the macro overload all operators: you can happily add a time to a rate, even though it doesn’t make sense (yes, dimensional analysis again). It would be nice if the type system prevented this from compiling, while still allowing, for instance, to add a spread to a rate yielding another rate or to multiply a rate by a time yielding a pure number.
How to do this in a generic way, and ideally with no run-time costs, was shown first by Barton and Nackman [1]; a variation of their idea is implemented in the Boost::Units library, and a simpler one was implemented once by yours truly while still working in Physics. (I won’t explain it here, but go look for it. It’s almost insanely cool.) However, that might be overkill here; we don’t have to deal with all possible combinations of length, mass, time and so on.
The ideal compromise for a future library might be to implement wrapper classes (à la Boost strong typedef) and to define explicitly which operators are allowed for which types. As usual, we’re not the first ones to have this problem: the idea has been floating around for a while, and a proposal was put forward [2] to add to the next version of C++ a new feature, called opaque typedefs, which would make it easier to define this kind of types.
A final note: among these types, there is at least one which is not
determined on its own (like Rate
or Time
) but depends on other
types. The volatility of a price and the volatility of a rate have
different dimensions, and thus should have different types. In short,
Volatility
should be a template type.
Bibliography
[1] J. Barton and L. R. Nackman, Dimensional Analysis, C++ Report,
January 1995.
[2] W. E. Brown, Toward Opaque Typedefs for C++1Y,
v2,
C++ Standard Committee Paper N3741, 2013