Library / SDK
iboB/dynamix avatar
iboB/dynamix

dynamix: runtime polymorphism by composing mixins into a live object

:fish_cake: A new take on polymorphism

693 stars49 forksC++MIT

At a glance

What is it?
dynamix is a C++17 library that replaces the fixed class hierarchy with a placeholder type, dynamix::object, whose instances are composed from mixins and can be mutated while the program is running. It targets architecture rather than features, and v2 is a full rewrite that is incompatible with v1, so adopting it is a design decision with a migration cost.
Who is it for?
dynamix fits a C++ team whose objects must change their capabilities while the program runs, such as a game with plugins or hot reloading, and it does not fit a codebase that depends on compile time type checking of behaviour. Before committing, read doc/misc/v2-vs-v1.md, build the example directory, and confirm your team can live with messages that the compiler cannot check against the object's type.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 99 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A placeholder type instead of a class hierarchy

DynaMix, which stands for Dynamic Mixins, is described as an alternative take on object oriented programming and dynamic polymorphism, letting users compose and modify polymorphic objects at run time, with C++ as the main target language and C also supported.

The mechanism is a placeholder type. The library uses dynamix::object, and instances of it can be extended with existing classes called mixins, which gives a particular instance the mixin features of all those types. The most important of those features are messages, functional objects which in C++ terms behave like methods, but are overridable and use late binding and singular dispatch. Unicasts and multicasts are possible.

The example in the README shows the shape of the code, and the shift in syntax is the point. A message is invoked as a standalone function taking the object, because C++ does not allow the library to add a member function to an object at run time.

c++
    // assuming my_objects.get_ally(0); is a way to get an ally to the
    // main character in a game
    dynamix::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().
                // DynaMix's messages are like standalone functions

The library frames itself as a means to create a project's architecture rather than to achieve its purpose, and it names extensibility, readability, scalability and interoperability as the goals, with maximal performance and minimal memory overhead as the stated priorities. The named application areas are games, especially role playing and strategy titles, CAD systems and enterprise systems.

Composition and mutation are the same operation

The second half of the example shows how an object's behaviour changes without its type changing. Composition adds a mixin, and the object can immediately respond to a message that mixin provides.

c++
    // composition
    mutate(obj, dynamix::add<flying_creature>());

    // object can now respond to fly()

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

Mutation then removes one mixin and adds another in a single call, and the same message produces a different outcome because the composition is different.

c++
    // mutation
    mutate(obj
        , dynamix::remove<ally>()
        , dynamix::add<enemy>()
    );

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

So the operation is a single mutate call with a list of additions and removals, and the semantics of a message depend on which mixins are currently present and in which order they were added. That ordering question is the part the README does not spell out: it mentions singular dispatch, overridable features and that a message can be handled by many mixins, but the documentation directory is where the precedence rules live.

For a reader used to virtual functions, this is the central trade. A virtual call is resolved through a vtable and a static type, and it cannot change during the lifetime of the object. A mixin message is resolved at call time from the object's current composition, so the same call site can mean different things before and after a mutation.

Mixins need no common parent and can arrive from a plugin

The library lists non-intrusive mixins among its key features, meaning the mixin classes do not need a common parent or any special code inside them. That is what makes the composition possible at all: the mixins are ordinary application or third party classes, and the library binds them to the object rather than asking them to inherit from anything.

The interface and the implementation are physically separated as a separate feature. The mixin supplies the implementation of a message, and the message itself is declared separately, so the two can live in different translation units and different libraries.

The consequence that reaches beyond the object model is plugin support. The README lists creating shared libraries and plugins which can enrich or modify objects without modifying, or even rebuilding, the executable. A game can ship a plugin that adds a new mixin type, load it at run time, and attach it to existing objects, with no relink of the main binary. Alongside that, the feature list includes adding hotswap to a project while developing, and complete runtime reflection by symbols or strings, which is what lets a plugin or a save file refer to a mixin type without a compile time reference to it.

The trade is that all of this depends on run time type information that the compiler cannot check. A mistyped message name or an unregistered mixin type is a run time failure rather than a compile time error, which is the price of the reflection that makes the plugin model possible.

Messages, multicast and thread safety

The message layer is where the performance claims have to be judged, and the README makes three specific statements about it.

Messages offer fast polymorphic calls, described as comparable to std::function, which is the benchmark a C++ developer will hold in mind: a call through a type erased callable rather than through a vtable, but with a comparable cost. Multicast messages are supported, and are handled by many mixins within an object, so a single send can reach several mixins rather than one. That is a capability plain C++ has no equivalent for, and it is the reason an object composed of several behaviours can react to one message in several ways.

Third, message calls are thread safe, with the stated caveat that they are as thread safe as the underlying methods. That phrasing is a boundary rather than a promise: the library does not serialise access for you, it does not introduce new hazards beyond what the mixin implementations already have.

The features list also mentions using std::polymorphic_allocator to allow fine-tuning allocations and achieving cache locality in critical parts of the code. Given the stated priorities of maximal performance and minimal memory overhead, that is the mechanism for the performance story: an object whose mixins are allocated by a custom allocator can have its memory laid out to suit the access pattern. It is also an extra decision for the adopter, since the allocator has to be threaded through the object creation path.

v2 is a rewrite, and v1 code does not move across

