Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Can I have your feedback on this general mix-in pattern?

43 views
Skip to first unread message

DeMarcus

unread,
Aug 15, 2010, 7:39:58 PM8/15/10
to
Hi,

I needed to implement cloning and saw that I ended up with copy and
paste all over the code. I thought that this should be able to be solved
with mix-ins. After research and help from you (Is the Mixin pattern
accepted in all camps? 6 July) I thought that I probably would find a
good solution to this.

I found a nice first approach at Alf P. Steinbach's blog.

http://alfps.wordpress.com/2010/06/12/cppx-3-ways-to-mix-in-a-generic-cloning-implementation/

The approach that caught my interest was Way #2: Mix-in via a middleman
base class.

However, I thought the syntax would be slightly verbose using it
straight out of the box, and since I'm a perfectionist I tried to find a
solution where I could pack mix-ins together.

In Andrei Alexandrescu's library Loki he presents type list as a way to
pack types.

http://www.drdobbs.com/184403813

The problem, though, is that I don't want to pack types, but templates.

I have now worked on the problem and think I've found something quite
powerful.

The general mix-in pattern idea builds on C++0x and looks like this.

All mix-ins must conform to the following interface.

template<class T, class Base>
class SomeCustomMixin : public Base
{
public:
// Pass on all constructor arguments to the Base.
// If you don't want to be able to pass arguments to the Base you
// can remove this.
template<typename... Arguments>
inline SomeCustomMixin( Arguments&&... arguments )
: Base( std::forward<Arguments>( arguments )... ) {}

/* Implement your mix-in here. */
};

-------------------------------------------------

This is what the mix-in list looks like. This is not a /type/ list but
rather a /template/ list.

//file NullType.h
#ifndef NULL_TYPE_H_
#define NULL_TYPE_H_

class NullType {};

#endif

-----------------------

// file MixinList.h
#ifndef MIXIN_LIST_H_
#define MIXIN_LIST_H_

#include "NullType.h"

// This is needed to circumvent unimplemented parts in gcc.
template<template<class, class> class... Rest>
struct MixinList;


template
<
template<class, class> class Mixin,
template<class, class> class... Rest
>
struct MixinList<Mixin, Rest...>
{
// This is a trick to simulate 'using Head = Mixin;'
template<class T, class Base>
class Head : public Mixin<T, Base>
{
public:
// Pass on all constructor arguments to the Base.
// If you don't want to be able to pass arguments to the Base you
// can remove this.
template<typename... Argument>
inline Head( Argument&&... arguments )
: Mixin<T, Base>( std::forward<Arguments>( arguments )... )
{
}
};
// When template aliasing comes to gcc we can use
// using Head = Mixin;

typedef MixinList<Rest...> Tail;
};

// Cap the MixinList recursion with a specialization using
// NullType as Tail.
template
<
template<class, class> class Mixin
>
struct MixinList<Mixin>
{
template<class T, class Base>
class Head : public Mixin<T, Base>
{
public:
// Pass on all constructor arguments to the Base.
// If you don't want to be able to pass arguments to the Base you
// can remove this.
template<typename... Arguments>
inline Head( Arguments&&... arguments )
: Mixin<T, Base>( std::forward<Arguments>( arguments )... )
{
}
};

typedef NullType Tail;
};
#endif

----------------

Now comes the actual Mixins template that applies all the mix-ins to a
class.

//file EmptyType.h
#ifndef EMPTY_TYPE_H_
#define EMPTY_TYPE_H_

struct EmptyType {};

#endif

-----------------------

// file Mixins.h
#ifndef MIXINS_H_
#define MIXINS_H_

#include "EmptyType.h"
#include "NullType.h"

template<class T, class MixinList, class Base = EmptyType>
class Mixins : public MixinList::template Head<T,
Mixins<T, typename MixinList::Tail, Base>>
{
public:
// Pass on all constructor arguments to the Base.
// If you don't want to be able to pass arguments to the Base you
// can remove this.
template<typename... Arguments>
inline Mixins( Arguments&&... arguments )
: MixinList::template Head<T,
Mixins<T, typename MixinList::Tail, Base>>(
std::forward<Arguments>( arguments )... )
{
}
};

// When MixinList is NullType, end the recursion with a specialization.
template<class T, class Base>
class Mixins<T, NullType, Base> : public Base
{
public:
// Pass on all constructor arguments to the Base.
// If you don't want to be able to pass arguments to the Base you
// can remove this.
template<typename... Arguments>
inline Mixins( Arguments&&... arguments )
: Base( std::forward<Arguments>( arguments )... )
{
}
};

#endif

------------

Or, when template aliasing comes to gcc we may be able to use this file
instead.

// file Mixins.h
#ifndef MIXINS_H_
#define MIXINS_H_

#include "EmptyType.h"
#include "NullType.h"

template<class T, class MixinList, class Base = EmptyType>
using Mixins = MixinList::Head<T,
Mixins<T, typename MixinList::Tail, Base>>;

// End the recursion with a specialization using NullType.
template<class T, class Base = EmptyType>
using Mixins<T, NullType, Base> = Base;

#endif

-----------------

That's it! Let's use it. All code here is written using gcc 4.5.0.

First we create a mix-in called CloneMixin.

// file CloneMixin.h
#ifndef CLONE_MIXIN_H_
#define CLONE_MIXIN_H_

#include <memory>
#include <assert.h>

