Boost logo

Boost :

From: Joachim Faulhaber (afojgo_at_[hidden])
Date: 2008-05-26 17:59:56


Hi!

My first goal in boostifying my proposed interval template library
(ITL http://sourceforge.net/projects/itl) was to provide good
examples for interval_sets and interval_maps using boost::date_time
objects. This was much harder than I had expected, because
incidentally the most important syntactical and semantical
requirements that I do rely on for a parameter T of itl::interval<T>
are not provided by boost::date_time.

The one property of boost::date_time that knocked my socks off was the
default constructor that 'is-not-a-date-time'. I paraphrase this as:
boost::time is lacking a big bang ;-)

Default constructors in nearly all the types that I've seen in my
lifetime (I know the realm of my unknown unknowns is vast ;-) are
intended to be elements of the type they do construct. Moreover in so
many cases including built in types, default ctors serve as *initial
elements*. Other elements can be *reached* from T() applying
operations of T on them.

This applies to all built in types and many std::types, like e.g.
string, set, list etc.
The most simple instance is unsigned int() with increment operation ++
that resembles the Peano axiomatic operations 0 and successor for
natural numbers.

This semantics of default ctors, at least in common built in and
standard types and also in so many user defined types is so
fundamental that I took it as a *semantical invariant* for the
construction of my library.

In the ITL an empty interval<T> [1,0] for instance, has to be defined
generically:
[ type<T>::unon(), type<T>::neutron() ] where
type<T>::neutron() is implemented via T() : the default ctor and
type<T>::unon() is implemented via ++T() : the default ctor
incremented exactly once.

So, striving to be as generic as possible I tried to set those minimal
requirements that I assumed to be common sense: For discrete types T,
the existence of an initial value T() and In(De)crementation.

Instead of being a big bang of time, boost::date_times default
constructors are cannibals. They give birth to a date_time() object
that they immediately swallow. Then saying schizophrenic things about
themselves like:

cout << to_simple_string(ptime());
"not-a-date-time"

They are singularities having an infinite gravitational pull on any
operators. You can apply as many of them on the constructed black
whole and they are never seen again having no effect.

In addition to making the expected source of all elements of
boost::date_time an awesome creature to be avoided, the universal c++
way of saying the next object within minimal distance '++' has also
been taken away from me. May be with the good intension to prevent me
from doing silly things like iterating over whole centuries in
nanoseconds ;-)

Yet all my applications of ++ and -- in my generic code use them only
once like in comparing intervals of discrete types
(a, b] == [c, d] is true, if(++a==c && b == d)
that have different borders. I only have to make sure here that the
minimal distance is proceeded only once.

To summarize: What I have been relying on in my design, an initial
default ctor T() and an increment operator ++ on the least availabel
distance unit is exactly not provided by boost::date_time.

Now the good news: Both libraries are so well designed that these
seemingly contrarian designs can still be coupled with relatively
small changes. Specifically, which is crucial for me, I can adapt my
libraries code so that I can use all of boost::date_time in it's
current form without patching anything (which would also work as I
wrote in my last posting).

The seemingly most decoupled remedy was, to implement the the missing
functions type<T>::neutron() as well as increment ++ and decrement --
operators using the public interface of boost::date_time.

This had to be done for every boost::date_time type like e.g. ptime:

itl/ptime_adapter.hpp:
-------------------------------------------------------------
boost::posix_time::ptime operator ++(boost::posix_time::ptime& x)
{ return x += boost::posix_time::ptime::time_duration_type::unit(); }

boost::posix_time::ptime operator --(boost::posix_time::ptime& x)
{ return x -= boost::posix_time::ptime::time_duration_type::unit(); }

template<>
inline boost::posix_time::ptime type<boost::posix_time::ptime>::neutron()
{ return boost::posix_time::ptime(boost::posix_time::min_date_time); }
-------------------------------------------------------------

Drawbacks of this are:
(1) Designs are not completely decoupled, for every boost::a_date_time
    the matching adapterfunctions must be included. This is a complication
    for the usage.
(2) *Every* generic library that relies on the existence of an initial
     default ctor and incrementation has to provide an adapter.
(3) Every adapter has to be maintained, when boost::date_time is changed.
    (Kind of a distributed responsibility that is error prone)

I would suggest, that a type that in it's basic characteristics
resembles an intergral numeric type like boosts date_time types, that
type should follow the concept Integral.

cheers
Joachim

Interval Template Library
download from http://sourceforge.net/projects/itl
documentation http://www.herold-faulhaber.de/


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk