Building factories in C++ with boost::factory

published at 04.08.2015 15:27 by Jens Weller

This is the third part of my series on writing applications with C++ using Qt and boost. This time I focus on building factories, using boost::factory, with the ultimate goal to have widget factory producing QWidgets. The last part was about building trees and treemodels for QTreeView.

The video for this part:

boost::factory

boost has a small library focusing on the creation of factories as functional objects, so that boost::factory is part of boost::functional. The library offers templates for creating values and objects (using new), it even can create smart pointers. My actual use case is, to create widgets, which contain the back ends data from a mix of the tree nodes id and type_id. But boost::factory only gives you function objects which create objects, one still has to write a wrapper like class, to have a true factory, where you can register types and create their objects. So I went ahead, and wrote a factory template:

I'm not too eager to mix boost::function/bind and std::function/bind, but boost::factory only really works with boost::function, so that I have to use boost instead of the standard in this case. I want the template to be useable with Type but also Type*, that is why I remove the pointer before I add it again. Yet, this template does not know anything about boost::factory, simply because boost::factory is only needed, when a concrete type needs to be added. The create method simply calls the factory function object, and hands over possible arguments. This is important, as QWidgets usually get a pointer to their parent when created. The constructor arguments are implemented as a variadic template. I opted to use boosts flat_map for storing the connection between typeid and creation object.

Currently missing is the option to remove a type from the factory, this does not apply to my needs currently. The factory is once setup, and then used through out the program. This could change, when things like plugins are added, with the need to add and remove types at run time.

The actual concrete factory class is WidgetFactory, which defines the interface with which the Widgets for the tabcontrol are created:

This class keeps a cache of already created widgets, and exposes the factory functions. I choose not to derive from Factory, but rather add it as an implementation detail to the class. The createWidget method either returns the widget from a cache, or creates the object, adds it to the cache and returns it:

When an item is to be deleted from the tree, also its widget needs to be removed, hence the need to offer a removal method. The key for the flat_map is a type that joins the instance specific id with the type specific typeid, currently I opted to use std::pair for this. The removeWidget method actually needs to return the QWidget:

In the context where this method is called, the widget is not known, it might needs to removed from the tabcontrol, and of course to be destroyed. This happens in Qt with the call to deleteLater(), as possible active events should be processed first. So this code simply searches for the entry in the map, removes it a returns then the pointer to widget. It returns nullptr for the case that there is no type registered or no widget has been created for this instance. The registerType method simply forwards to the Factory class.

The factory creates widgets, when a node in the Treeview is double clicked. Via Qt Signal/Slot mechanism one can register a slot in the mainwindow to a signal from the treeview. This is the corresponding handler method:

This is not very complicated code, it needs to obtain the treenode pointer from QModelIndex, and then create the widget. Which is added to the tabcontrol. There is one remain of a previous refactoring still in this code, type2data is used to execute typespecific code after the factory has created the code. Kind of doing a two step initialization. This was because I had a little bit of trouble to get the factory running with constructor arguments. This code can now be refactored, so that the item->shared_from_this() also becomes a constructor argument. In order to use boost::factory with constructor arguments, it needs to be combined with boost::bind:

From the many types which will populate the tree, those who need to be constructed by the user, share the common need to have a name displayed in the tree. So I opted for a template method, which is called with a concrete type: createInstance<Dir>(index, "Enter directory name");. The item then is actually constructed inside the tree directly, plus the tree model doing the update on the tree view.