PrevUpHomeNext

Tutorials

Messages
Object mutation
Multicast result combinators
Tips and tricks
Common problems and solutions

(For the complete, working source of this example see messages.cpp)

For this tutorial we'll look at a simplified piece of code from an imaginary game. First let's define the mixin classes that we're going to use.

There's a mixin that's a part from every object of our game. The one that gives them a unique id. We'll also define a method, called trace that will display information about the mixin in a stream.

class has_id
{
public:
    void set_id(int id) { _id = id; }
    int id() const { return _id; }

    void trace(ostream& out) const;

private:
    int _id;
};

Next we'll define a class for an animated model. We could have other types of models in the game, but for this tutorial there's no need to define anything more.

The mixin offers us a way to set a mesh and two ways to set an animation. It has a render method and, again the trace method, to display info about this mixin.

class animated_model
{
public:
    void set_mesh(const string& mesh);

    void set_animation(const string& animation);
    void set_animation(int anim_id);

    void render() const;

    void trace(ostream& out) const;

private:
    string _mesh;
    string _animation;
};

Now we'll define three types of mixins that will give us artificial intelligence logic for different occasions. They all share a method called think for the AI, and the familiar trace method.

class enemy_ai
{
public:
    void think();

    void trace(ostream& out) const;
};

class ally_ai
{
public:
    void think();

    void trace(ostream& out) const;
};

class stunned_ai
{
public:
    void think();

    void trace(ostream& out) const;
};

Now it's time to declare the messages our mixins will use. We have some methods in our classes for which there won't be any messages, since those methods aren't polymorphic. They're unique for their specific classes so it's absolutely adequate to call them by object.get<mixin>()->method(...).

So, let's start with the simplest case. The one we already used in the basic usage example.

The declaration syntax is the familiar macro BOOST_MIXIN_MESSAGE_|N|, where |N| stands for the number of arguments the message has. The macro's arguments are coma separated: return value, message/method name, argument 1 type, argument 1 name, argument 2 type, argument 2 name, etc etc.

This simple case is covered by the messages think and set_mesh [1]

BOOST_MIXIN_MESSAGE_0(void, think);
BOOST_MIXIN_MESSAGE_1(void, set_mesh, const string&, mesh);

Now it may seem that render is also a pretty simple example of a message, but there's a small difference. It's supposed to be handled by const methods. This makes it a const message and as such it has a different declaration macro – the same as before but with CONST added to it:

BOOST_MIXIN_CONST_MESSAGE_0(void, render);

Lets see the trace method, that's present in all of our classes. If we declare a message for it in the way we talked above, only of the mixins within an object will be able to handle it. But when we trace an object's info, we obviously would like to have the info for all of its mixins. For cases like this: where more than one of the mixins in an object is supposed to handle a message, Boost.Mixin introduces multicast messages. You declare those by adding MULTICAST to the macro (before MESSAGE but after CONST if it's a const one)

BOOST_MIXIN_CONST_MULTICAST_MESSAGE_1(void, trace, ostream&, out);

The last type of message there is meant for overloaded methods. For these we need message overloads.

A message overload will require you to think of a special name, that's used to refer to that message, different from the name of the method. Don't worry. The stand-alone function that's generated for the message call itself will have the appropriate name (the method's name).

The macro used for message overloads is the same as before with OVERLOAD at the end. The other difference is that its first argument should be the custom name for the message (followed by the type, method name, and method/message arguments like before).

In our case set_animation has two overloads:

BOOST_MIXIN_MESSAGE_1_OVERLOAD(set_anim_by_name, void, set_animation, const string&, animation);
BOOST_MIXIN_MESSAGE_1_OVERLOAD(set_anim_by_id, void, set_animation, int, anim_id);

As you might have guessed, any message could be defined as a message overload and indeed in the case where there are no overloads BOOST_MIXIN_MESSAGE_N(ret, message_name, ...) will just expand to BOOST_MIXIN_MESSAGE_N_OVERLOAD(message_name, ret, message_name, ...)

So, now that we've declared all our messages it's time to define them.

The macro used for defining a message is always the same, regardless of the message's constness, mechanism (multicast/unicast), or overload. It has a single argument – the message's name.

