|
Boost : |
Subject: [boost] [design] policy based. Was: [XInt] Review
From: Joachim Faulhaber (afojgo_at_[hidden])
Date: 2011-03-10 17:06:46
2011/3/10 Chad Nelson <chad.thecomfychair_at_[hidden]>:
> On Wed, 9 Mar 2011 19:11:31 +0100
> Joachim Faulhaber <afojgo_at_[hidden]> wrote:
>
>> This is my review on the XInt library. [...]
>
> Thank you for the review, and the comments.
Thank you for your replies and questions
[...]
>> 2.3 Policy based design
>> =======================
>>
>> I think policy based design is a very good and powerful technique that
>> is specifically useful to separate implementation variants of class
>> templates in clear and flexible ways. Unfortunately I can not see that
>> you are actually using policy based design here. Because your design
>> lacks typical policy classes that encapsulate implementation variants
>> in a way that reduces codereplication and enhances flexibility. [...]
>
> As I'm not sure what kind of implementation you're thinking of, I can't
> respond to this. So far as I can tell, my design is doing what you
> describe for policy-based design.
>
>> There is a great deal of code replication in the current
>> implementation of xint::integer_t. [...] This skeleton (and some
>> variations of it) occurs over and over again only to propagate the
>> call of the function to the implementing class at positions #1 and #2.
>
> integer_t handles some options, and passes the data to the implementing
> class for others. It was the best solution I could find to handle all
> options.
Trying to be consistent with my own request ...
>> "At the same time I'd like to call on the boost community to support
>> Chad and to see an Integer library as an opportunity to develop more
>> common understanding how a state of the art generic design looks like
>> in this specific case."
... I invested some work to make my suggestion about good design more
concrete. I will give you a small example of a policy based design
that is directly related to some problems of code replication and
insufficient separation of concerns in your library. At the same time
I hope it'll help to understand policy based design, which includes
getting a better understanding for myself.
The only source of PDB that I've been using so far is Andrei's book from 2001.
[Andrei Alexandrescu 2001, Moden C++ Design]
But I found the page on wikipedia informative as well.
http://en.wikipedia.org/wiki/Policy-based_design
Maybe others can contribute more good sources, I did not research.
Andrei's book has been really enlightening for me. I've never read a
book about programming with so much delight. I can highly recommend
it.
As for the example. I picked one aspect of your design, the option for
nothrow, that controls the handling of nan. Here I've criticized code
replication in your code like
if (Nothrow) {if (b.is_nan()) ... if (!is_nan()) { ... }} else {...}
that reoccurs a lot in the implementation of integer_t.
Using policy based design, we try to encapsulate all aspects of the
code that relates to the handling of nans in a policy class. So we try
to separate the concern "handling nan" from other parts of a class. In
this case aint (not xint ;)
//=========================================================
// Using policies, we want to handle nan in a "nany" or
// a "manly" way (manly: ignore it all together)
struct nany; // Takes care for the nasty nan
struct manly; // No, we don't want this
template<class, class>
class aint; // a int with nan policy
// Meta function for naniness.
template<class Type> struct is_nany
{ static const bool value = false; };
template<class Rep> // a int with nany policy is_nany
struct is_nany<aint<Rep, nany> >
{ static const bool value = true; };
template<class Type>
bool is_nan(Type const& value)
{ // Not perfect ... ok. for the example
return value
==( numeric_limits<Type>::has_quiet_NaN
? (numeric_limits<Type>::quiet_NaN)()
: (numeric_limits<Type>::max)() );
}
// An int class with NaNPolicy: Rep is supposed to be some
// integer implementation, including limitless integers
// NaNPolicy encapsulates nan-handling.
template<class Rep, class NaNPolicy = manly>
class aint // no sunshine when she's gone
{
public:
aint(): _value(){}
aint(Rep const& val): _value(val){}
aint& operator += (aint const& y)
{
NaNPolicy::plus_assign(_value, y._value);
return *this;
}
Rep value(){ return _value; }
private:
Rep _value;
};
// Finally two different NaN-policy classes
struct nany
{
template<class Rep>
inline static void plus_assign(Rep& x, Rep const& y)
{
if (is_nan(x)) ;
else if(is_nan(y)) x = y;
else x += y;
}
};
struct manly
{
template<class Rep>
inline static void plus_assign(Rep& x, Rep const& y)
{
x += y;
}
};
BOOST_AUTO_TEST_CASE(nan_policy_study)
{
// No NaN protection
aint<long> ago;
// A no throw int with NaN protection
aint<short, nany> sun(2);
aint<short, nany> shine(7);
sun += shine;
cout << sun.value() << endl;
// I is generic because it is suitable for all kinds of
// integer types Rep including say xint::integer_t types
aint<xint::integer, nany> ax_i;
}
//=========================================================
1. Encapsulation: The code necessary for the handling of nans
is either encapsulated in the policy class, or it uses some
meta code that is globally available.
2. Non intrusive: This code does not require changes in the
class Rep, that implements the basic semantics.
3. Generic: So, apart from very little code around the
existence and test of the NaN-element, the policy based
design works for all integer implementations Rep that
are model of a common integer concept, which is implicitly
given in this case.
4. Abstraction: Moreover separation of concerns leads to
abstraction here. We discover that this kind of "nan closure"
not only works for integers but also for floting point
numbers or continuous numbers like e.g. boost::rational.
Regards,
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