The README leads with a warning: v2 is here, it is a big change, and the last v1 release was v1.4.0. Further down, it is spelled out: DynaMix v2 is a complete rewrite, and though it has the same idea as v1, it is incompatible with it, with a different interface, a different implementation for most features and different terminology for some.

That is a large migration for a library embedded in an architecture, which is the point the README makes in the first place, that the library is a means to create a project's architecture. Architecture changes are not local edits. A codebase that took the v1 message declarations and mixin registrations has to be walked through one by one.

The migration aids are visible in the repository layout. There is a v1compat/ directory, a doc/ directory that the README names as the location of the documentation, and specifically doc/misc/v2-vs-v1.md, described as a list of the most notable differences between v1 and v2, plus a separate documentation site for v1. The build side shows CMakeLists.txt, CMakePresets.json, get_cpm.cmake for CMake package manager consumption, an example/ directory with hello-world and hello-c-world subdirectories, plus test/, bench/, code/, etc/, tools/, ci/ and scratch/ directories. The example directory with both a C++ and a C hello world confirms the C support claim concretely.

The library also has a history worth knowing: it was initially developed as Boost.Mixin but is now separate and does not depend on the Boost libraries collection. So the project is not a Boost extension any more, and a team that had used the Boost version is looking at a different package.

Where the approach pays and where it costs

The best case for this library is a system whose objects change their capabilities while the program runs. The README names games, in particular role playing and strategy titles, CAD systems and enterprise systems, and the list of projects built with it shows the pattern: a medical system for cancer treatment, an RPG, two mobile strategy titles, and a multiplayer RTS, among others. Several of those were built on v1, and the medical system and the serialization library huse are the ones recorded as using v2.

The alternative to consider is the plain virtual function, and the difference is not a matter of taste. With an interface and a vtable, the set of behaviours of an object is fixed when it is compiled, the call site is checked at compile time, and changing behaviour means rebuilding. With mixins, the behaviour set is decided at run time, the call site is a free function that the compiler cannot check against the object's type, and a plugin can extend the set without a relink. Teams that need plugins, hot reload during development, or objects whose role changes mid-session pay for that with a loss of static type safety.

A second alternative is to keep the type system and use composition in the ordinary way, holding shared pointers to collaborators and forwarding calls. That keeps compile time checking and avoids a learning curve, at the cost of a fixed object shape and manual forwarding, which becomes noise in a system with many capabilities.

The practical risk with DynaMix is therefore not performance but commitment. It changes how you declare behaviour across a codebase, the mixin ordering rules decide which implementation wins when several are present, and v2 already demonstrates that the interface can change between major versions.

MIT terms, a quiet release cadence, and a documentation directory

The library is distributed under the MIT Software License, with the copyright held by Borislav Stanimirov and Zahary Karadjov, and a LICENSE file at the top of the repository. The logo is separately licensed under Creative Commons Attribution 4.0, with its own attribution requirement, which is worth noting if you reuse the branding.

Maintenance has been quiet rather than abandoned. The last push was on 2026-07-18 and the repository is not archived, but the release list shows v2.0.4 on 2025-07-03, v2.0.3 on 2025-04-28 and v2.0.2 on 2024-03-16, so patch releases are infrequent and the project is not on a fast cadence.

The documentation lives in the repository rather than on a separate site, in the doc/ directory, with the README pointing there directly. The v1 documentation is on a separate site, and the two are separate interfaces for separate library versions, which is a detail to keep straight when following an old Stack Overflow answer.

The build badge points to a unit-test workflow in the.github directory, and the badge row includes a coverage report, so the project does run continuous integration and coverage tracking on the library. Contributions in the form of issues and pull requests are welcomed per the README.

Editorial conclusion

dynamix fits a C++ team whose objects must change their capabilities while the program runs, such as a game with plugins or hot reloading, and it does not fit a codebase that depends on compile time type checking of behaviour. Before committing, read doc/misc/v2-vs-v1.md, build the example directory, and confirm your team can live with messages that the compiler cannot check against the object's type.

Frequently asked questions

What is dynamix::object in DynaMix?

It is a placeholder type whose instances can be extended with existing classes called mixins, so an instance gains the mixin features of those types. The most important features are messages, functional objects that behave like methods with late binding and singular dispatch, and both unicasts and multicasts are possible.

How do you add or remove behaviour on a live DynaMix object?

You call mutate with additions and removals, for example mutate(obj, dynamix::add<flying_creature>()) to add a mixin and dynamix::remove<ally>() to take one away. The object then responds to the messages of the mixins currently present.

Is DynaMix v2 compatible with v1?

No. The README states that v2 is a complete rewrite with the same idea as v1 but a different interface, a different implementation for most features and different terminology for some, and the last v1 release was v1.4.0. Notable differences are listed in doc/misc/v2-vs-v1.md.

Can DynaMix mixins be delivered from a shared library at run time?

Yes. The feature list includes creating shared libraries and plugins which can enrich or modify objects without modifying, or even rebuilding, the executable, and the library offers complete runtime reflection by symbols or strings so a mixin type can be referred to without a compile time reference.

What are the requirements and licence for DynaMix?

The README badges indicate C++17, and C is also supported, with an example directory containing both a C++ and a C hello world. The software is distributed under the MIT Software License, and the logo is separately licensed under Creative Commons Attribution 4.0.

Official sources

  1. iboB/dynamix on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/ibob-dynamix.svg)](https://hysenlabs.com/projects/ibob-dynamix)

Was this useful?