BOOST_MIXIN_DEFINE_MESSAGE(think);
BOOST_MIXIN_DEFINE_MESSAGE(set_mesh);
BOOST_MIXIN_DEFINE_MESSAGE(render);
BOOST_MIXIN_DEFINE_MESSAGE(trace);

For the overloads we should use our custom name:

BOOST_MIXIN_DEFINE_MESSAGE(set_anim_by_name);
BOOST_MIXIN_DEFINE_MESSAGE(set_anim_by_id);

Great! Now that we have our messages it's time to define the classes from above as mixins.

Normally if our program is spread across several files, you should use BOOST_DECLARE_MIXIN to declare that those classes are mixins, but since our program is in a single file, it can be omitted. All of its functionality is also in BOOST_DEFINE_MIXIN.

We met the BOOST_DEFINE_MIXIN macro from the basic example. It has two arguments – the mixin/class name and its feature list. The feature list is a ampersand separated list of symbols that represent the mixin's features. It can contain many things, but for now we'll focus on messages – the ones this mixin is supposed to handle.

The special thing here is that in order to distinguish the stand-alone function that's generated to make message calls from the message, the library defines a special symbol for each message. This symbol is used in the mixin feature list and when checking whether a mixin implements a message. The symbol is the message name postfixed with _msg.

Let's define three of our simple mixins along with their feature (message) lists:

BOOST_DEFINE_MIXIN(enemy_ai, think_msg & trace_msg);
BOOST_DEFINE_MIXIN(ally_ai, think_msg & trace_msg);
BOOST_DEFINE_MIXIN(animated_model,
    trace_msg & set_mesh_msg & set_anim_by_id_msg & set_anim_by_name_msg & render_msg);

The reason we left out has_id and stunned_ai is because we'd like to do something special with their message lists.

First, about has_id. What we'd like to do is display its info first, because the object id is usually the first thing you need about an object. So in order to achieve this, the notion of message priority is introduced. Each message in a mixin gets a priority of 0 by default. For multicast messages, like trace, the priority will affect the order in which they're executed. The higher priority a multicast message has in a mixin, the earlier it will be executed. So if we set the priority of trace in has_id to something greater than zero, we'll have a guarantee that when the object info is displayed, its id will come first.

BOOST_DEFINE_MIXIN(has_id, priority(1, trace_msg));

For unicast messages the priority determines which of the potentially many mixin candidates will handle the message. Again, mixins with higher priority for a message are considered better candidates.

So if we set the priority of think in stunned_ai to something greater than zero, then adding this mixin to an object that already has a think message (like objects with enemy_ai or ally_ai), will hide it previous implementation and override it with the one from stunned_ai. If we remove the mixin, the previous implementation will be exposed and will resume handling the think calls.

Also we'll consider stunned_ai as a relatively uninteresting mixin, and set the priority of trace to -1, and make its info be displayed last (if at all available)

BOOST_DEFINE_MIXIN(stunned_ai, priority(1, think_msg) & priority(-1, trace_msg));

We're now ready to start using our mixins and messages in the simplified game.

Let's start by creating two objects - an enemy and an ally to the hypothetical main character. We'll give them some irrelevant id-s and meshes.

boost::mixin::object enemy; // just an empty boost::mixin::object

boost::mixin::mutate(enemy)
    .add<has_id>()
    .add<animated_model>()
    .add<enemy_ai>();

enemy.get<has_id>()->set_id(1);
set_mesh(enemy, "spider.mesh");

trace(enemy, cout); // trace enemy data

boost::mixin::object ally;

boost::mixin::mutate(ally)
    .add<has_id>()
    .add<animated_model>()
    .add<ally_ai>();

ally.get<has_id>()->set_id(5);
set_mesh(ally, "dog.mesh");

trace(ally, cout); // trace ally data

Both calls to trace from above will display info about the newly constructed objects in the console.

think(enemy); // doing enemy stuff
think(ally); // doing friendly stuff

render(enemy); // drawing a hostile enemy
render(ally); // drawing a friendly ally

