Feb 042016
 

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”

  1. 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.

Sorry, the comment form is closed at this time.