The power of method templates

published at 14.07.2017 17:30 by Jens Weller
Save to Instapaper Pocket

This is very useful if you work with Qt, but could also apply in many other situations. My use case often is Qt, as it does not allow for template classes to be derived from QObject. Hence, all of these UI and widget classes end up being quite concrete implementations.

When refactoring one of my goals is always to have less code. In the last days I'm working on a feature, which does a lot of things manually, what otherwise is well hidden inside Qt with models & views. But my "model" can have any number of fields, and is mutable during run time. The main widget hosts two other widgets, one which hosts the view on the data, one that lets you edit the fields, including adding and deleting fields. Both (ab)use a QGridLayout to display their views. As this is part of my CMS, it also uses the "Update data when focus is lost" mechanism.

With QGridLayout this involves creating quite a lot of widgets, and as two classes share this abomination, its in a helper class called QGridLayoutHelper. Its derived from QObject if I ever wanted to add signals for row created/changed/removed. If not, I could implement the method template inside a CRTP class, which would be even more generic. But this is only possible, if you can lift also the needed variables into the CRTP class, if no dependencies to any member exist, its a strong hint for making it a free standing template function.

A quick look at the factory function for QWidget related types:

template< class Widget, class... Args >
Widget* createWidget(int col,int row,Args&&... args)
{
    Widget* w = new Widget(std::forward< Args >(args)...);
    gl->addWidget(w,row,col);
    return w;
}

Its just a quick helper to have a Widget created and added to the GridLayout. The variadic arguments make this work nicely with the many different constructors of the QWidget derived classes. Plus, you get to return the actual type to the caller.

When I designed the callback system for updating the model on focus lost, I wasn't sure if that would be always the best, so the actual EventFilter is generic enough not to check on it. But with a method template, I can write a better interface, which looks like that:

template< typename EventType,class ObjectType, class CallBack >
void registerEventForObjectType(EventType type, ObjectType* obj,CallBack&& cb)
{
    auto impl = [type,cb](QObject* obj,QEvent* event){
        if(event->type() == type)
        {
            auto object = qobject_cast< ObjectType* >(obj);
            if(object)
                cb(object);
        }
        return true;
    };
    object2setter.emplace(obj,impl);
    obj->installEventFilter(this);
}

This makes it possible to use any widget type with and event type, but still have a concrete call back, with the correct widget type instead of a QWidget pointer. This is critical for a datastructure, where a fieldname can change, so the call back for the data view must correlate field names to Widgets, e.g. std::map<QWidget*,QString>. That way the callback can look up the current fieldname, to know in which field to save the currently changed value.

Also this can be used to make certain containers of your implementation to be visitable by and callable:

template< class callable >
void visitFields(callable && f)
{
    for(const auto&& v: fields)
        f(v);
}

So method templates can be a very valuable addition to your C++ tool case, when you'd like to add generic features to a otherwise non-generic class. Good alternatives are patterns like CRTP or, if you don't need member access, a template method. Even with member access, you could make the method template a generic friend function. This is always then good, when you are able to reuse the freestanding template function with other classes in your library or application.

 

Join the Meeting C++ patreon community!
This and other posts on Meeting C++ are enabled by my supporters on patreon!