Search code examples
c++qttemplatesqt4qabstractitemmodel

ModelResetter RAII Object


I have a custom proxy model that occasionally overhauls itself when a new column/row is added to its source model. From the docs it appears that calling QAbstractItemModel::beginResetModel() and QAbstractItemModel::endResetModel() at the beginning and end of such an operation is the proper methodology. My overhaul function, unfortunately, has several possible exit points and I just know that I'll forget to call endResetModel at every exit point as it gets more complex.

Therefore, I'd like to create a simple RAII class that will call beginResetModel upon construction and then call endResetModel upon destruction, like follows:

class ModelResetter
{
public:
    ModelResetter(QAbstractItemModel* model) : m_model(model)
    {
        m_model->beginResetModel();
    }
    ~ModelResetter()
    {
        m_model->endResetModel();
    }

private:
    QAbstractItemModel* m_model;
};

The problem is that beginResetModel() and endResetModel() are both protected in QAbstractItemModel. Declaring ModelResetter as a friend class in my inherited model doesn't appear to help, since I'm trying to interact with the base class.

I'd rather not do a custom implementation for every model I implement, so can I do this with templates? I'm not very familiar with template syntax yet.

Edit 1: (I removed the sample template code in Edit 2 to avoid confusion)

It'd be nice if I could somehow restrict the template to only allow types that inherit QAbstractItemModel, but I don't see anything in standard C++ that allows that. I will not use Boost.

Edit 2: I guess I wasn't really clear about my requirements. Here they are:

  • Operates on the base class for the general case
  • Enforces the QAbstractItemModel inheritance requirement while in debug mode without penalty in release mode
  • Simple usage with almost no overhead
  • Requires no modification of the base class or new functions

Solution

  • I hate to answer my own question, but after a couple days I put together a template-based solution that fulfills all of my requirements. Yay for my first from-scratch template class. Here's the implementation:

    //modelresetter.h
    #include <QAbstractItemModel>
    
    /* you must declare this class as a friend to your model
     * to give it access to protected members as follows:
     * template <class Model> friend class ModelResetter;
     */
    template<class Model>
    class ModelResetter
    {
    public:
        ModelResetter(Model* model) : m_model(model)
        {
            Q_ASSERT_X(qobject_cast<QAbstractItemModel*>(model) != 0, __FUNCTION__,
                       "templated object does not inherit QAbstractItemModel");
            m_model->beginResetModel();
        }
        ~ModelResetter()
        {
            m_model->endResetModel();
        }
    
    private:
        Model* m_model;
    };
    

    and usage:

    //mymodel.cpp
    bool MyModel::overhaul()
    {
        ModelResetter<MyModel> resetter(this); resetter;  //prevent compiler warning
    
        //do stuff
        if(somethingswrong)
            return false; //model will finish reset at every exit point
    
        //do more stuff
        return true; //model also completes reset on success
    }
    

    Thanks for your help!