Friday, September 21, 2012

C++ Mixins and Curiously Recurring Templates

C++ Curiously Recurring and Mixin's

In large systems it is often desirable to have a policy or rule classes that handle a group of settings for particular instance of a data object. Generally the most common settings are chosen for the base class so that it becomes the "Default" policy. Other Policy's are added by over loading the various part of the Rules class to make new policies such as "DefaultWithA" and "DefaultWithB". But then someone will note the need for a "DefaultWithAandB"

At this point coders either cut and paste. Or try to break the Main rule object into sub-grouping objects "PolicyGroupA" and "PolicyGroupB" and convert the the main Rule into an interface that just aggreatates the tree of sub rules objects. This slowly crags up the rule checking with a series wrapper function calls just to get to the final cluster of rules. Furthermore its often rather difficult to divide these objects in a sane way because they have some form of relationship that caused them to be grouped at the start anyway.

Ruby provides an interesting addition to its language to handle common functionally and code grouping called a mixin. Rubys mixin is just another inheritance trick and once you realize what it is its easy to repeat it in C++. Simply put works out as a near brother of the curiously reoccurring templates. here is how it works;

#include <iostream>

class Default
{
public:
  virtual void ruleA() { std::cout << "default::ruleA\n"; }
  virtual void ruleB() { std::cout << "default::ruleB\n"; }
  virtual void ruleC() { std::cout << "default::ruleC\n"; }
};

class Impl1 : public Default
{
public:
  virtual void ruleC() { std::cout << "Impl1::ruleC\n"; }
};

template <typename T>
class MixinA : public T
{
public:
  virtual void ruleA() { std::cout << "mixin::ruleA\n"; }
};

template <typename T>
class MixinB : public T
{
public:
  virtual void ruleB() { std::cout << "mixin::ruleB\n"; }
};

class Unrelated
{
};

class DefaultWithMixinA : public MixinA<Default >
{
};


class DefaultWithMixinAandB : public MixinB< MixinA< Default > >
{
};

class Impl1WithMixinA : public MixinA<Impl1>
{
};

class UnrelatedWithMixinA : public MixinA<Unrelated>
{
};

int main()
{
  std::cout << "DefaultWithMixinA\n";
  DefaultWithMixinA d;
  d.ruleA();
  d.ruleB();
  d.ruleC();
  
  std::cout << "DefaultWithMixinAandB\n";
  DefaultWithMixinAandB ab;
  ab.ruleA();
  ab.ruleB();
  ab.ruleC();
  
  std::cout << "Impl1WithMixinA\n";
  Impl1WithMixinA i;
  i.ruleA();
  i.ruleB();
  i.ruleC();
  
  std::cout << "UnrelatedWithMixinA\n";
  UnrelatedWithMixinA u;
  u.ruleA();
}

Here is some code so that you can see the difference between this and a real curiously reoccurring template. Pay close attention to how it inherits

#include <iostream>

template <typename T>
struct CuriouslyRecurring
{
public:
  virtual void ruleA()  { std::cout << "CRP::ruleA\n"; }
};

template <typename T>
struct Mixin : public T
{
public:
  virtual void ruleA() { std::cout << "mixin::ruleA\n"; }
};

struct Default
{
public:
  virtual void ruleA() { std::cout << "default::ruleA\n"; }
};

struct DefaultWithMixin : public Mixin< Default >
{

};

struct SelfWithCRT : CuriouslyRecurring< SelfWithCRT >
{
};

// Impossible complier cant tell which ruleA to use when you call it
//struct DefaultWithCRT : public CuriouslyRecurring< DefaultWithCRT >, Default
//{
//};

// Impossible this is a MIXIN cant overload an incomplete class
//struct SelfMixin : public Mixin< SelfMixin >
//{
//};

int main()
{
  SelfWithCRT crt;  
  crt.ruleA();

  DefaultWithMixin mix;  
  mix.ruleA();

  //DefaultWithCRT dcrt;  
  //dcrt.ruleA();
}

No comments:

Post a Comment