I just read Jens Wellers article regarding raw loops vs. STL algorithms.
At the end he states “When I work with Qt, there are some cases, when you only get the count and a method to access item n, like in a QListWidget:” with the example:
for(int i = 0,s=ui->lst_feeds->count();i < s; ++i) { auto* item = ui->lst_feeds->item(i); auto si = item->data(Qt::UserRole).value< FeedItem::SharedItem >(); if(si && si->contains(list)) item->setCheckState(Qt::Checked); }
And that is the point where I want bring in the possibility to use Qt widget container like objects in range based for loops or in STL algorithms. The only thing that one needs for this, is a small proxy that provides the begin(), end() interface:
template <class T, class R, int (T::*Count)() const, R *(T::*Access)(int)const> class QtContainerProxy { T *_object; public: using value_type = R*; explicit QtContainerProxy(T& object) : _object(&object) {} QtContainerProxy(const QtContainerProxy&) = default; QtContainerProxy& operator=(const QtContainerProxy&) = default; class iterator { public: using iterator_category = std::bidirectional_iterator_tag; using value_type = typename QtContainerProxy::value_type; using difference_type = ptrdiff_t; using pointer = value_type; using const_pointer = const pointer; using reference = value_type; using const_reference = const reference; iterator(const iterator&) = default; iterator& operator=(const iterator&) = default; iterator(QtContainerProxy& proxy, int index) : _index{ index } , _proxy{ proxy } {} reference operator*() const { return (_proxy._object->*Access)(_index); } pointer operator->() const { return operator*(); } iterator &operator++() { _index += 1; return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } iterator &operator--() { _index -= 1; return *this; } iterator operator--(int) { iterator tmp = *this; --*this; return tmp; } friend bool operator==(const iterator &x, const iterator &y) { return &x._proxy == &y._proxy && x._index == y._index; } friend bool operator!=(const iterator &x, const iterator &y) { return !(x == y); } private: int _index; QtContainerProxy& _proxy; }; using const_iterator = const iterator; iterator begin() const { return iterator(const_cast<QtContainerProxy&>(*this), 0); } iterator end() const { return iterator(const_cast<QtContainerProxy&>(*this), (_object->*Count)()); } const_iterator cbegin() const { return begin(); } const_iterator cend() const { return end(); } };
So how to use it in the example from above?
For better readability and perhaps later reuse at other places let’s define this type:
using ListWidgetProxy = QtContainerProxy<QListWidget, QListWidgetItem, &QListWidget::count, &QListWidget::item>;
And now the example becomes:
for(auto& item : ListWidgetProxy(*ui->lst_feeds)) { auto si = item->data(Qt::UserRole).value< FeedItem::SharedItem >(); if(si && si->contains(list)) item->setCheckState(Qt::Checked); }
The template might be simplified in regard of the interface. But Visual Studio 2013, the compiler I have to use currently, lags certain C++11 features.
What do you think? So feedback is welcome.
2 Responses to “How to bring Qt Widgets to STL algorithms”
Sorry, the comment form is closed at this time.
Hi,
You do not really need a proxy, just non-member begin and end functions, and you will be able to do a range-for over that container without wrapping it inside something else. You can see an example of this at [1] which provides iterators and range-for for QSqlQuery.
Cheers,
Ivan
[1] https://quickgit.kde.org/?p=kactivities.git&a=blob&h=d43cce96ce148c0a3caed012cce3b4bcc975a055&hb=e030197846a6f781264f36f8499a191be8402bf6&f=src%2Futils%2Fqsqlquery_iterator.h
The QSQLQuery solutions is an other valid one. From my point of view, it is a matter of taste, which one favors.