0

Consider the following code (Qt 6.0.3, C++17):

const QVector<int> arr = {1, 2, 3, 4, 5};
auto future = QtConcurrent::mappedReduced<int>(arr, [](auto item) {
    return item;
}, [](int& result, auto item) {
    result += item;
});

As you can see the first lambda expression passed to QtConcurrent::mappedReduced looks unnecessary. That's why I want to find something like QtConcurrent::reduced in Qt 6. Or how can I refactor this code to use only 1 lambda expression?

Roman Podymov
  • 4,168
  • 4
  • 30
  • 57

1 Answers1

1

You can use std::accumulate.

The goal of QtConcurrent mappedReduce is the apply an operation on all the items of container in a concurrent fashion and once done do the reduction.

In your case there's nothing to be done but the sum of the items so there's no need for mappedReduce.

Update:

Based on your comments here is an example using a custom class with the operator+ overloaded using a QList as container.

class MyClass
{
public:
    MyClass(int x=0, int y=0):
       _x(x),
       _y(y)
    {}

    int x() const { return _x; }
    int y() const { return _y; }

public:
    int _x;
    int _y;
};

MyClass operator+(const MyClass& left, const MyClass &right)
{
    MyClass m;
    m._x = left._x + right._x;
    m._y = left._y + right._y;
    return m;
}

Here is a version with std::accumulate

#include <QVector>
#include <QtDebug>
#include <numeric>

int main(int argc, char **argv)
{
    Q_UNUSED(argc);
    Q_UNUSED(argv);

    QVector<MyClass> myClassList = {MyClass(12, 42), MyClass(23, 53)};
    MyClass result = std::accumulate(myClassList.constBegin(), myClassList.constEnd(), MyClass());
    qDebug() << result.x() << result.y();
    return 0;
}

Update 2:

Based on @IlBeldus suggestion, here is the version were you use std::accumulate with QtConcurrent::run

#include <QtDebug>
#include <QVector>
#include <QtConcurrent>
#include <numeric>

MyClass myAccumulate(const QVector<MyClass> &input)
{
    return std::accumulate(input.constBegin(), input.constEnd(), MyClass());
}

int main(int argc, char **argv)
{
    Q_UNUSED(argc);
    Q_UNUSED(argv);

    QVector<MyClass> myClassList = {MyClass(12, 42), MyClass(23, 53)};
    QFuture<MyClass> future = QtConcurrent::run(myAccumulate, myClassList);
    result = future.result();
    qDebug() << result.x() << result.y();
    return 0;
}
SGaist
  • 906
  • 8
  • 33
  • 109
  • Thanks. But `arr` can have more items and `+=` is a custom `operator`, so I need something from `QtConcurrent`. – Roman Podymov May 23 '21 at 23:42
  • And `arr` can be defined as `QVector`. – Roman Podymov May 24 '21 at 09:08
  • Do you have any specific reasons to overload operator+= rather than just operator+ ? I added an update that shows an implementation with a custom class with the operator+ reimplemented. – SGaist May 24 '21 at 20:11
  • Sometimes it takes about 10 minutes to get the value. That's why I want to use something from `QtConcurrent`. I also want to pause, resume and cancel the calculation. – Roman Podymov May 25 '21 at 08:19
  • Just to be sure, you understand that the reduce function is not called concurrently ? From the documentation: Note that while mapFunction is called concurrently, only one thread at a time will call reduceFunction. The order in which reduceFunction is called is determined by reduceOptions. – SGaist May 25 '21 at 09:02
  • I understand it and currently I can click on buttons and type text while the calculation is running. How can I achieve it with `std::accumulate`? – Roman Podymov May 25 '21 at 10:00
  • 1
    You can mix `std::accumulate` with `QtConcurrent::run` to run the accumulation on a secondary thread – IlBeldus May 25 '21 at 10:06
  • @IlBeldus Sounds like a plan. Can you please add it as an answer? – Roman Podymov May 25 '21 at 12:39
  • 1
    I have updated my answer to incorporate @IlBeldus suggestion. – SGaist May 25 '21 at 15:21