PrevUpHomeNext

Debugging code that uses Boost.Mixin

Watching boost::mixin::object instances
Stepping into messages
Tracing/Logging information with code

Unfortunately debugging code that uses Boost.Mixin is a bit more challenging than debugging plain classes and method calls.

This is in part due to the macros that generate the message calling code, and also because – to save memory – the data within an object instance is not packed in easy to use and watch structures.

Perhaps one of the most common operations you'll want to have, will be to place a watch on a boost::mixin::object instance, in order to see the mixins that comprise it.

If you're using Microsoft Visual Studio, you can install one of the debug visualizers, provided with the library. There are instructions on how to do so in appendix entry B of this book.

If you're not using Microsoft Visual Studio or you don't want to install the visualizers, we'll assume you're trying to inspect an object instance called obj and proceed with the instructions for watching objects and their mixins:

The list of mixins in an object can be found in a std::vector in its type data. The field from our object's perspective is obj._type_info._compact_mixins.

The length of this vector is the number of mixins. Each element of the vector is of type mixin_type_info and has a field const char* name with the mixin name (which is a stringized version of its type)

In order to see one of the mixins in an object, you have to examine its mixin data. The field in the object is obj._mixin_data. This is a plain array of mixin_data_in_object. Its length is equal to the mixin count and each element corresponds to an element of the aforementioned _compact_mixins vector in the object type data. Each element has a field, called _mixin. This is a pointer to the actual mixin instance.

So, as an example let's assume obj is composed of the mixins opengl_rendering, mesh, and transform. This means that obj._type_info._compact_mixins will be a vector of three elements, and the names of those elements will be "opengl_rendering", "mesh", and "transform" in some (but not necessarily this) order.

Let's assume you want to inspect the mesh mixin within our object. Let's say it's with index N in the _compact_mixins vector. The value you'll need to watch then will be: (mesh*)obj._mixin_data[N]._mixin

The most difficult thing to see from an object is a list of the messages it implements. There is no compact structure that you can check.

The only way to do it is to check the call table of the type and compare it with the data in the domain.

So, first you need to inspect the plain array obj._type._call_table. Its size is the maximum number of messages allowed in the system. Some of its members are going to ne none-null pointers. Those are the messages that are implemented by the objects.

Unfortunately this information cannot help you see the message names, but only their ids. To see the exact name you need to add a domain instance to the watches. To do so, you'll have to have a line in your code like this one:

const boost::mixin::internal::domain& bm_domain = boost::mixin::internal::domain::instance();

From this domain instance you should be able to check its member bm_domain._messages. This again is an array of size BOOST_MIXIN_MAX_MESSAGES. Now you can use indexes of the non-null messages from obj._type._call_table in bm_domain._messages to see the message names. For example if our object implements message N, you can see its name in bm_domain._messages[N]->name.

Unfortunately due to the fact the the message caller functions are generated by macros, to step into a message call is a slightly annoying operation when debugging code that uses the library.

If you know which method in a mixin class will be called for a message it is a good idea to run to its first line (or place a breakpoint there) in order to skip the macro steps.

If you don't know, you will have to step into the macro. Since there's no debugger that we're aware of which will allow you to actually see the macro code as you're stepping through it, you most likely will have to spend some steps on the line BOOST_MIXIN_xxx_MESSAGE_N for the given message. And due to the fact that different IDEs and debuggers handle the stepping through macros differently, it's also hard to tell how many steps you need to spend there. It will be between 4 and 8 for unicasts and 5 and 14 for multicasts (plus 1 to 3 more if you're using a multicast combinator).

Your best approach is to experiment and write down how many steps (and what kind) you need to make on the macro line for your particular case of compiler and debugger. Initially stepping into everything until you reach the mixin class method.

For example, for Visual Studio 2012, in a Debug compilation, the pattern we use for a unicast message is 6 times step, 2 times step into.

Naturally, you might alternatively use a custom external preprocessor to expand the macro line (or expand it manually), for a message that you need to step into very often.

The boost::mixin::object class has two methods that have output parameters of type std::vector<const char*>get_message_names and get_mixin_names.

They will fill those vectors with the names of the messages the object implements and the mixins it has, respectively.


PrevUpHomeNext