Features


Policy Wrapper

Matvei Brodski

What do you get when you combine policies and wrappers? Yet another surprisingly brilliant, extensible mechanism for developing smart library components.


On May 18, 2001, Andrei Alexandrescu posted a message [1] on comp.lang.c++.moderated proposing “Yet Another Implementation of the Standard Library” that was going to be very efficient and highly configurable. Extensive use of policy classes in library class templates would allow users to “configure STL at compile time for optimization preferences — speed, space, safety, debuggability.” One of the problems Alexandrescu discussed was packaging the per-component configurability. Because you can’t add template parameters to a standard container, you must invent a way to pack the template parameters within the allocator class or some other existing parameter. I wondered whether I could put all the policies in some kind of a wrapper so that they could be represented by one parameter of a class template. This way, if I create a policy-based template, I can add a new policy later without breaking the existing interface.

Using Typelists

My first idea was to use Typelist, a class template created by Alexandrescu [2]. Basically, a Typelist is defined like this:

template < class H, class T >
struct Typelist
{
  typedef H Head;
  typedef T Tail;
}

Alexandrescu also provides a set of useful macros that help linearize the Typelist definition:

#define TYPELIST_1 (T1) \
Typelist<T1, NullType>
#define TYPELIST_2 (T1,T2) \
Typelist<T1, TYPELIST_1(T2) >
#define TYPELIST_3 (T1,T2,T3) \
Typelist<T1, TYPELIST_2(T2,T3) >

and so on. My idea was to pass a set of policies packed in a Typelist. Consider some class template, say FixedSizeArray, that has two policy parameters: a range-checking policy and a threading policy. Listing 1 shows one possible solution that uses Typelists. I used two templates defined by Alexandrescu in the Loki library described in [2]. First:

struct Conversion < class A,
  class B > { ... }

defines an enum value SameType that is 1 if A and B are the same type and 0 otherwise. Second:

struct Select< bool Flag, class A,
  class B > { ... }

defines (through a typedef) a Result type that is equivalent either to A or to B depending on the Flag value. Using these two templates, I created a template:

template < class PolicyList,
           class PolicyID,
           class DefaultPolicy
         >
struct SelectPolicy { ... }

that looks for a class with an internally defined PolicyID type equivalent to a given PolicyID. The template then “returns” the class (by again defining an internal Result type), or, if no class is found, it returns the DefaultPolicy class. If the first template argument happens to be a Typelist< Head,Tail >, the partial specialization template is used. The partial specialization template defers to Conversion to determine if the PolicyID parameter is of the same type as Head::PolicyID. If the types are the same, Result is defined as a synonym of the Head class. Otherwise, SelectPolicy is used recursively on the Tail.

To use this idea, the policy writer has to define PolicyID:

  typedef ParticularPolicyID PolicyID;

in all the classes that implement that particular policy, and so on. As a reward, the user can now instantiate FixedSizeContainer like this:

FixedSizeContainer< int >;

which uses all default policies, or like this:

FixedSizeContainer< double,
  TYPELIST_2( ClassLocking,
    NoCheck ) >

which uses two explicitly supplied policies. Note also that one can put policies in Typelist in any order and omit any of them to use the default policies.

Using Tags

The solution in Listing 1 is limited in that it does not allow the use of template template policies. I wondered if I could pass some tag instead of the policy class itself. I could then use the tag for selecting and instantiating a new policy. Listing 2 illustrates this approach.

Let me explain the differences between Listing 1 and Listing 2 using threading policy as an example. First, Listing 2 does not place PolicyID typedef inside the classes that implement the threading policy (SingleThread, ClassLocking). Instead Listing 2 defines a set of tags (SingleThreadTag, ClassLockingTag) that are publicly derived from the base tag (ThreadingPolicyTag) for this policy. Furthermore, a single class template representing this policy (ThreadingPolicy) is introduced. This single class template has one additional parameter: the tag type. A number of partial specializations for this policy for various types of tags are provided by deriving publicly from corresponding policy implementation classes. In other words, to create a new kind of threading policy (e.g., ObjectLocking), you need to do the following:

  1. Create a class template ObjectLocking that implements the policy:
  2. template < class T >
    struct ObjectLocking
    {
      // implementation
    };
    
  3. Derive ObjectLockingTag from ThreadingPolicyTag:
  4. struct ObjectLockingTag : ThreadingPolicyTag {};
    
  5. Provide partial specialization of the ThreadingPolicy template where the first template parameter is ObjectLockingTag. It should inherit from the ObjectLocking template:
  6. template < class T >
    struct ThreadingPolicy< ObjectLockingTag, T > :
            ObjectLocking< T > {};
    

The SelectPolicyTag class template (which is very much like SelectPolicy) uses the SUPERSUBCLASS macro from Loki to determine if a given policy tag is derived from the base tag type that we are looking for. SelectPolicyTag finds some concrete ThreadingPolicyTag in PolicyTagList and returns it via Result. The tag is then passed on to the ThreadingPolicy template as a first template parameter. As a result, MyThreadingPolicy is typedefed to the correct specialization of ThreadingPolicy.

Listing 2 allows you to use template template parameters. Other advantages include listing any policies in any order and skippping any of them in the Typelist. If you skip a policy, the default policies are used. Listing 2 also allows you to add new policies to your templates with minimal changes to the code that uses the templates.

Notes

[1] “YASTLI — Yet Another STL Implementation — any interest?” <http://groups.google.com/groups?hl=en&safe=off&th=32731af79c219247,63&start=0>.

[2] Andrei Alexandrescu. Modern C++ Design (Addison-Wesley, 2001).

Matvei Brodski is an associate director at Bear, Stearns & Co. Inc. and can be reached at mbrodski@bear.com.