Boost logo

Boost :

From: Spencer Collyer (spencer_at_[hidden])
Date: 2006-02-01 08:09:57


Whilst working on a policy-based sparse-array class (which I hope to
submit to Boost at some point), I had the problem that there did not seem
to be any sensible way to organise the policies such that sensible
defaults could be coded in the template. Rather than always forcing the
users to specify the earlier policies, even when the default would be
fine, simply because they wanted to use a different policy for one of the
later parameters, I came up with the following little utility.

===============================================================
#include <boost/mpl/find_if.hpp>
#include <boost/mpl/deref.hpp>
#include <boost/type_traits/is_base_of.hpp>

using namespace boost;
using namespace boost::mpl;

template<typename Base, typename Types, typename DefType>
class PolicySelector
{
private:
    typedef typename find_if< Types, is_base_of<Base,_1> >::type pos;
    typedef typename end<Types>::type end_pos;
public:
    typedef typename
    if_< is_same<pos, end_pos>,
         DefType,
         typename deref<pos>::type
>::type type;
};
===============================================================

Basically, you pass a boost::mpl sequence in the Types argument (I
usually use a mpl::vector), along with the base class for the policy
classes and a default class to use if none is found otherwise, and the
'type' member gives the selected policy.

The only restriction it imposes is that all classes implementing a
particular policy type have to inherit from a single base class.

A (slightly contrived :-) ) example to show how this is used:

===============================================================
#include <boost/mpl/find_if.hpp>
#include <boost/mpl/deref.hpp>
#include <boost/type_traits/is_base_of.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/placeholders.hpp>
#include <iostream>

using namespace boost;
using namespace boost::mpl;

template<typename Base, typename Types, typename DefType>
class PolicySelector
{
private:
    typedef typename find_if< Types, is_base_of<Base,_1> >::type pos;
    typedef typename end<Types>::type end_pos;
public:
    typedef typename
    if_< is_same<pos, end_pos>,
         DefType,
         typename deref<pos>::type
>::type type;
};

class a_base { };

class a1 : public a_base { };

class a2 : public a_base { };

class a3 : public a1 { };

class b_base { };

class b1 : public b_base { };

class b2 : public b_base { };

class b3 : public b_base { };

// Select the 'A' policy. Default is a3.
template<typename Types>
class A_Selector
{
public:
    typedef typename PolicySelector< a_base
    , typename Types::type
    , a3>::type type;
};

// Select the 'B' policy. Default is b2.
template<typename Types>
class B_Selector
{
public:
    typedef typename PolicySelector< b_base
    , typename Types::type
    , b2>::type type;
};

// Utility class to ease building a mpl::vector.
template<class p1=na, class p2=na>
class sel_types
{
public:
    typedef typename vector<p1,p2>::type type;
};

// Test class template.
template<class Policies>
class testit
: public A_Selector<Policies>::type
, public B_Selector<Policies>::type
{
public:
    typedef typename A_Selector<Policies>::type APolicy;
    typedef typename B_Selector<Policies>::type BPolicy;
};

int main()
{
    // Test policies listed in 'normal' order
    typedef testit<sel_types<a1, b1> > types1;
    typedef types1::APolicy A1;
    typedef types1::BPolicy B1;
    A1 aa1;
    std::cout << "Got " << typeid(aa1).name() << "\n";
    std::cout << "Expected " << typeid(a1).name() << "\n";
    B1 bb1;
    std::cout << "Got " << typeid(bb1).name() << "\n";
    std::cout << "Expected " << typeid(b1).name() << "\n";

    // Test missing 'B' policy - should use default
    typedef testit<sel_types<a2> > types2;
    typedef types1::APolicy A2;
    typedef types1::BPolicy B2;
    A2 aa2;
    std::cout << "Got " << typeid(aa2).name() << "\n";
    std::cout << "Expected " << typeid(a2).name() << "\n";
    B2 bb2;
    std::cout << "Got " << typeid(bb2).name() << "\n";
    std::cout << "Expected " << typeid(b2).name() << "\n";

    // Test missing 'A' policy - should use default
    typedef testit<sel_types<b1> > types3;
    typedef types1::APolicy A3;
    typedef types1::BPolicy B3;
    A3 aa3;
    std::cout << "Got " << typeid(aa3).name() << "\n";
    std::cout << "Expected " << typeid(a3).name() << "\n";
    B3 bb3;
    std::cout << "Got " << typeid(bb3).name() << "\n";
    std::cout << "Expected " << typeid(b1).name() << "\n";

    // Test policies in 'reversed' order.
    typedef testit<sel_types<b1, a1> > types4;
    typedef types1::APolicy A4;
    typedef types1::BPolicy B4;
    A4 aa4;
    std::cout << "Got " << typeid(aa4).name() << "\n";
    std::cout << "Expected " << typeid(a1).name() << "\n";
    B4 bb4;
    std::cout << "Got " << typeid(bb4).name() << "\n";
    std::cout << "Expected " << typeid(b1).name() << "\n";
}
===============================================================

As you can see, it doesn't matter which order the policies are specified
in, nor whether they are specified at all.

Would this utility be useful to anyone else? Is it worth my while writing
up some proper documentation and submitting it for review? If so, which
library would it go into?

Spencer Collyer

-- 
12:47pm up 48 days 20:28, 21 users, load average: 0.09, 0.06, 0.13
Registered Linux User #232457 | LFS ID 11703

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