Now lets try stunning our enemy. We'll just add the stunned_ai mixin and, because of its special think priority, the calls to think from then on will be handled by it.

boost::mixin::mutate(enemy).add<stunned_ai>();
think(enemy); // don't do hostile stuff, because you're stunned
render(enemy); // drawing a stunned enemy

Now let's remove the stun effect from our enemy, by simply removing the stunned_ai mixin from the object. The handling of think by enemy_ai will resume as before.

boost::mixin::mutate(enemy).remove<stunned_ai>();
think(enemy); // again do hostile stuff
render(enemy); // drawing a hostile enemy

And that concludes our tutorial on messages.

(For the complete, working source of this example see mutation.cpp)

For this tutorial let's begin by introducing some mixins that may be found in a game: A root mixin, that should be present in all objects, and two that provide a way to render the object on the screen:

class game_object
{
    int _id;
    string name;
    // ... other common fields
};

class opengl_rendering {};

class directx_rendering {};

We won't concert ourselves with their concrete functionality, so we'll just leave them with no messages.

BOOST_DEFINE_MIXIN(game_object, boost::mixin::none);
BOOST_DEFINE_MIXIN(opengl_rendering, boost::mixin::none);
BOOST_DEFINE_MIXIN(directx_rendering, boost::mixin::none);

You're probably familiar from the previous examples with the most basic way to mutate an object, so let's use it to give this one a type.

boost::mixin::object obj1;
boost::mixin::mutate(obj1)
    .add<game_object>()
    .add<opengl_rendering>();

...and then change it. Let's assume we're switching our rendering platform.

boost::mixin::mutate(obj1)
    .remove<opengl_rendering>()
    .add<directx_rendering>();

Using the mutate class is probably the most common way in which you'll mutate objects in Boost.Mixin. Yes, mutate is not a function but a class. It has methods remove and add, and in its destructor it applies the actual mutation.

A mutation is a relatively slow process so if the internal object type was being changed on each call of remove or add, first the program would be needlessly slowed down, and second the library would need to deal with various incomplete types in its internal type registry.

So, if you want to add and remove mixins across several blocks or functions, you may safely instantiate the mutate class or use its typedef single_object_mutator that probably has a more appropriate name for cases like this.

boost::mixin::single_object_mutator mutation(obj1);

mutation.remove<directx_rendering>();
// ...
mutation.add<opengl_rendering>();

Here obj1 hasn't been mutated yet. A type that has game_object and opengl_rendering hasn't been instantiated internally. In order for this to happen the mutation instance needs to be destroyed, or, to explicitly perform the mutation, you may call apply like so:

mutation.apply();

Now obj1 has been mutated, and mutation has been "cleared" – returned to the empty state it had right after its instantiation. This means we can reuse it to perform other mutation on obj1. Say:

mutation.remove<game_object>();

Oops! We're removing the mixin that needs to be present in all objects. Not to worry. You may "clear" a mutation without applying it, by calling cancel.

mutation.cancel();

Now the mutation is not performed, and its state is empty.

You may safely apply empty mutations to an object:

mutation.apply(); // no effect

Another way to mutate objects is by using a type template.

A type template gives a type to an object and, unlike mutate/single_object_mutator it's not bound to a specific object instance. Again unlike mutate it disregards all mixins within an object and applies its internal type, hence it has no remove method. It implicitly "removes" all mixins that are not among its own.

You can create a type template like so:

boost::mixin::object_type_template directx_rendering_template;
directx_rendering_template
    .add<game_object>()
    .add<directx_rendering>()
    .create();

Again, similar to the case with single_object_mutator, you can spread these calls among many blocks or functions.

Don't forget to call create. It is the method that creates the internal object type. If you try to apply a type template that hasn't been created to an object, a runtime error will be triggered.

To apply a type template to an object, you may pass it as a parameter to its constructor.

boost::mixin::object obj2(directx_rendering_template);

Now obj2 has the mixins game_object and directx_rendering.

Let's create a new type template.

boost::mixin::object_type_template opengl_rendering_template;
opengl_rendering_template
    .add<game_object>()
    .add<opengl_rendering>()
    .create();

...to illustrate the other way of applying it to an object:

