Policy based design with C++11 variadic template parameters
If you read the book "Modern c++ design" by Andrei Alexandrescu you should already know about policy based design, and you can jump directly to the second section (The problem).
If you have not, i will make a super quick introduction here, but you can find more at
http://en.wikipedia.org/wiki/Policy-based_design .
A policy is basically "one way to make something". If you write a class and you want to be able to customize some of its behaviour, you can isolate the customizable part into one or more policy classes and make your class derive from them.
Suppose you have this little class:
class C{
const int array_size=5;
int a[array_size];
public:
int read(int i) {return a[i];}
};
You may want to enable some type of bound checking while debugging the program, but disable it when you are releasing, in order to save those few cpu cycles.
One elegant way to do this is to separate the bound checking behaviour from the rest of the class, something like this:
class CheckedBoundsPolicy{
public:
int check(int i,int array_size){
if (i<0 || i>=array_size)
throw OutOfBoundException;
return i;
}
};
class UncheckedBoundsPolicy{
public:
int check(int i,int array_size){
return i;
}
};
template<class BoundsCheckingPolicy>
class C: public BoundsCheckingPolicy {
const int array_size=5;
int a[array_size];
public:
int read(int i) {
return a[BoundsCheckingPolicy::check(i,array_size)];
}
};
In this way you can select the behaviour of the class instantiating it:
C<UncheckedBoundsPolicy> c;
One useful tecnique for defining powerful PolicyClasses is the CRTP (
http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern).
Basically you pass the derived class to the policy class as a template argument, and the policy class can then use all the fields and types defined in the derived class. In our example we could do:
template<class Concrete>
class CheckedBoundsPolicy{
public:
int check(int i){
if (i<0 || i>=((Concrete*)this)->array_size)
throw OutOfBoundException;
return i;
}
};
template<class Concrete>
class UncheckedBoundsPolicy{
public:
int check(int i){
return i;
}
};
template<template <class> class BoundsCheckingPolicy>
class C: public BoundsCheckingPolicy<C> {
const int array_size=5;
int a[array_size];
public:
int read(int i) {
return a[BoundsCheckingPolicy<C>::check(i)];
}
};
I like to call Concrete the final class to underline the fact that a resulting instance will be of that type.
Note that thanks to multiple inheritance you can also define many policies for a single class.
The Problem
Each policy class can also add its own fields to the concrete class, but this possibility leads to a little problem.
Suppose a class C derive from one of 2 policies P1 and P2 like this:
class P1
{
Object& obj;
public:
P1(Object& _obj): obj(_obj) {};
void f(){... obj ... };
};
class P2
{
public:
void f(){...}:
};
template<class P>
class C : public P
{
C(?????): ??????????????
};
Suddenly we are unable to write a costructor for C which will work with both policies. If we use
C(Object& obj):P(obj) {};
the policy P2 becomes incompatible, because it doesn't have a constructor which accept a reference to Object. On the other hand, if we don't write a constructor, or don't initialize P, the policy P1 becomes incompatible, because it doesn't have a default constructor. Even if we wanted to write one we couldn't, because we need that reference to Object at construction time.
IMO this was a big limit, and until now i used the workaround of giving the same constructor set to all the policies of the same group, in our case by adding a constructor to P2 which would ignore the unused arguments:
P2(Object& ) {}; // just ignore it
.
This is absolutely ugly, and if the two policies have different members which needs initialization it becomes even more ugly from the point of view of the usability of the final class. You can select the behaviour you want, but you must carry all the baggage coming from all the policy members.
The solution (or what's changed with c++11 ?)
By experimenting whit the c++11 variadic template arguments i got the idea to use them inside the constructor of the final (concrete) class. In that way i could call the constructor with exactly the needed number and types of arguments needed by the selected policy/ies, without knowing them beforehand. Writing such constructor is really simple, in our example case:
template<typename... Args>
C(Args... args):P(args...) {};
This work just perfectly if your class derives from exactly one policy, but what if you have more? Suppose our class was defined like this:
template<class P,class Q>
class C: public P, public Q
{
public:
template<typename... Args>
C(Args... args):P(args...), Q(???) {};
};
C++11 doesn't give us any tool to split an "argument pack", the only tool is the extraction of a certain number of types from the head of the list.
We could think of:
...
C(Object& obj,Args... args):P(obj),Q(args...) {} // don't work
But this has the same problem as before: we cannot know if P constructor have 1 or zero arguments.
So we can only use an argument pack if we derive from a single policy, but who says that policy cannot derive from another policy?
So i decided to try this approach: i will build a deep hierarchy of policies, and have them all extract the arguments they need from the pack and pass along the rest of the pack.
So here is my creation:
/////////////////////////////////////////////
// Generic part //
/////////////////////////////////////////////
template<class Concrete,class Mother,class Policy>
struct PolicyHolder{};
struct DeepHierarchyFinal{};
template<class Concrete,class P,typename... Args>
struct DeepHierarchy: public PolicyHolder<Concrete,DeepHierarchy<Concrete,Args...>,P>,
public P
{
template<typename... ConstructorArgs>
DeepHierarchy(ConstructorArgs... cargs):
PolicyHolder<Concrete,DeepHierarchy<Concrete,Args...>,P>(cargs...){};
};
template<class Concrete,class P>
struct DeepHierarchy<Concrete,P>: public PolicyHolder<Concrete,DeepHierarchyFinal,P>,
public P
{
template<typename... ConstructorArgs>
DeepHierarchy(ConstructorArgs... cargs):
PolicyHolder<Concrete,DeepHierarchyFinal,P>(cargs...){};
};
///////////////////////////////////////////
// Test case //
///////////////////////////////////////////
///////////////////////////////////////////
// Policies //
///////////////////////////////////////////
struct Policy1{};
struct Policy2{};
struct Policy3{};
struct Policy4{};
template<class Concrete,class Mother>
struct PolicyHolder<Concrete,Mother,Policy1> : public Mother
{
int x;
template<typename... Args>
PolicyHolder(int _x,Args... args):Mother(args...),x(_x) {};
};
template<class Concrete,class Mother>
struct PolicyHolder<Concrete,Mother,Policy2> : public Mother
{
template<typename... Args>
PolicyHolder(Args... args):Mother(args...){
cout<<"Policy2 initialized";
// Here is a way to know (at runtime) if a particular
// policy has been selected in the concrete class
if (boost::is_convertible<Concrete,Policy3>::value)
cout<<" together with Policy3\n";
else
cout<<" without Policy3\n";
};
};
template<class Concrete,class Mother>
struct PolicyHolder<Concrete,Mother,Policy3> : public Mother
{
string s;
char c;
template<typename... Args>
PolicyHolder(string _s,char _c,Args... args):Mother(args...), s(_s),c(_c) {};
};
template<class Concrete,class Mother>
struct PolicyHolder<Concrete,Mother,Policy4> : public Mother
{
template<typename... Args>
PolicyHolder(Args... args):Mother(args...) {
// Here is a way to check (at compile time) that 2 incompatible policies
// does not coexist
BOOST_STATIC_ASSERT(( ! boost::is_convertible<Concrete,Policy1>::value));
};
};
//////////////////////////////////////////////
// Concrete class //
//////////////////////////////////////////////
template<class... PoliciesPack>
struct C: public DeepHierarchy<C<PoliciesPack...>,PoliciesPack...>
{
using Policies=DeepHierarchy<C<PoliciesPack...>,PoliciesPack...>;
string s;
template<typename... Args>
C(string _s,Args... args):Policies(args...),s(_s){};
};
BOOST_AUTO_TEST_CASE( testDeepHierarchyConstruction )
{
C<Policy1,Policy2> c0("foo",4);
BOOST_CHECK_EQUAL(c0.x,4);
C<Policy1,Policy2,Policy3> c1("bar",3,"foo",'f');
BOOST_CHECK_EQUAL(c1.c,'f');
C<Policy3,Policy4> c2("rab","oof",'d');
BOOST_CHECK_EQUAL(c2.c,'d');
}
If you have any idea how to make this more usable please leave a comment. I have not tested this design in a real world program yet, so there may be some issues, but i'm sure that this test case do compile and works (i'm using clang in c++11 mode and the boost libraries);