Saturday, October 30, 2004

What are Policy Classes?

Policy classes are implementations of punctual design choices. They are inherited from, or contained within, other classes. They provide different strategies under the same syntactic interface. A class using policies is templated having one template parameter for each policy it uses. This allows the user to select the policies needed.1

An example of a policy class is the char_traits class used by the basic_string class


template
<
class charT,
class traits = char_traits<chart>,
class Allocator = allocator<charT>
> class basic_string;


A simple Policy Class example

We would declare some classes that implement the behaviour like so:

struct Hacker
{
void Greeting()
{
cout << "whoa Dude!" << endl;
}
};
struct Boffin
{
void Greeting()
{
cout << "good morning" << endl;
}
};


And construct a host class for them like so:

template< class Behaviour >
class Developer : public Behaviour
{
public:
void Greeting()
{
Behaviour::Greeting();
}
};


This allows us to use it in our code thus;

Developer<Hacker> dev;
dev.Greeting();


How do Policy Classes compare with multiple inheritance?

In order to construct the above classes by using inheritance with the same strategy of policy classes defining behaviour for a host class we need had to make a commitment to an intermediate concept ... the BoffinDeveloper

struct BoffinDeveloper : public Boffin , public Developer {};

This extra step prior to instantiation leads us to have a concrete class which contains our concept prior to actual use. The class BoffinDeveloper is both fixed and has been concieved before use although it may only need to be used once.

We have created a dependency.

So the fundamental difference is : the Developer writing the code has the choice in instantiating a Developer class at the time the code is written as opposed to declaring a class in order to use it.

A Practical Example

This example implements an assert style validation class which allows us to use a kind of typesafe variable arguments.2

The class is structured in a way that uses the c++ operator concatenation technique. In the same way that by returning the class in a '+' or '-' operator allows it to have its operators called a multiple of times we use obtain the same one line multiple argument functionality to simulate typesafe variable arguments.

The actual work of validating our variable is done in the policy class.

struct IsGoodReadPtr
{
template<class T>
void CheckValidity(const T& t)
{
if( ::IsBadReadPtr(t, sizeof(T)))
{
throw std::invalid_argument("bad read pointer");;
}
}
};


The host class provides our operator()

template<class T, class ValidationPolicy >
class Validate : public ValidationPolicy
{
public:
Validate(const T& t)
{
CheckValidity(t);
}

Validate& operator()(const T& t)
{
CheckValidity(t);
return *this;
}
};


In order to obtain the class in the first place we need to return it from a function.

template<class T>
Validate<T, IsGoodReadPtr> ValidateIsGoodReadPtr(const T t)
{
return Validate<T , IsGoodReadPtr>(t);
}


Used in this manner.

class CMyClass{};

void TestValidateIsGoodReadPtr()
{
CMyClass* pMyClass = new CMyClass();
CMyClass* pMyClass1 = new CMyClass();
CMyClass* pMyClass2 = NULL/*new CMyClass()*/;

ValidateIsGoodReadPtr(pMyClass) (pMyClass1) (pMyClass2);
}


References

1. Andrei Alexandrescu Modern C++ Design

2. Herb Sutter comp.lang.c++.moderated posting on typesafe variable arguments

3. Eric Niebler Microsoft Research - Policy Class based Resource Management classes

4. Extending ATL 3.0 Control Containment to Help You Write Real-world Containers

5.
ATL Copy Policy Classes





No comments: