QtConcurrent与索引映射

问题描述

我想知道是否有一个选项也可以用QtConcurrent::mapped(someVector,&someFunction))(还有filterfilteredmap,...)来移交当前处理的索引>

我想要的东西:我想根据someVector中的当前索引对其进行某些处理。但是由于函数someFunction仅采用T类型,该类型也用于QVector<T> vector

我的工作:由于我需要这样做,因此我创建了QVector<std::pair<int,T>>并手动创建了元素的索引。

由于这需要更多空间,而且不是一个好的解决方案,所以我认为可能还有另一种解决方案。

文档:https://doc.qt.io/qt-5/qtconcurrent-index.html

解决方法

如果您的输入是QVector,则可以利用QVector连续存储所有元素的事实。这意味着给定对e中元素QVector v的引用,则e的索引可以通过以下方式获得:

std::ptrdiff_t idx = &e - &v.at(0);

以下是使用QtConcurrent::mapped的完整示例:

#include <iterator>
#include <numeric>
#include <type_traits>
#include <utility>

#include <QtCore>
#include <QtConcurrent>


// lambda functions are not directly usable in QtConcurrent::mapped,the
// following is a necessary workaround
// see https://stackoverflow.com/a/49821973
template <class T> struct function_traits :
        function_traits<decltype(&T::operator())> {};

template <typename ClassType,typename ReturnType,typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const> {
   // specialization for pointers to member function
   using functor_type = ClassType;
   using result_type = ReturnType;
   using arg_tuple = std::tuple<Args...>;
   static constexpr auto arity = sizeof...(Args);
};

template <class Callable,class... Args>
struct CallableWrapper : Callable,function_traits<Callable> {
   CallableWrapper(const Callable &f) : Callable(f) {}
   CallableWrapper(Callable &&f) : Callable(std::move(f)) {}
};

template <class F,std::size_t ... Is,class T>
auto wrap_impl(F &&f,std::index_sequence<Is...>,T) {
   return CallableWrapper<F,typename T::result_type,std::tuple_element_t<Is,typename T::arg_tuple>...>(std::forward<F>(f));
}

template <class F> auto wrap(F &&f) {
   using traits = function_traits<F>;
   return wrap_impl(std::forward<F>(f),std::make_index_sequence<traits::arity>{},traits{});
}

int main(int argc,char* argv[]) {
    QCoreApplication app(argc,argv);

    // a vector of numbers from 0 to 500
    QVector<int> seq(500,0);
    std::iota(seq.begin(),seq.end(),0);
    qDebug() << "input: " << seq;
    QFuture<int> mapped = QtConcurrent::mapped(seq,wrap([&seq](const int& x) {
        // the index of the element in a QVector is the difference between
        // the address of the first element in the vector and the address of
        // the current element
        std::ptrdiff_t idx = std::distance(&seq.at(0),&x);
        // we can then use x and idx however we want
        return x * idx;
    }));
    qDebug() << "output: " << mapped.results();

    QTimer::singleShot(100,&app,&QCoreApplication::quit);
    return app.exec();
}

参见this question进行相关讨论。请注意,链接的问题有一个更干净的答案,其中涉及使用zip并计算来自boost(或可能是其C ++ 20系列的对等对象)的迭代器,但是当使用QtConcurrent::map时,我认为这不太适合map将序列切成块,并将这些块分配到多个线程。