Boost logo

Boost :

From: Gennadiy Rozental (gennadiy_at_[hidden])
Date: 2002-09-09 03:13:09


> This whole discussion was about your suggestion of creating a conversion
> policy and separating it from the rounding policy. So let's come back to
> this subject and please provide some details about the way to do it.

You see, as I am trying to explain, IMO problem lays in original design on
Rounding policy, which is too bulky and inconvenient to update. That is why
you were struggle adding anything new. My point was and is that once you
segregate Rounding policy interface on several parts "layers" it would be
much more easy to add something new. You current internal separation on
layers seems to be a good fit. I was just proposing to bring it close to the
light. On the other hand I agree with you that we do not force namely this
separation in any case. So we keep Rounding policy as one template parameter
of interval with one bulky contract. What we need is one implementation of
the Rounding policy that is based in the segregation mentioned above. And we
already have almost what we need: class rounded_math. It's just not flexible
enough yet. In a review and later I was proposing to change the design of
class rounded_math and others classes involved in this segregation.
    The general idea is to parameterize rounded_math with all other levels
representative and it will be the responsibility of the rounded_math to
combine them properly. IOW interface will look like this:

template<typename RoundingConversion, typename RoundingTransc, typename
RoundingArith, typename RoundingControl>
struct rounded_math ....
{
};

Once we introduce named template parameters (omitted here for simplicity)
changing of any part of rounded_math would look like this:

interval<my_number_type,rounded_math<rounding_control_is<my_fancy_rounding_c
ontrol> > >

Let's now look closer on what should be instead of dots. Since Segregated
part of the interface are dependent by implementation they should be queued
in a single inheritance hierarchy
Note that you a lucky (or smart, since you properly defined segregation)
that we don't have interdependence. In other case it would be more
difficult.
So second line above should look like:
struct rounded_math : RoundingConversion< RoundingTransc< RoundingArith<
RoundingControl > > > >

But this would make our template parameters "template templates". We do not
want this and use the same trick as MPL. IOW our best friend - another level
of indirection
In this case all level implementation would look like this:
struct RoundingConversion
{
    template<typename RoundingTransc>
    struct impl
    {
    };
};

struct RoundingTransc
{
    template<typename RoundingArith>
    struct impl
    {
    };
};

....

Now rounded_math definition would look like:

template<typename RoundingConversion, typename RoundingTransc, typename
RoundingArith, typename RoundingControl>
struct rounded_math : RoundingConversion::template impl<
RoundingTransc::template impl< RoundingArith::template impl< RoundingControl
> > > >
{
};

As you may remarked by now I did not say a word about Number type
specialization. As you see rounded_math does not depend on T. Neither any of
segregated layers.
Note that even in a current state of affair we still would be able to use
explicit specialization to define different, let say rounding arithmetic for
let say type float. But this is not that you want. You want that using of
interval<float> will automatically pickup proper optimization. So we want to
be able to specialize some number type specific behavior for rounded_math
class. As with everything that is type specific we will use notion of
traits. So we introduce generic rounding_math_traits:
template<typename T>
struct rounding_math_traits
{
    typedef ... rounding_control;
    typedef ... rounding_arith;
    typedef ... rounding_transc;
    typedef ... rounding_conversion;
};

and specializations for float, double, long double:

template<>
struct rounding_math_traits<float>
{
    typedef ... rounding_control;
    typedef ... rounding_arith;
    typedef ... rounding_transc;
    typedef ... rounding_conversion;
};

One thing is still missing - we need to add T as a first template argument
into rounded_math to be able to select the traits

Finally rounded_math and segregated layers definition look like this:

struct RoundingConversion
{
    template<typename RoundingTransc>
    struct impl
    {
        ...
    };
};

struct RoundingTransc
{
    template<typename RoundingArith>
    struct impl
    {
        ...
    };
};

...

template<typename T,
               typename RoundingConversion =
rounding_math_traits<T>::rounding_conversion,
               typename RoundingTransc =
rounding_math_traits<T>::rounding_transc,
               typename RoundingArith =
rounding_math_traits<T>::rounding_arith,
               typename RoundingControl =
rounding_math_traits<T>::rounding_control>
struct rounded_math : RoundingConversion::template impl<
                                     RoundingTransc::template impl<
                                           RoundingArith::template impl<
                                               RoundingControl > > > >
{
};

And interval definition will still look like
template<typename T, typename Rounding = rounded_math<T> >
struct interval
{
...
};
Here the corrected version of the rule:

*********************************************************
Unless you are defining traits class never use template parameter in sole
purpose of introducing specialization later on.
*********************************************************

Gennadiy.


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