Boost logo

Boost :

Subject: Re: [boost] [design] policy based. Was: [XInt] Review
From: Joachim Faulhaber (afojgo_at_[hidden])
Date: 2011-03-12 16:36:58


2011/3/12 Chad Nelson <chad.thecomfychair_at_[hidden]>:
> On Fri, 11 Mar 2011 10:51:24 +0100
> JCK <jan.kleinsorge.bulk_at_[hidden]> wrote:
>
>>> [...] I think I see what you're getting at. Thank you. I'll have to
>>> think about that further to see the best way to implement it.
>>
>> As a side-note, in that context, providing an implementation
>> (sometime in the future) similar to Microsoft's SafeInt [1] would be
>> an extremely useful addition to this library. This would be more
>> useful than a simple binary fail-state, IMHO.
>
> That would be easy to add, interface-wise. Just a "throw on overflow"
> option, for use with fixed-size integers. I'll put it on the to-do
> list, but I think it'll be quite a while before I get down to it.

With respect to the side note of Jan, I'd like to resume the example
of a policy based design. As we are separating concerns and
encapsulate them into policy classes, we should be able to code a
behavior like the MS SafeInt mentioned by Jan in a policy class.

typedef aint<int, MsSafeIntBehavior> aSafeInt;
typedef aint<xint::integer_t<options::fixedlenth<512> >,
MsSafeIntBehavior> anXSafeInt;

Note, that in this type of design:
(1) The "policy equipped" class (aint) doesn't have to be changed.
(2) The implementing class e.g. xint::integer_t doesn't have to be changed
(3) The author of xint, doesn't need to do anything
(4) The client developer, that implements a new policy class
'MsSafeIntBehavior' can develop a behavior of her choice.

So we have:
(1) Separation of concerns/encapsulation
(2) Extensibility by user code
(3) Abstraction

As already mentioned in the first posting in this thread, we can see
that the policy based design of class aint facilitates to discover,
that a NaNPolicy could be applicable not only to a single int
implementation or a whole set of integer implementations but also to
number implementations in general.

Moreover we may discover, that a NaNPolicy is not the only addition or
"completion", that people many desire for numbers. I'd like to call
those classes "closures", because they try to close or complete the
behavior of a data type in a defined way where is has been undefined
before.

[BTW I don't like those and I am not advocating to use them, but they
are a nice vehicle for some design aspects that we can study here.]

So I generalized this template parameter a little calling it 'class
Closure' now instead of 'class NaNPolicy'. (I suggested this idea
earlier
http://lists.boost.org/Archives/boost/2010/04/164816.php
but didn't make sure it was well received)

On 04/07/2010 05:04 PM, Joachim Faulhaber wrote:
> Maybe it would be more interesting to provide the minimal and "pure"
> numeric type and provide a NaN and Infinity closure as template
>
> template<class IntegralT, class ClosureTraits=...> closed { ... }
>
> because the way in which NaNs, Infinities (and maybe more special
> values) are handled should be the same for all the different
> implementations of integral numeric types.

Another aspect I wanted to develop further is

(4) Reduction of code replication.

In the first example of 'class aint' we have still code replication in
of the form

    template<class Rep>
    inline static void o_assign(Rep& x, Rep const& y)
    {
        if (is_nan(x)) ;
        else if(is_nan(y)) x = y;
        else x o= y; // only 'o' varies
    }

The "skeleton" 'if(is_nan(x)); else if(is_nan(y))...' replicates for
all implementations of e.g. operators o=

Within our design we get rid of it by lifting all operations of
identical signatures like binary op_assign operators: We encapsulate
them in functor classes like

template<class Type> struct add_to
{
    Type& operator()(Type& x, Type const& y)
    { return x += y; }
};

and pass them via template parameters:

    template<class Rep, template<class>class Op>
    inline static void op_assign(Rep& x, Rep const& y)
    {
        if (is_nan(x)) ;
        else if(is_nan(y)) x = y;
        else Op<Rep>()(x, y);
    }

So here is the enhanced example of 'class aint' using policy based design:

