List of features
When (and when not) to use Boost.Mixin

Boost.Mixin is a library that allows the composition and modifications of polymorphic types at run time. Types and objects are constructed out of building blocks called mixins enabling an effect similar to multiple inheritance, while allowing the client code to remain oblivious to the actual composition of the objects.

A take on the Composition over inheritance technique, the result closely resembles the popular pattern entity-component-system, or the mixins in Ruby. It can also be compared to the inheritance in Eiffel or the traits in Self, Scala, PHP, and many others, or the roles in Perl.

This is given while also having full abstraction between the interface and the definition of types – a problem often given as the motivation for the PIMPL idiom.

In short, Boost.Mixin is an alternative way to accomplish polymorphism.

The library uses the type boost::mixin::object as a placeholder, whose instances can be extended with existing classes (mixins), thus providing a particular instance with the functionality of all those types. Accessing the newly formed type's interface is made through messages – stand-alone functions generated by the library, which can be thought of as methods.

Boost.Mixin focuses on maximal performance and minimal memory overhead.

The name "Mixin" is not to be confused with another meaning, popular in C++, namely CRTP mixins. This particular use of CRTP in this document shall henceforth be referred to as "traits", as the exact same functionality is called "traits" in many other languages.

  • In D the term mixin exists and mixin is a keyword. It is a compile time feature, that has some similarities with the macros in C and C++, and none at all with Boost.Mixin. It is a completely different concept.

Here is a very small and incomplete example of what your code may look like if you use the library.

// assuming my_objects.get_ally(0); is a way to get an ally to the
// main character in a game
boost::mixin::object& obj = my_objects.get_ally(0);

// now let's make the object think some positive thoughts about the
// main character

think(obj); // C++ doesn't allow us to have obj.think().
            // Boost.Mixin's messages are standalone functions

// type composition

// object can now respond to fly()

fly(obj); // ...instead of

// type mutation

think(obj); // the same object now thinks negative thoughts about the main
            // character, since it's no longer an ally, but an enemy

For a more detailed, working example see Basic usage.

  • Compose types from mixins at run time
  • Physically separate interface and implementation
  • Non-intrusive – mixins don't need to have a common parent or any special code inside
  • Mutate "live" objects by changing their composition at run time
  • Have multicast messages, which are handled by many mixins within an object
  • Possibility to have custom allocators to finely tune the memory and aim for cache-locality for critical parts of the code
  • Ability to have dynamic libraries that can enrich or modify objects, without modifying (even rebuilding) the executable.
  • Thread safe message calls - as thread safe as the underlying methods.

The more complex your objects are, the more beneficial it will be to use the library. Pieces of software that typically have very complex objects include Games (especially role-playing ones or strategies), CAD systems, enterprise systems, UI libraries, and more.

As a general rule of thumb: if you have complex polymorphic objects, Boost.Mixin is a good choice.

We should emphasize on the polymorphism. In many very high-performance systems polymorphism is avoided, at the cost of code that is (at least somewhat) harder to write and maintain (this is most often the case with high-end games). Since such systems will try to "squeeze" every possible piece of processing power out of the CPU, cache locality and lack of cache misses are critical in some parts of their code. As is the case with all instances of polymorphism, including C++ virtual methods and std::function, uses of Boost.Mixin's features will almost certainly lead to cache misses. Of course, you may still rely on the library in other parts of your code. For more information about the library performance, see the Performance section.

Of course, small projects with simple objects, even if they are polymorphic, may end up not finding any particular benefits in using the library, since their size makes them fast to compile and easy to maintain as they are. If a piece of software can be created in a couple of days, by one or two programmers, there will hardly be any need for Boost.Mixin.

The closest thing to Boost.Mixin that C++ can offer out of the box is multiple inheritance. A boost::mixin::object composed of some mixins, can be thought of as an empty class that's derived from these classes – the object's interface will be equal to the union of the interfaces of its mixins, and it will internally instantiate them.

We covered how much more than multiple inheritance the library is in the previous section, but in case you haven't inferred everything, here a comprehensive list of the most important differences between Boost.Mixin and C++ multiple inheritance:

  • No combinatorial explosion of types: you compose types at runtime, and don't need to explicitly list all possible combination of building-block types.
  • No type-bound interfaces: since the interface is physically separated from the implementation (or type), you don't need to have a single class implement an interface. You could separate it between multiple classes or combine interfaces in a single class.
  • Live object mutation: change the interface and/or implementation of messages in an object at runtime.
  • Natural common reference: in order to have containers of your objects composed by multiple inheritance, you need to define a "master type" from which all parents need to be derived by virtual inheritance. Such a type would be a coupling "focal point" and would need to be frequently changed while you develop your software. In Boost.Mixin this is a no-issue, since all objects in the library are of type boost::mixin::object.