template<class T, class Base>
class CloneMixin : public Base
{
public:
typedef std::unique_ptr<T> CloneUPtr;

// Pass on all constructor arguments to the Base.
// If you don't want to be able to pass arguments to the Base you
// can remove this.
template<typename... Arguments>
inline CloneMixin( Arguments&&... arguments )
: Base( std::forward<Arguments>( arguments )... ) {}

virtual ~CloneMixin() {}

// Use NVI to be able to do an important assert.
inline CloneUPtr clone() const
{
CloneUPtr c( static_cast<T*>(clone_()) );
assert( typeid(*c.get()) == typeid(*this) );
return c;
}

protected:
virtual CloneMixin* clone_() const
{
return new T( *static_cast<const T*>(this) );
}
};

#endif

-------------------------------------

Let's make another mix-in.

// file TypeNameMixin.h
#ifndef TYPE_NAME_MIXIN_H_
#define TYPE_NAME_MIXIN_H_

template<class T, class Base>
class TypeNameMixin : public Base
{
public:

// Pass on all constructor arguments to the Base.
// If you don't want to be able to pass arguments to the Base you
// can remove this.
template<typename... Arguments>
inline TypeNameMixin( Arguments&&... arguments )
: Base( std::forward<Arguments>( arguments )... ) {}

virtual ~TypeNameMixin() {}

virtual std::string getTypeName() const
{
// This could be changed to something of your own taste.
// Maybe the mix-in could be initialized with a more convenient
// type name.
return typeid(T).name();
}
};

#endif

----------------------------------------

And here's the main program.

class MyBaseClass : public Mixins<MyBaseClass,
MixinList<CloneMixin, TypeNameMixin>>
{
};

class MyDerivedClass : public Mixins<MyDerivedClass,
MixinList<CloneMixin, TypeNameMixin>,
MyBaseClass>
{
};

int main()
{
MyDerivedClass dc;
std::unique_ptr<MyBaseClass> bc = dc->clone();

assert( typeid(*bc.get()) == typeid(dc) );

std::cout << dc.getTypeName() << std::endl;
std::cout << bc->getTypeName() << std::endl;

return 0;
}


I would really appreciate your feedback, both if you find flaws, but
also if you do not find flaws (or even find it usable).

I have two questions myself though:

1. The clone() function will be overridden in each derived class using
the CloneMixin. In C++ FAQ Lite they mention something about this, but
what's your opinion about overriding non-virtual for mix-in purposes?

http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.8


2. To be able to store templates in the template list MixinList without
template aliasing that doesn't exist yet for gcc, I use a special inner
template trick for Head. Do you see any problems with that trick?

Thanks,
Daniel

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Mathias Gaunard

unread,
Aug 16, 2010, 10:33:49 PM8/16/10
to
On Aug 16, 12:39 am, DeMarcus <use_my_alias_h...@hotmail.com> wrote:
> [fairly large amount of code]

> And here's the main program.
>
> class MyBaseClass : public Mixins<MyBaseClass,
> MixinList<CloneMixin, TypeNameMixin>>
> {
>
> };
>
> class MyDerivedClass : public Mixins<MyDerivedClass,
> MixinList<CloneMixin, TypeNameMixin>,
> MyBaseClass>
> {
>
> };

So all your over-complicated code just to write that instead of:

class MyBaseClass : public CloneMixin<MyBaseClass,
TypeNameMixin<MyBaseClass>>
{
};

class MyDerivedClass : public CloneMixin<MyDerivedClass,
TypeNameMixin<MyDerivedClass, MyBaseClass>>
{
};

DeMarcus

unread,
Aug 17, 2010, 8:18:52 PM8/17/10
to
On 2010-08-17 04:33, Mathias Gaunard wrote:
> On Aug 16, 12:39 am, DeMarcus<use_my_alias_h...@hotmail.com> wrote:
>> [fairly large amount of code]
>> And here's the main program.
>>
>> class MyBaseClass : public Mixins<MyBaseClass,
>> MixinList<CloneMixin, TypeNameMixin>>
>> {
>>
>> };
>>
>> class MyDerivedClass : public Mixins<MyDerivedClass,
>> MixinList<CloneMixin, TypeNameMixin>,
>> MyBaseClass>
>> {
>>
>> };
>
> So all your over-complicated code just to write that instead of:
>
> class MyBaseClass : public CloneMixin<MyBaseClass,
> TypeNameMixin<MyBaseClass>>
> {
> };
>
> class MyDerivedClass : public CloneMixin<MyDerivedClass,
> TypeNameMixin<MyDerivedClass, MyBaseClass>>
> {
> };
>
>

In summary: yes.
That was the goal; to make the syntax clean. But we also get another
good thing for free.

Suppose you have a large set of classes where all of them shall have the
same set of mix-ins. Then you can easily do this abstraction.

typedef MixinList<CloneMixin, TypeNameMixin> MySpecialMixinList;

class MyBaseClass
: public Mixins<MyBaseClass, MySpecialMixinList>
{
};

class MyDerivedClassX
: public Mixins<MyDerivedClassX, MySpecialMixinList, MyBaseClass>
{
};

class MyDerivedClassY
: public Mixins<MyDerivedClassY, MySpecialMixinList, MyBaseClass>
{
};

And if you later realize you need to add another mix-in, you just put it
in one place in your code; the MySpecialMixinList.
It's neat, isn't it? And the syntax is clean, for free. ;)

0 new messages