QtConcurrent: filter/map reduction without default ctor

Previously a default constructor was required for the result type
of mappedReduced() and filteredReduced(), even if a default value
was provided.

This patch fixes the problem.

The issue was in the ResultReporter type, that was calling
QList::resize() to adjust the size of expected reported results.
A default-value parameter was added to the class, so that
a corresponding overload of QList::resize could be invoked.

Task-number: QTBUG-88452
Change-Id: I51113753e314d76aa74d201b5a7e327a6ca75f47
Reviewed-by: Sona Kurazyan <sona.kurazyan@qt.io>
This commit is contained in:
Ivan Solovev 2020-12-01 19:22:16 +01:00
parent 0c19e3f703
commit 3d780c0d70
5 changed files with 177 additions and 34 deletions

View File

@ -160,21 +160,19 @@ template <typename ReducedResultType,
typename qValueType<Iterator>::value_type> >
class FilteredReducedKernel : public IterateKernel<Iterator, ReducedResultType>
{
ReducedResultType reducedResult;
ReducedResultType &reducedResult;
KeepFunctor keep;
ReduceFunctor reduce;
Reducer reducer;
typedef IterateKernel<Iterator, ReducedResultType> IterateKernelType;
public:
template <typename Keep = KeepFunctor, typename Reduce = ReduceFunctor>
FilteredReducedKernel(QThreadPool *pool,
Iterator begin,
Iterator end,
Keep &&_keep,
Reduce &&_reduce,
ReduceOptions reduceOption)
: IterateKernelType(pool, begin, end), reducedResult(), keep(std::forward<Keep>(_keep)),
template<typename Keep = KeepFunctor, typename Reduce = ReduceFunctor>
FilteredReducedKernel(QThreadPool *pool, Iterator begin, Iterator end, Keep &&_keep,
Reduce &&_reduce, ReduceOptions reduceOption)
: IterateKernelType(pool, begin, end),
reducedResult(this->defaultValue.value),
keep(std::forward<Keep>(_keep)),
reduce(std::forward<Reduce>(_reduce)),
reducer(pool, reduceOption)
{ }
@ -183,8 +181,8 @@ public:
FilteredReducedKernel(QThreadPool *pool, Iterator begin, Iterator end, Keep &&_keep,
Reduce &&_reduce, ReducedResultType &&initialValue,
ReduceOptions reduceOption)
: IterateKernelType(pool, begin, end),
reducedResult(std::forward<ReducedResultType>(initialValue)),
: IterateKernelType(pool, begin, end, std::forward<ReducedResultType>(initialValue)),
reducedResult(this->defaultValue.value),
keep(std::forward<Keep>(_keep)),
reduce(std::forward<Reduce>(_reduce)),
reducer(pool, reduceOption)

View File

@ -93,23 +93,22 @@ template <typename T>
class ResultReporter
{
public:
ResultReporter(ThreadEngine<T> *_threadEngine)
:threadEngine(_threadEngine)
ResultReporter(ThreadEngine<T> *_threadEngine, T &_defaultValue)
: threadEngine(_threadEngine), defaultValue(_defaultValue)
{
}
void reserveSpace(int resultCount)
{
currentResultCount = resultCount;
vector.resize(qMax(resultCount, vector.count()));
resizeList(qMax(resultCount, vector.count()));
}
void reportResults(int begin)
{
const int useVectorThreshold = 4; // Tunable parameter.
if (currentResultCount > useVectorThreshold) {
vector.resize(currentResultCount);
resizeList(currentResultCount);
threadEngine->reportResults(vector, begin);
} else {
for (int i = 0; i < currentResultCount; ++i)
@ -125,6 +124,17 @@ public:
int currentResultCount;
ThreadEngine<T> *threadEngine;
QList<T> vector;
private:
void resizeList(qsizetype size)
{
if constexpr (std::is_default_constructible_v<T>)
vector.resize(size);
else
vector.resize(size, defaultValue);
}
T &defaultValue;
};
template <>
@ -137,6 +147,22 @@ public:
inline void * getPointer() { return nullptr; }
};
template<typename T>
struct DefaultValueContainer
{
template<typename U = T>
DefaultValueContainer(U &&_value) : value(std::forward<U>(_value))
{
}
T value;
};
template<>
struct DefaultValueContainer<void>
{
};
inline bool selectIteration(std::bidirectional_iterator_tag)
{
return false; // while
@ -160,11 +186,41 @@ class IterateKernel : public ThreadEngine<T>
public:
typedef T ResultType;
template<typename U = T, std::enable_if_t<std::is_same_v<U, void>, bool> = true>
IterateKernel(QThreadPool *pool, Iterator _begin, Iterator _end)
: ThreadEngine<T>(pool), begin(_begin), end(_end), current(_begin)
, iterationCount(selectIteration(IteratorCategory()) ? std::distance(_begin, _end) : 0)
, forIteration(selectIteration(IteratorCategory()))
, progressReportingEnabled(true)
: ThreadEngine<U>(pool),
begin(_begin),
end(_end),
current(_begin),
iterationCount(selectIteration(IteratorCategory()) ? std::distance(_begin, _end) : 0),
forIteration(selectIteration(IteratorCategory())),
progressReportingEnabled(true)
{
}
template<typename U = T, std::enable_if_t<!std::is_same_v<U, void>, bool> = true>
IterateKernel(QThreadPool *pool, Iterator _begin, Iterator _end)
: ThreadEngine<U>(pool),
begin(_begin),
end(_end),
current(_begin),
iterationCount(selectIteration(IteratorCategory()) ? std::distance(_begin, _end) : 0),
forIteration(selectIteration(IteratorCategory())),
progressReportingEnabled(true),
defaultValue(U())
{
}
template<typename U = T, std::enable_if_t<!std::is_same_v<U, void>, bool> = true>
IterateKernel(QThreadPool *pool, Iterator _begin, Iterator _end, U &&_defaultValue)
: ThreadEngine<U>(pool),
begin(_begin),
end(_end),
current(_begin),
iterationCount(selectIteration(IteratorCategory()) ? std::distance(_begin, _end) : 0),
forIteration(selectIteration(IteratorCategory())),
progressReportingEnabled(true),
defaultValue(std::forward<U>(_defaultValue))
{
}
@ -199,7 +255,7 @@ public:
ThreadFunctionResult forThreadFunction()
{
BlockSizeManager blockSizeManager(ThreadEngineBase::threadPool, iterationCount);
ResultReporter<T> resultReporter(this);
ResultReporter<T> resultReporter = createResultsReporter();
for(;;) {
if (this->isCanceled())
@ -252,7 +308,7 @@ public:
if (iteratorThreads.testAndSetAcquire(0, 1) == false)
return ThreadFinished;
ResultReporter<T> resultReporter(this);
ResultReporter<T> resultReporter = createResultsReporter();
resultReporter.reserveSpace(1);
while (current != end) {
@ -283,6 +339,14 @@ public:
return ThreadFinished;
}
private:
ResultReporter<T> createResultsReporter()
{
if constexpr (!std::is_same_v<T, void>)
return ResultReporter<T>(this, defaultValue.value);
else
return ResultReporter<T>(this);
}
public:
const Iterator begin;
@ -294,6 +358,7 @@ public:
const int iterationCount;
const bool forIteration;
bool progressReportingEnabled;
DefaultValueContainer<ResultType> defaultValue;
};
} // namespace QtConcurrent

View File

@ -93,7 +93,7 @@ template <typename ReducedResultType,
QtPrivate::MapResultType<Iterator, MapFunctor>>>
class MappedReducedKernel : public IterateKernel<Iterator, ReducedResultType>
{
ReducedResultType reducedResult;
ReducedResultType &reducedResult;
MapFunctor map;
ReduceFunctor reduce;
Reducer reducer;
@ -102,20 +102,22 @@ class MappedReducedKernel : public IterateKernel<Iterator, ReducedResultType>
public:
typedef ReducedResultType ReturnType;
template <typename F1 = MapFunctor, typename F2 = ReduceFunctor>
MappedReducedKernel(QThreadPool *pool, Iterator begin, Iterator end, F1 &&_map,
F2 &&_reduce, ReduceOptions reduceOptions)
: IterateKernel<Iterator, ReducedResultType>(pool, begin, end), reducedResult(),
map(std::forward<F1>(_map)), reduce(std::forward<F2>(_reduce)),
template<typename F1 = MapFunctor, typename F2 = ReduceFunctor>
MappedReducedKernel(QThreadPool *pool, Iterator begin, Iterator end, F1 &&_map, F2 &&_reduce,
ReduceOptions reduceOptions)
: IterateKernel<Iterator, ReducedResultType>(pool, begin, end),
reducedResult(this->defaultValue.value),
map(std::forward<F1>(_map)),
reduce(std::forward<F2>(_reduce)),
reducer(pool, reduceOptions)
{ }
template <typename F1 = MapFunctor, typename F2 = ReduceFunctor>
MappedReducedKernel(QThreadPool *pool, Iterator begin, Iterator end, F1 &&_map,
F2 &&_reduce, ReducedResultType &&initialValue,
ReduceOptions reduceOptions)
: IterateKernel<Iterator, ReducedResultType>(pool, begin, end),
reducedResult(std::forward<ReducedResultType>(initialValue)),
template<typename F1 = MapFunctor, typename F2 = ReduceFunctor>
MappedReducedKernel(QThreadPool *pool, Iterator begin, Iterator end, F1 &&_map, F2 &&_reduce,
ReducedResultType &&initialValue, ReduceOptions reduceOptions)
: IterateKernel<Iterator, ReducedResultType>(pool, begin, end,
std::forward<ReducedResultType>(initialValue)),
reducedResult(this->defaultValue.value),
map(std::forward<F1>(_map)),
reduce(std::forward<F2>(_reduce)),
reducer(pool, reduceOptions)

View File

@ -294,3 +294,79 @@ void tst_QtConcurrentFilterMapGenerated::moveOnlyReductionItem()
QCOMPARE(result, expected_result);*/
}
void tst_QtConcurrentFilterMapGenerated::noDefaultConstructorItemMapped()
{
/* test for
template<typename typename ResultType, typename Sequence, typename MapFunctor, typename
ReduceFunctor, typename reductionitemtype> ResultType blockingMappedReduced(QThreadPool* pool,
const Sequence & sequence, MapFunctor function, ReduceFunctor reduceFunction, reductionitemtype
&& initialValue, ReduceOptions);
with
inputsequence=standard
inputsequencepassing=lvalue
inputitemtype=standard
maptype=same
mappeditemtype=standard
reductiontype=different
reductionitemtype=noconstruct
mapfunction=functor
mapfunctionpassing=lvalue
reductionfunction=function
reductionfunctionpassing=lvalue
reductioninitialvaluepassing=lvalue
reductionoptions=unspecified
*/
QThreadPool pool;
pool.setMaxThreadCount(1);
auto input_sequence = []() {
std::vector<SequenceItem<tag_input>> result;
result.push_back(SequenceItem<tag_input>(1, true));
result.push_back(SequenceItem<tag_input>(2, true));
result.push_back(SequenceItem<tag_input>(3, true));
result.push_back(SequenceItem<tag_input>(4, true));
result.push_back(SequenceItem<tag_input>(5, true));
result.push_back(SequenceItem<tag_input>(6, true));
return result;
}();
auto map = MyMap<SequenceItem<tag_input>, SequenceItem<tag_input>> {};
auto reductor = myReduce<SequenceItem<tag_input>, NoConstructSequenceItem<tag_reduction>>;
auto initialvalue = NoConstructSequenceItem<tag_reduction>(0, true);
auto result =
QtConcurrent::blockingMappedReduced(&pool, input_sequence, map, reductor, initialvalue);
auto expected_result = NoConstructSequenceItem<tag_reduction>(42, true);
QCOMPARE(result, expected_result);
}
void tst_QtConcurrentFilterMapGenerated::noDefaultConstructorItemFiltered()
{
QThreadPool pool;
pool.setMaxThreadCount(1);
auto input_sequence = []() {
std::vector<SequenceItem<tag_input>> result;
result.push_back(SequenceItem<tag_input>(1, true));
result.push_back(SequenceItem<tag_input>(2, true));
result.push_back(SequenceItem<tag_input>(3, true));
result.push_back(SequenceItem<tag_input>(4, true));
result.push_back(SequenceItem<tag_input>(5, true));
result.push_back(SequenceItem<tag_input>(6, true));
return result;
}();
auto filter = MyFilter<SequenceItem<tag_input>> {};
auto reductor = myReduce<SequenceItem<tag_input>, NoConstructSequenceItem<tag_reduction>>;
auto initialvalue = NoConstructSequenceItem<tag_reduction>(0, true);
auto result = QtConcurrent::blockingFilteredReduced(&pool, input_sequence, filter, reductor,
initialvalue);
auto expected_result = NoConstructSequenceItem<tag_reduction>(9, true);
QCOMPARE(result, expected_result);
}

View File

@ -44,6 +44,8 @@ private slots:
void moveOnlyReduceObject();
void functorAsReduction();
void moveOnlyReductionItem();
void noDefaultConstructorItemMapped();
void noDefaultConstructorItemFiltered();
// START_GENERATED_SLOTS (see generate_tests.py)
void test1();
// END_GENERATED_SLOTS (see generate_tests.py)