Account Options

  1. Sign in
The old Google Groups will be going away soon, but your browser is incompatible with the new version.
Google Groups Home
« Groups Home
Can I have your feedback on this general mix-in pattern?
There are currently too many topics in this group that display first. To make this topic appear first, remove this option from another topic.
There was an error processing your request. Please try again.
flag
  3 messages - Collapse all  -  Translate all to Translated (View all originals)
The group you are posting to is a Usenet group. Messages posted to this group will make your email address visible to anyone on the Internet.
Your reply message has not been sent.
Your post was successful
 
From:
To:
Cc:
Followup To:
Add Cc | Add Followup-to | Edit Subject
Subject:
Validation:
For verification purposes please type the characters you see in the picture below or the numbers you hear by clicking the accessibility icon. Listen and type the numbers you hear
 
DeMarcus  
View profile  
 More options Aug 15 2010, 7:39 pm
Newsgroups: comp.lang.c++.moderated
From: DeMarcus <use_my_alias_h...@hotmail.com>
Date: Sun, 15 Aug 2010 17:39:58 CST
Local: Sun, Aug 15 2010 7:39 pm
Subject: Can I have your feedback on this general mix-in pattern?
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...

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! ]


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Mathias Gaunard  
View profile  
 More options Aug 16 2010, 10:33 pm
Newsgroups: comp.lang.c++.moderated
From: Mathias Gaunard <loufo...@gmail.com>
Date: Mon, 16 Aug 2010 20:33:49 CST
Local: Mon, Aug 16 2010 10:33 pm
Subject: Re: Can I have your feedback on this general mix-in pattern?
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>>
{

};

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

 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
DeMarcus  
View profile  
 More options Aug 17 2010, 8:18 pm
Newsgroups: comp.lang.c++.moderated
From: DeMarcus <use_my_alias_h...@hotmail.com>
Date: Tue, 17 Aug 2010 18:18:52 CST
Local: Tues, Aug 17 2010 8:18 pm
Subject: Re: Can I have your feedback on this general mix-in pattern?
On 2010-08-17 04:33, Mathias Gaunard wrote:

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. ;)

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


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
End of messages
« Back to Discussions « Newer topic     Older topic »