Mixin-Based Programming In C++
by Ulrich W. Eisenecker, Frank Blinn, and Krzysztof Czarnecki

Listing One
#include <iostream>
using namespace std;

class Customer
{
  public:
    Customer(const char* fn,const char* ln)
      :firstname_(fn),lastname_(ln)
    {}
    void print() const
    {
      cout << firstname_
      << ' '
      << lastname_;
    }
private:
    const char *firstname_,
               *lastname_;
};

template <class Base>
class PhoneContact: public Base
{
  public:
    PhoneContact(const char* fn,const char* ln,const char* pn)
      :Base(fn,ln),phone_(pn)
    {}
    void print() const
    {
      Base::print();
      cout << ' ' << phone_;
    }
  private:
    const char *phone_;
};

template <class Base>
class EmailContact: public Base
{
  public:
    EmailContact(const char* fn,const char* ln,const char* e)
      :Base(fn,ln),email_(e)
    {}
    void print() const
    {
      Base::print();
      cout << ' ' << email_;
   }
private:
      const char *email_;
};

int main()
{
  Customer c1("Teddy","Bear");
  c1.print(); cout << endl;
  PhoneContact<Customer> c2("Rick","Racoon","050-998877");
  c2.print(); cout << endl;
  EmailContact<Customer> c3("Dick","Deer","dick@deer.com");
  c3.print(); cout << endl;
  // The following composition isn't legal because there
  // is no constructor that takes all four arguments.
  // EmailContact<PhoneContact<Customer> >
  // c4("Eddy","Eagle","049-554433","eddy@eagle.org");
  // c4.print(); cout << endl;
  return 0;
}


Listing Two
#include <iostream>
using namespace std;

// Define a new mixin class with a constructor that accepts all arguments.

class Customer
{
  public:
    Customer(const char* fn,const char* ln)
      :firstname_(fn),lastname_(ln)
    {}
    void print() const
    {
      cout << firstname_
      << ' '
      << lastname_;
    }

  private:
    const char *firstname_,
               *lastname_;
};

// The new mixin class will be defined using multiple inheritance. 
// Therefore Base must be turned into a virtual base class.
template <class Base>
class PhoneContact: virtual public Base
{
  public:
    PhoneContact(const char* fn,const char* ln,const char* pn)
      :Base(fn,ln),phone_(pn)
    {}
    void print() const
    {
      Base::print();
      basicprint();
    }

  protected:
    // We need an "inner" print method that prints the PhoneContact-specific
    // information only.
    void basicprint() const
    {
      cout << ' ' << phone_;
    }
  private:
    const char *phone_;
};

// Base has to be declared as virtual base class here, too.
template <class Base>
class EmailContact: virtual public Base
{
  public:
    EmailContact(const char* fn,const char* ln,const char* e)
      :Base(fn,ln),email_(e)
    {}

    void print() const
    {
      Base::print();
      basicprint();
    }
  protected:
    // We need an "inner" print method that prints the EmailContact-specific
    // information only.
    void basicprint() const
    {
      cout << ' ' << email_;
    }
  private:
    const char *email_;
};

template <class Base>
class PhoneAndEmailContact :
         public PhoneContact<Base>,
         public EmailContact<Base>
{
  public:
    // Because Base is a virtual base class, PhoneAndEmailContact is now 
    // responsible for its initialization.
    PhoneAndEmailContact(const char* fn,
                         const char* ln,char* pn,const char* e)
       :PhoneContact<Base>(fn,ln,pn),
       EmailContact<Base>(fn,ln,e),
         Base(fn,ln)
      {}
      void print() const
      {
         Base::print();
         PhoneContact<Base>::basicprint();
         EmailContact<Base>::basicprint();
      }
};

int main()
{
  Customer c1("Teddy","Bear");
  c1.print(); cout << endl;
  PhoneContact<Customer> c2("Rick","Racoon","050-998877");
  c2.print(); cout << endl;
  EmailContact<Customer> c3("Dick","Deer","dick@deer.com");
  c3.print(); cout << endl;
  PhoneAndEmailContact<Customer>
  c4("Eddy","Eagle","049-554433","eddy@eagle.org");
  c4.print(); cout << endl;
  return 0;
}

Listing Three
#include <iostream>
using namespace std;
// Define a class that wraps the union of all constructor arguments 
// of Customer and all derived mixin classes. 