opengl_rendering_template.apply_to(obj2);

Applying this type template it the same object, was equivalent to mutate-ing it, removing directx_rendering and adding opengl_rendering.

Now we have two objects – obj1 and obj2 – that have the same mixins.

Sometimes the case would be such that you have a big group of objects that have the exact same type internally, and want them all to be mutated to have a different type. Naturally you may mutate each of them one by one, and this would be the appropriate (and only) way to mutate a group of objects that have a different type.

If the type is the same, however, you have a slightly faster alternative. The same type mutator:

boost::mixin::same_type_mutator directx;
directx
    .remove<opengl_rendering>()
    .add<directx_rendering>();

Unlike type templates, same type mutators don't need you to create them explicitly with some method. The creation of the internal type and all preparations are done when the mutation is applied to the first object.

directx.apply_to(obj1);
directx.apply_to(obj2);

Remember that the only time you can afford to use a same type mutator, is when all objects that need to be mutated with it are composed of the same set of mixins.

(For the complete, working source of this example see mutation_rules.cpp)

Let's define some mixins that may be present in a CAD system specialized for furniture design. Like in the previous example, we won't concern ourselves with any particular messages.

So, again we have a mixin that we want to be present in every object.

class furniture
{
    int _id;
    string name;
    // ... other common fields
};

BOOST_DEFINE_MIXIN(furniture, boost::mixin::none);

We also have mixins that describe the frame of the piece of furniture.

class wood_frame {};
BOOST_DEFINE_MIXIN(wood_frame, boost::mixin::none);

class metal_frame {};
BOOST_DEFINE_MIXIN(metal_frame, boost::mixin::none);

Let's also define some mixins that will be responsible for the object serialization.

class ofml_serialization {};
BOOST_DEFINE_MIXIN(ofml_serialization, boost::mixin::none);

class xml_serialization {};
BOOST_DEFINE_MIXIN(xml_serialization, boost::mixin::none);

And finally let's define two mixins that would help us describe our piece of furniture if it can contain objects inside – like a cabinet or a wardrobe.

class has_doors {};
BOOST_DEFINE_MIXIN(has_doors, boost::mixin::none);

class container {};
BOOST_DEFINE_MIXIN(container, boost::mixin::none);

Now, let's move on to the entry point of our program.

We said that each and every object in our system should be expected to have the mixin furniture. That could be accomplished if we manually add it to all mutations we make but there is a simpler way to do it. By adding the mandatory_mixin mutation rule.

All mutation rules should be added by calling add_new_mutation_rule. Since mandatory_mixin is a mutation rule that the library provides, we can accomplish this with a single line of code:

boost::mixin::add_new_mutation_rule(new boost::mixin::mandatory_mixin<furniture>);

