Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Two different mixin patterns in C++. (mixin? CRTP?)

I'm studying about mixins (in C++). I read some articles on mixins and found two different patterns of "approximating" mixins in C++.

Pattern 1:

template<class Base>
struct Mixin1 : public Base {
};

template<class Base>
struct Mixin2 : public Base {
};

struct MyType {
};

typedef Mixin2<Mixin1<MyType>> MyTypeWithMixins;

Pattern 2: (may be called CRTP)

template<class T>
struct Mixin1 {
};

template<class T>
struct Mixin2 {
};

struct MyType {
};

struct MyTypeWithMixins : 
    public MyType, 
    public Mixin1<MyTypeWithMixins>, 
    public Mixin2<MyTypeWithMixins> {
};

Are they equivalent practically? I'd like to know practical difference between the patterns.

like image 541
upxuk Avatar asked Oct 21 '14 12:10

upxuk


People also ask

What is mixin class in C++?

A mixin is a class dessigned to provide functionality for another class, normally through a specified class which provides the basic features that the functionality needs. For example, consider your example: The mixin in this case provides the functionality of undoing the set operation of a value class.

What are mixins used for?

Mixins are a language concept that allows a programmer to inject some code into a class. Mixin programming is a style of software development, in which units of functionality are created in a class and then mixed in with other classes. A mixin class acts as the parent class, containing the desired functionality.

Does C++ support mixin?

This can be a bit confusing, however, because C++ does not natively support mixins; in order to "do" mixins in C++, you have to use multiple-inheritance! What this ends up meaning in practice is that you still use multiple-inheritence, but you artifically limit what you allow yourself to use it for.


2 Answers

The difference is visibility. In the first pattern, MyType's members are directly visible to and usable by the mixins, without any need for casting, and Mixin1's members are visible to Mixin2. If MyType wants to access members from the mixins, it needs to cast this, and there isn't a great way to do so safely.

In the second pattern, there is no automatic visibility between the type and the mixins, but the mixins can safely and easily cast this to MyTypeWithMixins and thereby access the members of the type and of other mixins. (MyType could too, if you applied the CRTP to it too.)

So it comes down to convenience versus flexibility. If your mixins are purely accessing services from the type, and have no sibling dependencies of their own, the first pattern is nice and straightforward. If a mixin depends on services provided by the type or other mixins, you're more or less forced to use the second pattern.

like image 162
Sneftel Avatar answered Nov 13 '22 05:11

Sneftel


Are they equivalent practically? I'd like to know practical difference between the patterns.

They are different conceptually.

For the first pattern, you have decorators going (transparently) over a core functionality class, each adding their own twist/specialization to an existing implementation.

The relationship the first pattern models is "is-a" (MyTypeWithMixins is a Mixin1<MyType> specialization, Mixin1<MyType> is a MyType specialization).

This is a good approach when you are implementing functionality within a rigid interface (as all types will implement the same interface).

For the second pattern, you have functionality parts used as implementation details (possibly within different, unrelated classes).

The relationship modeled here is "is implemented in terms of" (MyTypeWithMixins is a MyType specialization, implemented in terms of Mixin1 and Mixin2 functionality). In many CRTP implementation, the CRTP templated base is inherited as private or protected.

This is a good approach when you are implementing common functionality accross different, unrelated components (i.e. not with the same interface). This is because two classes inheriting from Mixin1 will not have the same base class.

To provide a concrete example for each:

For the first case, consider the modeling of a GUI library. Each visual control would have a (for example) display function, which in a ScrollableMixin would add scroll-bars, if required; The scrollbars mixin would be a base class for most controls that are re-sizable (but all of them a part of the "control/visual component/displayable" class hierarchy.

class control {
    virtual void display(context& ctx) = 0;
    virtual some_size_type display_size() = 0;
};

template<typename C>class scrollable<C>: public C { // knows/implements C's API
    virtual void display(context& ctx) override {
        if(C::display_size() > display_size())
            display_with_scrollbars(ctx);
        else
            C::display(canvas);
    }
    ... 
};

using scrollable_messagebox = scrollable<messagebox>;

In this case, all mixin types would override (for example) a display method, and delegate parts of it's functionality (the specialized drawing part) to the decorated type (the base).

For the second case, consider a case when you would implement an internal system for adding a version number to serialized objects within the application. The implementation would look like this:

template<typename T>class versionable<T> { // doesn't know/need T's API
    version_type version_;
protected:
    version_type& get_version();
};

class database_query: protected versionable<database_query> {};
class user_information: protected versionable<user_information> {};

In this case, both database_query and user_information store their settings with a version number, but they are in no way in the same object hierarchy (they don't have a common base).

like image 45
utnapistim Avatar answered Nov 13 '22 03:11

utnapistim