// CustomerParameter combines all constructor arguments of CustomerParameter 
// and its derived mixin classes. The default values for the last two 
// arguments provide some convenience to the client programmer.
struct CustomerParameter
{
  const char
    * fn,
    * ln,
    * pn,
    * e;
  CustomerParameter( const char* fn_,const char*ln_,
    const char* pn_ = "",const char* e_ = "")
    :fn(fn_),ln(ln_),pn(pn_),e(e_)
  {}
};

class Customer
{
  public:
    Customer(const CustomerParameter& cp)
      :firstname_(cp.fn),lastname_(cp.ln)
    {}
    void print() const
    {
      cout << firstname_
           << ' '
           << lastname_;
    }
  private:
    const char *firstname_,
               *lastname_;
};

template <class Base>
class PhoneContact: public Base
{
  public:
    PhoneContact(const CustomerParameter& cp)
      :Base(cp),phone_(cp.pn)
    {}
    void print() const
    {
       Base::print();
       cout << ' ' << phone_;
    }
  private:
    const char *phone_;
};

template <class Base>
class EmailContact: public Base
{
  public:
    EmailContact(const CustomerParameter& cp)
      :Base(cp),email_(cp.e)
    {}
    void print() const
    {
      Base::print();
      cout << ' ' << email_;  
    }
  private:
      const char *email_;
};

int main()
{
  Customer c1(CustomerParameter("Teddy","Bear"));
  c1.print(); cout << endl;
  PhoneContact<Customer>
  c2(CustomerParameter("Rick","Racoon","050-998877"));
  c2.print(); cout << endl;
  EmailContact<Customer>
  c3(CustomerParameter("Dick","Deer","","dick@deer.com"));
  c3.print(); cout << endl;
  EmailContact<PhoneContact<Customer> >
  c4(CustomerParameter("Eddy","Eagle","049-554433","eddy@eagle.org"));
  c4.print(); cout << endl;
  PhoneContact<EmailContact<Customer> >
  // The following composition prints the last two arguments in a 
  // reverse order because the print() method is now composed differently.
  c5(CustomerParameter("Eddy","Eagle","049-554433","eddy@eagle.org"));
  c5.print(); cout << endl;
  return 0;
}

Listing Four
#include <iostream>
using namespace std;

// Define special intialization methods in each class and no longer rely 
// on the proper initialization though constructors.

class Customer
{
  public:
    // Initialization method for Customer.
    // A default constructor will be generated automatically.
    void init(const char* fn,const char* ln)
    {
      firstname_ = fn;
      lastname_ = ln;
    }
    void print() const
    {
      cout << firstname_
        << ' '
        << lastname_;
    }
  private:
    const char *firstname_,
               *lastname_;
};

template <class Base>
class PhoneContact: public Base
{
  public:
    // Initialization method for PhoneContact only.
    // A default constructor will be generated automatically.
    void init(const char* pn)
    {
      phone_ = pn;
    }

    void print() const
    {
      Base::print();
      cout << ' ' << phone_;
    }
  private:
    const char *phone_;
};

template <class Base>
class EmailContact: public Base
{
  public:
    // Initialization method for EmailContact only.
    // A default constructor will be generated automatically.
    void init(const char* e)
    {
      email_ = e;
    }

    void print() const
    {
      Base::print();
      cout << ' ' << email_;
    }
  private:
    const char *email_;
};

int main()
{
  // Compiler generated default constructor gets called.
  Customer c1;
  // Now explicitly invoke the initialization method.
  c1.init("Teddy","Bear");
  c1.print(); cout << endl;
  // Basically the same as above.
  PhoneContact<Customer> c2;
  // But initialization method for Customer must also be explicitly invoked!
  c2.Customer::init("Rick","Racoon");
  c2.init("050-998877");
  c2.print(); cout << endl;
  // Basically the same as above.
  EmailContact<Customer> c3;
  c3.Customer::init("Dick","Deer");
  c3.init("dick@deer.com");
  c3.print(); cout << endl;
  // Now the three initialization methods of three different mixin classes 
  // must be explicitly invoked! The composed class does not provide its own
  // initialization method.
  EmailContact<PhoneContact<Customer> > c4;
  c4.Customer::init("Eddy","Eagle");
  c4.PhoneContact<Customer>::init("eddy@eagle.org");
  c4.EmailContact<PhoneContact<Customer> >::init("049-554433");
  c4.print(); cout << endl;
  // Basically the same as above.
  PhoneContact<EmailContact<Customer> > c5;
  c5.Customer::init("Eddy","Eagle");
  c5.EmailContact<Customer>::init("eddy@eagle.org");
  c5.PhoneContact<EmailContact<Customer> >::init("049-554433");
  c5.print(); cout << endl;
  return 0;
}