//==========================================================
struct nany; // Takes care for the nasty nan
struct manly; // No, we don't want this

template<class, class>
class aint; // a int with closure policies

//----------------------------------------------------------
// Functor classes for op_assign operators to reduce
// code replication.
template<class Type> struct add_to
{
    Type& operator()(Type& x, Type const& y)
    { return x += y; }
};

template<class Type> struct subtract_from
{
    Type& operator()(Type& x, Type const& y)
    { return x -= y; }
};

// ... also |=, *= etc.
//----------------------------------------------------------

// An int class with Closure
template<class Rep, class Closure = manly>
class aint // no sunshine when she's gone
{
public:
    aint(): _value(){}
    aint(Rep const& val): _value(val){}

    aint& operator /= (aint const& y)
    {
        Closure::div_assign(_value, y._value);
        return *this;
    }

    aint& operator += (aint const& y)
    {
        Closure::op_assign<Rep,add_to>(_value, y._value);
        return *this;
    }

    aint& operator -= (aint const& y)
    {
        Closure::op_assign
            <Rep,subtract_from>(_value, y._value);
        return *this;
    }

    Rep value()const
    {
        return _value;
    }

private:
    Rep _value;
};

// Our Closure policy class can also be used in
// free standing functions
template<class CharType, class CharTraits,
         class Rep, class Closure>
std::basic_ostream<CharType, CharTraits>& operator <<
  (std::basic_ostream<CharType, CharTraits> &stream,
   aint<Rep,Closure> const& x)
{
    return Closure::to_stream
           <CharType,CharTraits,Rep>(stream, x.value());
}

struct nany
{
    // Improvement: all the nan stuff now with the
    // nan-Closure class
    template<class Rep>
    inline static Rep nan()
    { // Again not perfect, ... ok for the example
        return
          ( numeric_limits<Rep>::has_quiet_NaN
         ? (numeric_limits<Rep>::quiet_NaN)()
         : (numeric_limits<Rep>::max)() );
    }

    template<class Rep>
    inline static bool is_nan(const Rep& x)
    {
        return x == nan<Rep>();
    }

    template<class Rep>
    inline static void div_assign(Rep& x, Rep const& y)
    {
        if (is_nan(x)) ;
        else if(is_nan(y)) x = y;
        else if(y == 0) x = nan<Rep>();
        else x /= y;
    }

    // Code replication: For all op_assign operators Op
    // we have only one implementation
    template<class Rep, template<class>class Op>
    inline static void op_assign(Rep& x, Rep const& y)
    {
        if (is_nan(x)) ;
        else if(is_nan(y)) x = y;
        else Op<Rep>()(x, y);
    }

    template<class CharType, class CharTraits, class Rep>
    static std::basic_ostream<CharType, CharTraits>&
    to_stream(std::basic_ostream<CharType, CharTraits> &stream,
              Rep const& x)
    {
        if(is_nan(x))
            return stream << "NaN";
        else
            return stream << x;
    }
};

struct manly
{
    template<class Rep>
    inline static void div_assign(Rep& x, Rep const& y)
    {
        x /= y;
    }

    template<class Rep, template<class>class Op>
    inline static void op_assign(Rep& x, Rep const& y)
    {
        Op<Rep>()(x, y);
    }

    template<class CharType, class CharTraits, class Rep>
    static std::basic_ostream<CharType, CharTraits>&
    to_stream(std::basic_ostream<CharType, CharTraits> &stream,
              Rep const& x)
    {
        return stream << x;
    }
};

BOOST_AUTO_TEST_CASE(nan_policy_study)
{
    // No NaN protection
    aint<long> ago;

    //
    aint<short, nany> sun(2);
    aint<short, nany> shine(7);
    sun += shine;
    cout << "2+7 = " << sun << endl;

    shine /= 0;
    cout << "7/0 = " << shine << endl;

}
//==========================================================

I hope this is not too, well, overwhelming and still helpful ;-/

Cheers,
Joachim

-- 
Interval Container Library [Boost.Icl]
http://www.joachim-faulhaber.de

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