What is Mixins (as a concept)

I am trying to understand the concept of Mixin, but I can not understand what it is. I see that this is a way to extend the class with inheritance. I read that people call them "abstract subclasses." Can someone explain why?

I would be grateful if you explain your answer with the following example (from one of my slide shows): A C ++ Mixin Example

+52
c ++ oop mixins templates
Sep 12 '13 at 20:03
source share
5 answers

Before moving on to what mixing is, it is helpful to describe the problems that he is trying to solve. Say that you have a bunch of ideas or concepts that you are trying to model. They can be connected in some way, but for the most part they are orthogonal, that is, they can stand independently independently of each other. Now you can model this by inheritance, and each of these concepts derives from some general interface class. You then provide specific methods in a derived class that implements this interface.

The problem with this approach is that this design does not offer a clear, intuitive way to take each of these specific classes and combine them.

The idea with mix-ins is to provide a bunch of primitive classes where each of them models a basic orthogonal concept and can combine them together to create more complex classes with just the function you want - kind of like legos. The primitive classes themselves are intended to be used as building blocks. This is extensible since you can later add other primitive classes to the collection without affecting existing ones.

Returning to C ++, for this you need to use templates and inheritance. The main idea here is that you connect these building blocks together by providing them through a template parameter. Then you connect them together, for example. via typedef to form a new type containing the desired functionality.

Taking your example, suppose we want to add redo functionality on top. Here's what it might look like:

 #include <iostream> using namespace std; struct Number { typedef int value_type; int n; void set(int v) { n = v; } int get() const { return n; } }; template <typename BASE, typename T = typename BASE::value_type> struct Undoable : public BASE { typedef T value_type; T before; void set(T v) { before = BASE::get(); BASE::set(v); } void undo() { BASE::set(before); } }; template <typename BASE, typename T = typename BASE::value_type> struct Redoable : public BASE { typedef T value_type; T after; void set(T v) { after = v; BASE::set(v); } void redo() { BASE::set(after); } }; typedef Redoable< Undoable<Number> > ReUndoableNumber; int main() { ReUndoableNumber mynum; mynum.set(42); mynum.set(84); cout << mynum.get() << '\n'; // 84 mynum.undo(); cout << mynum.get() << '\n'; // 42 mynum.redo(); cout << mynum.get() << '\n'; // back to 84 } 

You will notice that I made a few changes from your original:

  • Virtual functions are not really needed here because we know for sure that our composite class type is at compile time.
  • I added a default value_type for the second template parameter to make its use less cumbersome. This way you do not need to type <foobar, int> every time you insert a piece together.
  • Instead of creating a new class that inherits from parts, a simple typedef .

Note that this should be a simple example illustrating the idea of ​​mixing. Therefore, it does not take into account corner cases and funny customs. For example, executing undo without setting the number will probably not behave as you might expect.

As a side element, you can also find this article .

+82
Sep 12 '13 at 22:21
source share

A mixin is a class assigned to provide functionality for another class, usually through a specific class that provides the basic functions that the functionality needs. For example, consider your example:
The mixture in this case provides the functionality of canceling a given operation of a class of values. This habit is based on the get/set functionality provided by the parameterized class (the Number class in your example).

Another example (extracted from "Mixin-based C ++ Programming" ):

 template <class Graph> class Counting: public Graph { int nodes_visited, edges_visited; public: Counting() : nodes_visited(0), edges_visited(0), Graph() { } node succ_node (node v) { nodes_visited++; return Graph::succ_node(v); } edge succ_edge (edge e) { edges_visited++; return Graph::succ_edge(e); } ... }; 

In this example, mixin provides vertex counting functionality, given the class of the graph that performs transverse operations.

Usually in C ++ mixins are implemented using CRTP idioms. This thread can be well read about the implementation of mixin in C ++: What is C ++ Mixin-Style?

Here is a mixin example that uses the CRTP idiom (thanks to @Simple):

 #include <cassert> #ifndef NDEBUG #include <typeinfo> #endif class shape { public: shape* clone() const { shape* const p = do_clone(); assert(p && "do_clone must not return a null pointer"); assert( typeid(*p) == typeid(*this) && "do_clone must return a pointer to an object of the same type" ); return p; } private: virtual shape* do_clone() const = 0; }; template<class D> class cloneable_shape : public shape { private: virtual shape* do_clone() const { return new D(static_cast<D&>(*this)); } }; class triangle : public cloneable_shape<triangle> { }; class square : public cloneable_shape<square> { }; 

This mixin provides heterogeneous copy functionality for a set (hierarchy) of form classes.

+7
Sep 12 '13 at 20:31 on
source share

I like the answer from the great wolf, but I would suggest one point of caution.

The spinning top said: "Virtual functions are really not needed here, because we know for sure that our composite class type is at compile time." Unfortunately, you may encounter some inconsistent behavior if you use your object polymorphically.

Let me customize the main function from his example:

 int main() { ReUndoableNumber mynum; Undoable<Number>* myUndoableNumPtr = &mynum; mynum.set(42); // Uses ReUndoableNumber::set myUndoableNumPtr->set(84); // Uses Undoable<Number>::set (ReUndoableNumber::after not set!) cout << mynum.get() << '\n'; // 84 mynum.undo(); cout << mynum.get() << '\n'; // 42 mynum.redo(); cout << mynum.get() << '\n'; // OOPS! Still 42! } 

By making the "set" function virtual, the correct override will be called and the inconsistent behavior above will not occur.

+4
Feb 10 '14 at 23:57
source share

Mixes in C ++ are expressed using the Curiously Recurring Template Pattern (CRTP). This post is a great breakdown of what they provide compared to other reuse methods ... compile-time polymorphism.

+4
Mar 06 '15 at 2:34
source share

This works just like an interface, and perhaps more abstract, but interfaces are easier to get for the first time.

It addresses many problems, but one of them that I find in development, which appears a lot, is the external apis. Imagine this.

You have a user database, this database has a certain way of accessing its data. Now imagine that you have facebook, which also has a certain way of accessing your data (api).

at any time when your application can be launched using data from facebook or your database. so you create an interface that says that "everything that implements me will have certain methods", now you can implement this interface in your application ...

because the interface promises that the methods declared in them will be indicated in the implementation repositories, you know that everywhere or when you use this interface in your application, if you switch data, they will always have methods that you are defining and therefore have data to work with.

There are many layers to this work template, but the bottom line is that it’s good, because data or other such permanent elements become a big part of your application, and if they change without your knowledge, your application may break :)

Here is some pseudo code.

 interface IUserRepository { User GetUser(); } class DatabaseUserRepository : IUserRepository { public User GetUser() { // Implement code for database } } class FacebookUserRepository : IUserRepository { public User GetUser() { // Implement code for facebook } } class MyApplication { private User user; MyApplication( IUserRepository repo ) { user = repo; } } // your application can now trust that user declared in private scope to your application, will have access to a GetUser method, because if it isn't the interface will flag an error. 
0
Sep 12 '13 at 20:31 on
source share



All Articles