Listing Five
#include <iostream>
using namespace std;

// Define additional constructors that will be instantiated only if required.

class Customer
{
  public:
    Customer(const char* fn,const char* ln):firstname_(fn),lastname_(ln)
    {}
    void print() const
    {
      cout << firstname_
        << ' '
        << lastname_;
    }
  private:
    const char *firstname_,
               *lastname_;
};

template <class Base>
class PhoneContact: public Base
{
  public:
    // The following constructors will be instantiated only if required.
    PhoneContact( const char* fn,const char* ln,
                  const char* pn):Base(fn,ln),phone_(pn)
    {}
    PhoneContact( const char* fn,const char* ln,
                  const char* pn,const char* e)
      :Base(fn,ln,e),phone_(pn)
    {}
    void print() const

    {
      Base::print();
      cout << ' ' << phone_;
    }
  private:
      const char *phone_;
};

template <class Base>
class EmailContact: public Base
{
  public:
    // The following constructors will be instantiated only if required.
    EmailContact( const char* fn, const char* ln,
                  const char* e):Base(fn,ln),email_(e)
    {}
    EmailContact( const char* fn,const char* ln,
                  const char* pn,const char* e)
      :Base(fn,ln,pn),email_(e)
    {}
    void print() const
    {
      Base::print();
      cout << ' ' << email_;
    }

  private:
    const char *email_;
};

int main()
{
  Customer c1("Teddy","Bear");
  c1.print(); cout << endl;
  PhoneContact<Customer> c2("Rick","Racoon","050-998877");
  c2.print(); cout << endl;
  EmailContact<Customer> c3("Dick","Deer","dick@deer.com");
  c3.print(); cout << endl;
  EmailContact<PhoneContact<Customer> >
  c4("Eddy","Eagle","049-554433","eddy@eagle.org");
  c4.print(); cout << endl;
  PhoneContact<EmailContact<Customer> >
  // The following composition prints the last two arguments in reverse 
  // order because the print() method is composed differently than previously.
  c5("Eddy","Eagle","049-554433","eddy@eagle.org");
  c5.print(); cout << endl;
  return 0;
}

Listing Six
#include <iostream>
using namespace std;
// We need NIL because - as opposed to void - it must be possible 
// to create instances of it.
struct NIL
{};

template <class T,class Next_ = NIL>
struct Param
{
  Param(const T& t_,const Next_& n_ = NIL()):t(t_),n(n_)
  {}
  const T& t;
  Next_ n;
  typedef Next_ N;
};

struct SomePersonParameters
{
  const char *firstname_,
             *lastname_;
  int age_;

  SomePersonParameters(const char* fn,const char* ln,const int age)
    :firstname_(fn),lastname_(ln),age_(age)
  {}
};

int main()
{
  SomePersonParameters p1("Peter","Parrot",3);
  cout << p1.firstname_ << ' '
       << p1.lastname_ << ' '
       << p1.age_ << endl;
  // Can be rewritten as
  Param<const char*,Param<const char*,Param<int> > >
  p2("Peter",Param<const char*,Param<int> >("Parrot",3)); //please note
  //that we can pass 3 as the last element instead of Param<int>(3)
  //because it will be automatically converted using the constructor
  //of Param
  cout << p2.t << ' '
       << p2.n.t << ' '
       << p2.n.n.t << endl;
  // Or more easily readable
  typedef Param<int> T1;
  typedef Param<const char*,T1> T2;
  typedef Param<const char*,T2> T3;
  T3 p3("Peter",T2("Parrot",3));
  cout << p3.t << ' '
       << p3.n.t << ' '
       << p3.n.n.t << endl;
  return 0;
}


Listing Seven
#include <iostream>
#include <string>
using namespace std;

struct NIL
{};

template <class T,class Next_ = NIL>
struct Param
{
  Param(const T& t_,const Next_& n_ = NIL()):t(t_),n(n_)
  {}
  
  const T& t;
  Next_ n;
  typedef Next_ N;
};