Now each mutation after this line will add furniture to the objects (even if it's not been explicitly added) and also if a mutation tries to remove the furniture mixin from the object, it won't be allowed. There won't be an error or a warning. The library will silently ignore the request to remove furniture, or any other mixin that's been added as mandatory. Note, that if a mutation tries to remove furniture, and also adds and removes other mixins, only the part removing the mandatory mixin will be ignored. The others will be performed.

Another common case for using mandatory_mixin is if you want to have some debugging mixin, that you want present in you objects, when you're debugging your application. This is very easily accomplished if you just set the rule for it in a conditional compilation block.

You probably noticed the mixin ofml_serialization. OFML is a format specifically designed for describing furniture that's still used in some European countries, but hasn't gotten worldwide acceptance. Let's assume we want to drop the support for OFML, but without removing the actual code, since third party plugins to our CAD system may still depend on it. All we want is to prevent anybody from adding the mixin to an object. Basically the exact opposite of mandatory_mixin. This is the mutation rule deprecated_mixin

boost::mixin::add_new_mutation_rule(new boost::mixin::deprecated_mixin<ofml_serialization>);

After that line of code, any mutation that tries to add ofml_serialization won't be able to, and all mutations will try to remove it if it's present in an object. Again, as was the case before, if a mutation does many things, only the part from it, trying to add ofml_serialization will be silently ignored.

The last built-in rule in the library is mutually_exclusive_mixins.

Since a piece of furniture has either wood frame or a metal frame and never both, it would be a good idea to prevent the programmers from accidentally adding both mixins representing the frame in a single object. This mutation rule helps us do exactly that.

boost::mixin::mutually_exclusive_mixins* rule = new boost::mixin::mutually_exclusive_mixins;
rule->add<wood_frame>();
rule->add<metal_frame>();
boost::mixin::add_new_mutation_rule(rule);

You may add as many mutually exclusive mixins as you wish. If you had, say, plastic_frame, you could also add it to that list.

Any object mutated after that rule is set will implicitly remove any of the mutually exclusive mixins if another is added.

In many of our examples a sample game code was given, with mixins opengl_rendering and directx_rendering. The mutually_exclusive_mixins is perfect for this case and any other when we're always doing add<x>().remove<y>() and add<y>().remove<x>().

So to see this in practice:

boost::mixin::object o;

This object is empty. Mutation rules don't apply if there's no mutation. If, however, the object had been created with a type template passed in its constructor, then the rules would have been applied.

boost::mixin::mutate(o)
    .add<ofml_serialization>()
    .add<xml_serialization>()
    .add<wood_frame>();

Two rules are affected by this mutation. First it will implicitly add furniture to the object, and second it will ignore the attempt to add ofml_serialization. As a result the object will have furniture, xml_serialization and wood_frame.

boost::mixin::mutate(o)
    .add<metal_frame>();

The mutually exclusive mixins will ensure that after this line the object won't have the wood_frame mixin.

Having listed all built-in mutation rules, let's define a custom one.

Defining a custom rule is very easy. All you need to do is create a class derived from boost::mixin::mutation_rule and override its pure virtual method apply_to. The method has a single input-output parameter – the mutation that has been requested.

If you remember, we defined two mixins we haven't yet used – has_doors and container. We can safely say that a piece of furniture that has doors is always also a container (The opposite is not true. Think racks and bookcases). So it would be a good idea to add a mutation rule which adds the container mixin if has_doors is being added, and removes has_doors if container is being removed and the object has doors.

class container_rule : public boost::mixin::mutation_rule
{
public:
    virtual void apply_to(boost::mixin::object_type_mutation& mutation)
    {
        if(mutation.is_adding<has_doors>())
        {
            mutation.start_adding<container>();
        }

        if(mutation.is_removing<container>() && mutation.source_has<has_doors>())
        {
            mutation.start_removing<has_doors>();
        }
    }
};

That's it. Now all we have to do is add our mutation rule and it will be active.

boost::mixin::add_new_mutation_rule(new container_rule);

boost::mixin::mutate(o)
    .add<has_doors>();

After this mutation our custom mutation rule has also added container to the object.

boost::mixin::mutate(o)
    .remove<container>();

And after this line, thanks to our custom mutation rule, the object o will also have its has_doors mixin removed.

To see all ways in which you can change a mutation from the mutation rule, check out the documentation entry on object_type_mutation.

Lastly, there are two important pieces of information about mutation rules that you need to know.

First, note that the library will be responsible for freeing the memory and destroying the rules you've added. All you need to do is call add_new_mutation_rule with a rule, allocated and constructed with new.

Second, you may have noticed that mutation rules can logically depend on each other. You may ask yourselves what does the library do about that? Does it do a topological sort of the rules? Say we add a mandatory and a deprecated rule about the same mixin. How does it handle dependency loops?

The answer is simple. It doesn't. The rules are applied once per mutation in the order in which they were added. It is the responsibility of the user to add them in some sensible order. Had the library provided some form of rule sort, it would have needlessly overcomplicated the custom rule definition, especially for cases in which you actually want to... well, overrule a rule.

So, that's all there is to know about mutation rules.

(For the complete, working source of this example see combinators.cpp)

For this tutorial let's imagine we have a simple CAD program, which deals designing 3-dimensional objects. In such programs various aspects of an object need to be visible and editable at different times. Let's assume our objects are defined by their wireframe, their vertices, and their surface.

We'll define mixins for those and focus on the parts they have in common: namely whether an part of the object is visible, and how much elements is this part composed of.

BOOST_DECLARE_MIXIN(wireframe);

class wireframe
{
public:
    void set_visible(bool value);
    void set_elements_count(int count);

    bool visible() const;
    int elements_count() const;

    // ...

private:
    bool _visible;
    int _elements_count;
};

BOOST_DECLARE_MIXIN(vertices);

class vertices
{
public:
    void set_visible(bool value);
    void set_elements_count(int count);

    bool visible() const;
    int elements_count() const;

    // ...

private:
    bool _visible;
    int _elements_count;
};

BOOST_DECLARE_MIXIN(surface);

class surface
{
public:
    void set_visible(bool value);
    void set_elements_count(int count);

    bool visible() const;
    int elements_count() const;

    // ...

private:
    bool _visible;
    int _elements_count;
};

Now let's define messages for the methods we'll want to access polymorphically and define our mixins to use those messages.

BOOST_MIXIN_CONST_MULTICAST_MESSAGE_0(bool, visible);
BOOST_MIXIN_CONST_MULTICAST_MESSAGE_0(int, elements_count);

BOOST_MIXIN_DEFINE_MESSAGE(visible);
BOOST_MIXIN_DEFINE_MESSAGE(elements_count);

BOOST_DEFINE_MIXIN(vertices, visible_msg & elements_count_msg);
BOOST_DEFINE_MIXIN(wireframe, visible_msg & elements_count_msg);
BOOST_DEFINE_MIXIN(surface, visible_msg & elements_count_msg);

As you can see those are multicast messages. Each of the mixins in the object will implement and respond to them.

Now let's create some objects.

const int NUM_OBJECTS = 20;
boost::mixin::object objects[NUM_OBJECTS];

const boost::mixin::object& o = objects[0];

...and mutate them with some mixins, and give them some values.

For example let's say all of our objects are cubes, that have 24 vertices (4 per side), 6 squares for the wireframe, and a single (folded) surface.

for(int i=0; i<NUM_OBJECTS; ++i)
{
    boost::mixin::object& o = objects[i];

    boost::mixin::mutate(o)
        .add<vertices>()
        .add<wireframe>()
        .add<surface>();

    o.get<vertices>()->set_elements_count(24);
    o.get<vertices>()->set_visible(false);

    o.get<wireframe>()->set_elements_count(6);
    o.get<wireframe>()->set_visible(false);

    o.get<surface>()->set_elements_count(1);
    o.get<surface>()->set_visible(true);
}

As you may have noticed, all of our messages are functions that have a return value. You may have tried making multicast messages with non-void functions and noticed that the generated message function is void and doesn't return anything.

The things that will help us make use of the values returned from the messages are the multicast result combinators.

So, let's say we want to see if an object is visible. We say that it is visible if at least one of its mixins is. To get this value we may use the combinator boolean_or provided by the library (all built-in combinators are in namespace boost::mixin::combinators)

bool is_first_visible = visible<boost::mixin::combinators::boolean_or>(o);
cout << "The first object is " << (is_first_visible ? "visible" : "invisible") << "." << endl;

That's it. Giving a combinator as an explicit template argument to a multicast message call, will call a function that has a return value defined by the combinator. In this case boolean_or causes the message to return a bool which is true if at least one of the messages returns non-zero.

Had we defined that an object is visible if all of its mixins were visible, then we could have used the built-in combinator boolean_and.

Now let's look at another built-in combinator – sum. You may have guessed that it's a sum of all values returned by the messages. In our case we may want to check how many elements are there in an object:

cout << "There are " << elements_count<boost::mixin::combinators::sum>(o) << " elements in the first object." << endl;

All built-in combinators have an alternative usage. You saw the first, where putting the combinator as a template argument, causes the message to return a value.

The second usage keeps the message function void, but lets you add the combinator as an output parameter. This way, for example you may sum all elements throughout all objects with a single reusable combinator:

boost::mixin::combinators::sum<int> sum;
for(int i=0; i<NUM_OBJECTS; ++i)
{
    elements_count(objects[i], sum);
}
cout << "There are " << sum.result() << " elements among all objects." << endl;

That's basically all there is to know about using combinators. Now, let's move on to creating our own custom ones.

The built-in combinators are powerful, but sometimes you need to accomplish a task where you need some specific combinator behavior and need to add a custom one.

To create a custom combinator that's used as an output parameter is very easy. All you need to do is create a class, that has a public method called add_result. This public method should take one argument of the same type as (or one that can be implicitly cast to) the return type of the multicast message that you're "combining" with it. The method will be repeatedly called with each successful message with its return value as an argument. It should return booltrue when the execution should continue and false when it should stop.

We mentioned boolean_or and boolean_and. The function add_result in boolean_or returns false on the first non-zero value. That means it has determined the the final value is true (because at least one true has been met) and there is no need to execute the rest. Likewise boolean_and's add_result returns false on the first zero value it gets. Exactly as C++'s operators || and && behave.

So let's define our output parameter combinator that counts all mixins that have more than 1 element. Also, we could call it for a single object, but let's make use of the fact that it's an output parameter and count all mixins with more than 1 element among all objects:

struct more_than_1
{
    more_than_1() : count(0) {}

    bool add_result(int elements)
    {
        if(elements > 1)
            ++count;

        // Never break. We need this for all mixins in an object
        return true;
    }

    int count;
};

more_than_1 counter;

for(int i=0; i<NUM_OBJECTS; ++i)
{
    elements_count(objects[i], counter);
}
cout << "There are " << counter.count << " mixins with more than 1 element among all objects." << endl;

Now, the last case we need to cover is when you want your custom combinator to be added as a template argument to the message's function giving it a return value of its own.

To do this is only slightly more complicated the the previous return parameter case.

You need to create a template class whose template parameter will be provided by the message call and will be the message return type.

Next, as before you'll need an add_result method to be repeatedly called, again having a single argument of type equal to the message return type (you may just reuse the template argument of the combinator class), and again returning bool to indicate whether the message execution should continue or stop.

Next, you'll need a typedef result_type, which will indicate the return type of the message function.

Lastly, you'll have to create a method, called result, with no arguments, that has a return type result_type. It will be called when the execution is completed and it will provide the return value of the message function.

So, let's create an identical combinator as before – one that counts the mixins in an object that have more than one element, but this time to be used as a template argument of the message function.

template <typename MsgReturnType>
struct more_than_1_t
{
    more_than_1_t() : count(0) {}

    bool add_result(MsgReturnType elements)
    {
        if(elements > 1)
            ++count;

        // Never break. We need this for all mixins in an object
        return true;
    }

    // return type of message function
    typedef int result_type;

    result_type result() const
    {
        return count;
    }

    int count;
};

Now we can use our new custom combinator as we used boolean_or above.

cout << "There are " << elements_count<more_than_1_t>(o)
    << " mixins with more than 1 element in the first object." << endl;

And that's all there is about multicast result combinators.

  • When adding the same set of messages to multiple mixins, create a define of them all. Like: #define transform_messages set_position_msg & set_orientation_msg. Then use it like this BOOST_DEFINE_MIXIN(x, some_messages & transform_messages);
  • Instead of using the long message declaration macros, consider defining your own. Maybe something like #define BM_C_MSG_1 BOOST_MIXIN_CONST_MESSAGE_1
  • Prefer using object_type_template-s instead of mutating each new object in the same fashion.
  • Prefer using same_type_mutator when mutating multiple objects of the same type.
  • If you have some messages that are valid for all objects in your system, instead of adding them to a mixin present in every object, consider having some stand-alone functions where your first parameter is boost::mixin::object&. They will be indistinguishable from messages.
  • If you have multicast logic that needs to stop after a success in any of the message implementations in an object, have your messages return bool and then use the boolean_or combinator. It will stop the message execution on the first true.

Sometimes you will feel the need to have mixins with a common parent. Most likely this will happen when you want to define two different mixins that share some common functionality. Moving the shared functionality in the same common parent is a good idea and Boost.Mixin will work exactly the same way if you do this. However there is a pitfall in this case. It happens when you have multiple inheritance. Due to the special nature in which the library arranges the memory internally, if a mixin type has more than one parent, using bm_this in some of those parents might lead to crashes.

More precisely, when the library allocates memory for a mixin type, it allocates a buffer that is slightly bigger than needed and puts the pointer to the owning object at its front. What bm_this does is actually an offset from this with the appropriate number of bytes for object*. So if a parent of your mixin type, other than the first, calls bm_this, it will end up returning an invalid pointer to the owning object.

To be able to have parents, other than the first, with access to the owning object we suggest that you create a pure virtual function that gets it from the actual mixin type.

Say virtual object* get_bm_object() = 0; in the parents, which is implemented in the child class (the actual mixin defined with BOOST_DEFINE_MIXIN) by simply return bm_this.

Of course there are other ways to accomplish this, for example with CRTP, but the virtual function is probably the cleanest and safest one.

  • No overload of _boost_get_mixin_type_info supports the type
    • Problem: In your code you're referring to a mixin, that the library cannot recognize as such. This could be caused by a mutation, or by calling object::get or object::has.
    • Solution: Make sure you've spelled the name of the mixin correctly. Make sure the line that produces the error has visibility to the declaration of this mixin (by BOOST_DECLARE_MIXIN)
  • No overload of _boost_get_mixin_feature supports the type
    • Problem: You're calling object::implements with a message that cannot be recognized as such.
    • Solution: Make sure you've spelled the message name correctly. Make sure you've added the _msg suffix to the message.
  • No overload of _boost_register_mixin_feature supports the type
  • ...or no overload of operator & supports the type
    • Problem: You're adding a mixin feature to the feature list in BOOST_DEFINE_MIXIN that cannot be recognized as such
    • Solution: If it's a message, check that you've added the _msg suffix. If it's an allocator, make sure it's derived from the mixin_allocator class
  • Redefinition of _boost_get_mixin_feature
    • Problem: You're defining two messages with BOOST_MIXIN_DEFINE_MESSAGE that have same name in the same file.
    • Solution: Declare the messages as overloads with BOOST_MIXIN_xxx_MESSAGE_N_OVERLOAD. Define the overloads.

Undefined reference (mentioned below) will be reported as an "unresolved external symbol" in Visual C++.

  • Undefined reference to _boost_mixin_get_type_info
    • Problem 1: A type has been declared as a mixin, but hasn't been defined
    • Solution 1: Define the mixin with BOOST_DEFINE_MIXIN
    • Problem 2: A mixin is being used from a dynamic library and hasn't been exported correctly.
    • Solution 2: Declare the mixin properly as BOOST_DECLARE_EXPORTED_MIXIN
  • Undefined references to _boost_register_mixin_feature and _boost_get_mixin_feature
    • Problem 1: A message has been declared but not defined.
    • Solution 1: Define the message with BOOST_MIXIN_DEFINE_MESSAGE
    • Problem 2 : A message is being used from a dynamic library and hasn't been exported correctly.
    • Solution 2: Declare the message properly as BOOST_MIXIN_EXPORTED_xxx_MESSAGE_N
  • Multiple references to _boost_register_mixin_feature and _boost_get_mixin_feature
    • Problem: Two messages have been defined with the same name and different arguments.
    • Solution: Declare the messages as overloads with BOOST_MIXIN_xxx_MESSAGE_N_OVERLOAD

The exceptions the library may throw, what causes them, and how to fix them can be found in the reference. Besides them, these may also occur:

  • Assertion fails in domain.hpp: "you have to increase the maximum number of mixins"
    • Problem: The maximum number of registered mixins supported by the library has been surpassed.
    • Solution: You have to increase the value of BOOST_MIXIN_MAX_MIXINS in the file config.hpp of the library and then rebuild it.
  • Assertion fails in domain.cpp: "you have to increase the maximum number of messages"
    • Problem: The maximum number of registered messages supported by the library has been surpassed.
    • Solution: You have to increase the value of BOOST_MIXIN_MAX_MESSAGES in the file config.hpp of the library and then rebuild it.


[1] Although set_mesh is a message that can be handled by a single class in our example, in an actual product there would be other types of "model" mixins, which would make it polymorphic. That's why we're making it a message instead of a method to be called by object.get<animated_model>()->set_mesh(somemesh)


PrevUpHomeNext