However, even-though compared to other libraries that have similar features, Boost.Mixin is one of the fastest and with the least memory overhead, using the library comes with some inevitable downsides when compared with plain multiple inheritance:

  • No compile-time type: An object derived from, say, foo and bar can implicitly be cast too foo or bar. Since the library uses a placeholder type – boost::mixin::object – implicit casts to any of its mixins are impossible. That aside, the hypothetical object from above, will receive all of the methods from foo and bar, but while this is true for the mixin::object with such mixins, a compilation error cannot be generated if a message is called that the object can't handle. A runtime error will be generated instead, which is the norm in similar libraries, but is harder to catch and debug than it is with plain multiple inheritance.
  • Memory overhead per type: Each unique combination of mixins is internally represented by a type, which has type metadata – for example an internal alternative to a virtual table. The type metadata can take up to two kilobytes of memory. Unless a type is instantiated its metadata won't be created, but still if you expect to have a huge variety of types, you may want to keep this in mind. Of course systems that are expected to have thousands of different object types, usually wouldn't care about a couple of extra megabytes of memory, but it could happen.
  • Memory overhead per object: An instance of an empty class that has multiple parents takes up memory equal to the sizes of all of its parents. This is inevitable. A boost::mixin::objects naturally takes up the same amount plus an additional pointer for the type metadata, plus N pointers used for the special bm_this pointer, where N is equal to the number of mixins within. So, in short, the memory overhead of an object composed on N mixins, is N+1 times sizeof(intptr_t) ((N+1) * 8 bytes on a 64 bit system).
  • Speed of message calls: A message call is slower than a regular non-virtual method call. Exactly how much is very hard to estimate, since this depends very much on cache locality, but a message call can easily be compared to a std::function call in terms of speed, which in most (but not all) cases is a negligible overhead.

If you're familiar with entity-component-systems, one way of looking at at the library is as if it is one of those, and indeed, it has many features that are characteristic for such systems.

If you're not familiar with entity-component-systems, you might want to check out the appendix entry on them.

Here's how Boost.Mixin is like an ECS:

  • boost::mixin::object can be interpreted as an entity in an ECS. It's just an empty class, that needs to be "built" from mixins.
  • consequently mixins can be thought of as components. You use mixins to build objects just as you use components to build entities.
  • As with any ECS, you can mutate objects by adding/removing/substituting mixins.

However, Boost.Mixin is not strictly an ECS. Here's a list of the differences.

  • It is non-intrusive: Mixins don't need to inherit from a common parent, nor do you need to change the code of a class to "turn it into" a mixin.
  • Instead of classes with pure virtual methods, messages are used to represent the functional interface of mixins, and through that – of objects.
  • Because of the previous two differences, boost::mixin::object-s can be (and are) completely oblivious to what kinds of mixins there may be, allowing you to truly, physically separate a program's subsystems. The entity in an ECS on the other hand, usually has at least some knowledge of all the possible component types (like the top level parent classes, for example).
  • Some entity-component-systems allow several entities to point to the same instance of a component. This is not possible in Boost.Mixin. A mixin instance is bound to an object instance

Note that some data structures, sometimes called entity-component-systems, are not used to create and manage polymorphic objects, but instead are used to bind a strictly predefined set of concrete components to the same entity for a very performance intensive piece of software. The components are used by different subsystems, that require them to be aligned in dense arrays for faster processing, without cache misses. As we mentioned before, Boost.Mixin isn't designed for such cases, and while in terms of design and ease of use, it is a better choice than such an approach, it cannot help you in their main goal.

As mentioned above Boost.Mixin is similar to a feature of many other languages, called "traits". The exact same feature can be mimicked in C++ with CRTP. You may have heard the term "mixin" being mentioned in a C++ context before. It is very likely that what was meant was this CRTP style of creating types from existing ones.

Indeed, at least superficially, both Boost.Mixin and said traits are very similar as both are used to create new types by combining existing ones, while also solving one of the problems of plain old multiple inheritance - the communication between the different components that comprise the object.

However much of the multiple inheritance problems remain:

  • Types composed of traits are static. Most languages (including C++ using the aforementioned mimic) don't allow you to change an object's type.
  • There is still a combinatorial explosion of types: each possible combination needs to be explicitly coded.
  • In strongly typed languages, like C++, there is still the problem of having a common interface to all objects.
  • In C++ you are bound to having the same compilation dependencies because of the header files that need to be included to compose types.

Boost.Mixin solves all of these problems.

If you're familiar with the mixins in Ruby, perhaps you will find a lot of similarities between them and Boost.Mixin.

Ruby's mixins can of course be used just like traits, however extending existing objects with modules is also allowed via Object#extend. This is almost exactly what Boost.Mixin allows you to do. Indeed, Ruby has been a great inspiration for this library.

Still, barring the differences that arise from C++ being a strongly typed language, for a small amount of extra code Boost.Mixin allows you to do much more:

  • Removing mixins: in Ruby, when you extend an object with a module, all functions that both object and the module have are permanently overridden in the object by their versions from the module. Boost.Mixin allows you to temporarily mutate the object and then remove the mixin while restoring the object exactly to its former self.
  • Prioritize messages: in Boost.Mixin you can set priorities to the messages and thus when extending an object with a mixin, if a message is implemented by the object with a higher priority, even though it is also supplied by the mixin, in won't override the one already in the object.
  • Multicast messages: in Ruby (as in almost every other language with a similar functionality) a method call leads to a piece of code, defined for that method. Boost.Mixin allows you to have messages that are handled by more than one of mixins in an object.