template <class Config_>
class Customer
{
  public:
    // Exporting config
    typedef Config_ Config;
    // Create parameter type
    typedef
      Param< typename Config::LastnameType,
      Param< typename Config::FirstnameType > > ParamType;
    Customer(const ParamType& p)
     :lastname_(p.t),firstname_(p.n.t)
    {}
    void print() const
    {
       cout << firstname_
       << ' '
       << lastname_;
    }
  private:
    typename Config::FirstnameType firstname_;
    typename Config::LastnameType lastname_;
};
template <class Base>
class PhoneContact: public Base
{
  public:
    // retrieve config and export it
    typedef typename Base::Config Config;
    // retrieve the constructor parameter type from the base class
    // and extend it with own parameters
    typedef Param< typename Config::PhoneNoType,
                   typename Base::ParamType > ParamType;
    PhoneContact(const ParamType& p)
      :Base(p.n),phone_(p.t)
    {}
    void print() const
    {
      Base::print();
      cout << ' ' << phone_;
    }
  private:
    typename Config::PhoneNoType phone_;
};
template <class Base>
class EmailContact: public Base
{
  public:
    // retrieve config and export it
    typedef typename Base::Config Config;
    // retrieve the constructor parameter type from the base class
    // and extend it with own parameters
    typedef Param< typename Config::EmailAddressType,
                   typename Base::ParamType> ParamType;
    EmailContact(const ParamType& p)
      :Base(p.n),email_(p.t)
    {}
    void print() const
    {
      Base::print();
      cout << ' ' << email_;
    }
  private:
    typename Config::EmailAddressType email_;
};
template <class Base>
struct ParameterAdapter: Base
{
  // Retrieve config form Base and export it.
  typedef typename Base::Config Config;

  // Retrieve the most complete param type
  typedef typename Config::RET::ParamType P;
  typedef typename P::N P1;
  typedef typename P1::N P2;

  // Constructor adapter with 1 argument
  template < class A1
           >
  ParameterAdapter( const A1& a1) 
    :Base(a1)
  {}

  // Constructor adapter with 2 arguments
  template < class A1,
             class A2
           >
  ParameterAdapter( const A1& a1,
                    const A2& a2)
    :Base(P(a2,a1))
  {}

  // Constructor adapter with 3 arguments
  template < class A1,
             class A2,
             class A3
           >
  ParameterAdapter( const A1& a1,
                    const A2& a2,
                    const A3& a3)
    :Base(P(a3,P1(a2,a1)))
  {}
  // Constructor adapter with 4 arguments
  template < class A1,
             class A2,
             class A3,
             class A4
           >
  ParameterAdapter( const A1& a1,
                    const A2& a2,
                    const A3& a3,
                    const A4& a4)
    :Base(P(a4,P1(a3,P2(a2,a1))))
  {}
};

struct C1
{
  // Provide standard name for config
  typedef C1 ThisConfig;

  // Provide elementary types
  typedef const char* FirstnameType;
  typedef const char* LastnameType;

  // Parameterize base class
  typedef Customer<ThisConfig> CustomerType;

  // Add ParameterAdapter
  typedef ParameterAdapter<CustomerType> RET;
};

struct C2
{
  // Provide standard name for config
  typedef C2 ThisConfig;

  // Provide elementary types
  typedef const char* FirstnameType;
  typedef const char* LastnameType;
  typedef const char* PhoneNoType;

  // Assemble mixin classes
  typedef Customer<ThisConfig> CustomerType;
  typedef PhoneContact<CustomerType> PhoneContactType;

  // Add ParameterAdapter
  typedef ParameterAdapter<PhoneContactType> RET;
};

struct C3
{
  // Provide standard name for config
  typedef C3 ThisConfig;
  
  // Provide elementary types
  typedef const char* FirstnameType;
  typedef const char* LastnameType;
  typedef const char* EmailAddressType;

  // Assemble mixin classes
  typedef Customer<ThisConfig> CustomerType;
  typedef EmailContact<CustomerType> EmailContactType;

  // Add ParameterAdapter
  typedef ParameterAdapter<EmailContactType> RET;
};
struct C4
{
  // Provide standard name for config
  typedef C4 ThisConfig;

  // Provide elementary types
  typedef const char* FirstnameType;
  typedef const char* LastnameType;
  typedef const char* PhoneNoType;
  typedef const char* EmailAddressType;
 
  // Assemble mixin classes
  typedef Customer<ThisConfig> CustomerType;
  typedef PhoneContact<CustomerType> PhoneContactType;
  typedef EmailContact<PhoneContactType> EmailContactType;
 
  // Add ParameterAdapter
  typedef ParameterAdapter<EmailContactType> RET;
};

int main()
{
  C1::RET c1("Teddy","Bear");
  c1.print(); cout << endl;
  C2::RET c2("Rick","Racoon","050-998877");
  c2.print(); cout << endl;
  C3::RET c3("Dick","Deer","dick@deer.com");
  c3.print(); cout << endl;
  C4::RET c4("Eddy","Eagle","049-554433","eddy@eagle.org");
  c4.print(); cout << endl;

return 0;
}

Listing Eight
#include <iostream>
#include <string>
using namespace std;
#include "meta.h"
using namespace meta;
#ifdef _MSC_VER
  #pragma warning (disable:4786)
  #pragma warning (disable:4305)
  #define GeneratorRET RET
#else
#define GeneratorRET Generator::RET
#endif

struct NIL
{};
template <class T,class Next_ = NIL>
struct Param
{
  Param(const T& t_,const Next_& n_ = NIL()):t(t_),n(n_)
  {}  

  const T& t;
  Next_ n;
  typedef Next_ N;
};

// We will pass Generator to Customer rather than Config; the latter will be
// nested in Generator, and Customer has to retrieve it; this modification
// of Customer is necessary to avoid certain circularity problems.
template <class Generator_>
class Customer
{
  public:
    // Exporting config
    typedef typename Generator_::Config Config;
   // ...
   // Rest of Customer is the same as in Listing Seven.

// The remaining mixin classes and the parameter adapter
// are the same as in Listing Seven.

// Bitmask for describing customer options - part of the domain
// specific language (DSL) for describing customers
enum CustomerSpec
{
  BasicCustomer = 0, // values represent bits in a bitmask
  WithPhone = 1,
  WithEmail = 2
};

// Customer generator (the generator parameters represent rest of DSL)
template <
           int spec = BasicCustomer, // spec is a bitmask
           class Firstname = const char*,
           class Lastname = const char*,
           class PhoneNo = const char*,
           class EmailAdd = const char*
         >
struct CUSTOMER_GENERATOR
{
  // Provide a shorthand for CUSTOMER_GENERATOR ...
  typedef CUSTOMER_GENERATOR < spec,
                               Firstname,
                               Lastname,
                               PhoneNo,
                               EmailAdd
                             > Generator;
  // Parse DSL
  // Assume there is always a basic customer ...
  enum
  { hasPhone = spec & WithPhone,
    hasEmail = spec & WithEmail
  };

  // Assemble components
  typedef Customer<Generator> Part1;

  typedef typename
    IF < hasPhone,
         PhoneContact<Part1>,
         Part1
       >::RET Part2;

  typedef typename
    IF < hasEmail,
         EmailContact<Part2>,
         Part2
       >::RET Part3;
  // Result of the generator template metafunction:
  typedef ParameterAdapter<Part3> RET;

  // Compute config
  struct BasicCustomerConfig
  { // Provide some metainformation
    enum
    { specification = spec };
    typedef Firstname FirstnameType;
    typedef Lastname LastnameType;
    typedef GeneratorRET RET;
  };
 
  struct CustomerWithPhoneConfig: BasicCustomerConfig
  {
    typedef PhoneNo PhoneNoType;
  };

  struct CustomerWithEmailConfig: BasicCustomerConfig
  {
    typedef EmailAdd EmailAddressType;
  };

  struct CustomerWithPhoneAndEmailConfig
    : CustomerWithPhoneConfig,CustomerWithEmailConfig
  {};

  typedef typename
    SWITCH < spec,
      CASE<BasicCustomer,BasicCustomerConfig,
      CASE<WithPhone,CustomerWithPhoneConfig,
      CASE<WithEmail,CustomerWithEmailConfig,
      CASE<WithPhone+WithEmail,CustomerWithPhoneAndEmailConfig
    > > > > >::RET Config;
};
int main()
{
  CUSTOMER_GENERATOR<>::RET c1("Teddy","Bear");
  c1.print(); cout << endl;
  CUSTOMER_GENERATOR<WithPhone>::RET c2("Rick","Racoon","050-998877");
  c2.print(); cout << endl;
  CUSTOMER_GENERATOR<WithEmail>::RET c3("Dick","Deer","dick@deer.com");
  c3.print(); cout << endl;
  CUSTOMER_GENERATOR<WithPhone + WithEmail>::RET
  c4("Eddy","Eagle","049-554433","eddy@eagle.org");
  c4.print(); cout << endl;
return 0;
}




15

