forked from qt-creator/qt-creator
Utils: Remove functiontraits.h, mapreduce.h and runextensions.h
Change-Id: I61d0f95d4120c0de0045c1a817fd13a09eeb5402 Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Eike Ziller <eike.ziller@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
@@ -6,7 +6,6 @@ add_qtc_library(Utils
|
|||||||
SOURCES
|
SOURCES
|
||||||
../3rdparty/span/span.hpp
|
../3rdparty/span/span.hpp
|
||||||
../3rdparty/tl_expected/include/tl/expected.hpp
|
../3rdparty/tl_expected/include/tl/expected.hpp
|
||||||
QtConcurrentTools
|
|
||||||
algorithm.h
|
algorithm.h
|
||||||
ansiescapecodehandler.cpp ansiescapecodehandler.h
|
ansiescapecodehandler.cpp ansiescapecodehandler.h
|
||||||
appinfo.cpp appinfo.h
|
appinfo.cpp appinfo.h
|
||||||
@@ -66,7 +65,6 @@ add_qtc_library(Utils
|
|||||||
flowlayout.cpp flowlayout.h
|
flowlayout.cpp flowlayout.h
|
||||||
fsengine/fsengine.cpp fsengine/fsengine.h
|
fsengine/fsengine.cpp fsengine/fsengine.h
|
||||||
fsengine/fileiconprovider.cpp fsengine/fileiconprovider.h
|
fsengine/fileiconprovider.cpp fsengine/fileiconprovider.h
|
||||||
functiontraits.h
|
|
||||||
futuresynchronizer.cpp futuresynchronizer.h
|
futuresynchronizer.cpp futuresynchronizer.h
|
||||||
fuzzymatcher.cpp fuzzymatcher.h
|
fuzzymatcher.cpp fuzzymatcher.h
|
||||||
genericconstants.h
|
genericconstants.h
|
||||||
@@ -93,7 +91,6 @@ add_qtc_library(Utils
|
|||||||
listmodel.h
|
listmodel.h
|
||||||
listutils.h
|
listutils.h
|
||||||
macroexpander.cpp macroexpander.h
|
macroexpander.cpp macroexpander.h
|
||||||
mapreduce.h
|
|
||||||
mathutils.cpp mathutils.h
|
mathutils.cpp mathutils.h
|
||||||
mimeutils.h
|
mimeutils.h
|
||||||
minimizableinfobars.cpp
|
minimizableinfobars.cpp
|
||||||
@@ -143,7 +140,6 @@ add_qtc_library(Utils
|
|||||||
ranges.h
|
ranges.h
|
||||||
reloadpromptutils.cpp reloadpromptutils.h
|
reloadpromptutils.cpp reloadpromptutils.h
|
||||||
removefiledialog.cpp removefiledialog.h
|
removefiledialog.cpp removefiledialog.h
|
||||||
runextensions.cpp runextensions.h
|
|
||||||
savefile.cpp savefile.h
|
savefile.cpp savefile.h
|
||||||
scopedswap.h
|
scopedswap.h
|
||||||
scopedtimer.cpp scopedtimer.h
|
scopedtimer.cpp scopedtimer.h
|
||||||
|
@@ -1,4 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
#include "runextensions.h"
|
|
@@ -1,169 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
namespace Utils {
|
|
||||||
|
|
||||||
/*
|
|
||||||
struct functionTraits<Function>
|
|
||||||
{
|
|
||||||
using ResultType; // Return type of Function
|
|
||||||
struct argument<unsigned index>
|
|
||||||
{
|
|
||||||
using type; // type of Function argument at index (starting with 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct functionTakesArgument<Function, unsigned index, ArgumentType>;
|
|
||||||
|
|
||||||
Is derived from std::true_type if Function takes an argument of type ArgumentType at index.
|
|
||||||
Otherwise derived from std::false_type.
|
|
||||||
*/
|
|
||||||
|
|
||||||
////////////////////
|
|
||||||
// functionTraits
|
|
||||||
////////////////////
|
|
||||||
|
|
||||||
// for callables. defined below.
|
|
||||||
template <typename Callable>
|
|
||||||
struct functionTraits;
|
|
||||||
|
|
||||||
// function
|
|
||||||
template <typename Result, typename... Args>
|
|
||||||
struct functionTraits<Result(Args...)>
|
|
||||||
{
|
|
||||||
using ResultType = Result;
|
|
||||||
static const unsigned arity = sizeof...(Args); // TODO const -> constexpr with MSVC2015
|
|
||||||
|
|
||||||
template <unsigned i>
|
|
||||||
struct argument
|
|
||||||
{
|
|
||||||
using type = typename std::tuple_element<i, std::tuple<Args...>>::type;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// function pointer
|
|
||||||
template <typename Result, typename... Args>
|
|
||||||
struct functionTraits<Result(*)(Args...)> : public functionTraits<Result(Args...)>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
// const function pointer
|
|
||||||
template <typename Result, typename... Args>
|
|
||||||
struct functionTraits<Result(* const)(Args...)> : public functionTraits<Result(Args...)>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
// member function
|
|
||||||
template <typename Type, typename Result, typename... Args>
|
|
||||||
struct functionTraits<Result(Type::*)(Args...)> : public functionTraits<Result(Type&,Args...)>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
// const member function
|
|
||||||
template <typename Type, typename Result, typename... Args>
|
|
||||||
struct functionTraits<Result(Type::*)(Args...) const> : public functionTraits<Result(Type&,Args...)>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
// const pointer to member function
|
|
||||||
template <typename Type, typename Result, typename... Args>
|
|
||||||
struct functionTraits<Result(Type::* const)(Args...)> : public functionTraits<Result(Type&,Args...)>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
// const pointer to const member function
|
|
||||||
template <typename Type, typename Result, typename... Args>
|
|
||||||
struct functionTraits<Result(Type::* const)(Args...) const> : public functionTraits<Result(Type&,Args...)>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: enable lvalue and rvalue ref member function later (MSVC 2015?)
|
|
||||||
//// lvalue ref member function
|
|
||||||
//template <typename Type, typename Result, typename... Args>
|
|
||||||
//struct functionTraits<Result(Type::*)(Args...) &> : public functionTraits<Result(Type&,Args...)>
|
|
||||||
//{
|
|
||||||
//};
|
|
||||||
|
|
||||||
//// const lvalue ref member function
|
|
||||||
//template <typename Type, typename Result, typename... Args>
|
|
||||||
//struct functionTraits<Result(Type::*)(Args...) const &> : public functionTraits<Result(Type&,Args...)>
|
|
||||||
//{
|
|
||||||
//};
|
|
||||||
|
|
||||||
//// rvalue ref member function
|
|
||||||
//template <typename Type, typename Result, typename... Args>
|
|
||||||
//struct functionTraits<Result(Type::*)(Args...) &&> : public functionTraits<Result(Type&,Args...)>
|
|
||||||
//{
|
|
||||||
//};
|
|
||||||
|
|
||||||
// callables. only works if operator() is not overloaded.
|
|
||||||
template <typename Callable>
|
|
||||||
struct functionTraits
|
|
||||||
{
|
|
||||||
using callableTraits = functionTraits<decltype(&Callable::operator())>;
|
|
||||||
using ResultType = typename callableTraits::ResultType;
|
|
||||||
static const unsigned arity = callableTraits::arity - 1; // ignore object pointer arg // TODO const -> constexpr with MSVC2015
|
|
||||||
|
|
||||||
template <unsigned i>
|
|
||||||
struct argument
|
|
||||||
{
|
|
||||||
using type = typename callableTraits::template argument<i+1>::type; // ignore object pointer arg
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// lvalue ref callables
|
|
||||||
template <typename Callable>
|
|
||||||
struct functionTraits<Callable&> : public functionTraits<Callable>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
// const lvalue ref callables
|
|
||||||
template <typename Callable>
|
|
||||||
struct functionTraits<const Callable&> : public functionTraits<Callable>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
// rvalue ref callables
|
|
||||||
template <typename Callable>
|
|
||||||
struct functionTraits<Callable&&> : public functionTraits<Callable>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename F>
|
|
||||||
using functionResult_t = typename functionTraits<F>::ResultType;
|
|
||||||
|
|
||||||
////////////////////
|
|
||||||
// functionTakesArgument
|
|
||||||
////////////////////
|
|
||||||
namespace Internal {
|
|
||||||
|
|
||||||
template <typename HasArity/*true_type or false_type*/,
|
|
||||||
typename Function, unsigned index, typename T>
|
|
||||||
struct functionTakesArgumentArityDispatch;
|
|
||||||
|
|
||||||
template <typename Function, unsigned index, typename T>
|
|
||||||
struct functionTakesArgumentArityDispatch<std::false_type, Function, index, T>
|
|
||||||
: public std::false_type
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Function, unsigned index, typename T>
|
|
||||||
struct functionTakesArgumentArityDispatch<std::true_type, Function, index, T>
|
|
||||||
: public std::is_same<T, typename functionTraits<Function>::template argument<index>::type>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
} // Internal
|
|
||||||
|
|
||||||
template <typename Function, unsigned index, typename T>
|
|
||||||
struct functionTakesArgument : public Internal::functionTakesArgumentArityDispatch<
|
|
||||||
std::integral_constant<bool, (functionTraits<Function>::arity > index)>,
|
|
||||||
Function, index, T>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
} // Utils
|
|
@@ -1,594 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utils_global.h"
|
|
||||||
|
|
||||||
#include "algorithm.h"
|
|
||||||
#include "runextensions.h"
|
|
||||||
|
|
||||||
#include <QFutureWatcher>
|
|
||||||
|
|
||||||
#include <iterator>
|
|
||||||
|
|
||||||
namespace Utils {
|
|
||||||
|
|
||||||
enum class MapReduceOption
|
|
||||||
{
|
|
||||||
Ordered,
|
|
||||||
Unordered
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace Internal {
|
|
||||||
|
|
||||||
class QTCREATOR_UTILS_EXPORT MapReduceObject : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename ForwardIterator, typename MapResult, typename MapFunction, typename State, typename ReduceResult, typename ReduceFunction>
|
|
||||||
class MapReduceBase : public MapReduceObject
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
static const int MAX_PROGRESS = 1000000;
|
|
||||||
// either const or non-const reference wrapper for items from the iterator
|
|
||||||
using ItemReferenceWrapper = std::reference_wrapper<
|
|
||||||
std::remove_reference_t<typename std::iterator_traits<ForwardIterator>::reference>>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
MapReduceBase(QFutureInterface<ReduceResult> &futureInterface, ForwardIterator begin, ForwardIterator end,
|
|
||||||
MapFunction &&map, State &state, ReduceFunction &&reduce,
|
|
||||||
MapReduceOption option, QThreadPool *pool, int size)
|
|
||||||
: m_futureInterface(futureInterface),
|
|
||||||
m_iterator(begin),
|
|
||||||
m_end(end),
|
|
||||||
m_map(std::forward<MapFunction>(map)),
|
|
||||||
m_state(state),
|
|
||||||
m_reduce(std::forward<ReduceFunction>(reduce)),
|
|
||||||
m_threadPool(pool),
|
|
||||||
m_handleProgress(size >= 0),
|
|
||||||
m_size(size),
|
|
||||||
m_option(option)
|
|
||||||
{
|
|
||||||
if (!m_threadPool)
|
|
||||||
m_threadPool = new QThreadPool(this);
|
|
||||||
if (m_handleProgress) // progress is handled by us
|
|
||||||
m_futureInterface.setProgressRange(0, MAX_PROGRESS);
|
|
||||||
connect(&m_selfWatcher, &QFutureWatcher<void>::canceled,
|
|
||||||
this, &MapReduceBase::cancelAll);
|
|
||||||
m_selfWatcher.setFuture(QFuture<void>(futureInterface.future()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void exec()
|
|
||||||
{
|
|
||||||
// do not enter event loop for empty containers or if already canceled
|
|
||||||
if (!m_futureInterface.isCanceled() && schedule())
|
|
||||||
m_loop.exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void reduce(QFutureWatcher<MapResult> *watcher, int index) = 0;
|
|
||||||
|
|
||||||
bool schedule()
|
|
||||||
{
|
|
||||||
bool didSchedule = false;
|
|
||||||
while (m_iterator != m_end
|
|
||||||
&& m_mapWatcher.size() < std::max(m_threadPool->maxThreadCount(), 1)) {
|
|
||||||
didSchedule = true;
|
|
||||||
auto watcher = new QFutureWatcher<MapResult>();
|
|
||||||
connect(watcher, &QFutureWatcher<MapResult>::finished, this, [this, watcher] {
|
|
||||||
mapFinished(watcher);
|
|
||||||
});
|
|
||||||
if (m_handleProgress) {
|
|
||||||
connect(watcher, &QFutureWatcher<MapResult>::progressValueChanged,
|
|
||||||
this, &MapReduceBase::updateProgress);
|
|
||||||
connect(watcher, &QFutureWatcher<MapResult>::progressRangeChanged,
|
|
||||||
this, &MapReduceBase::updateProgress);
|
|
||||||
}
|
|
||||||
m_mapWatcher.append(watcher);
|
|
||||||
m_watcherIndex.append(m_currentIndex);
|
|
||||||
++m_currentIndex;
|
|
||||||
watcher->setFuture(runAsync(m_threadPool, std::cref(m_map),
|
|
||||||
ItemReferenceWrapper(*m_iterator)));
|
|
||||||
++m_iterator;
|
|
||||||
}
|
|
||||||
return didSchedule;
|
|
||||||
}
|
|
||||||
|
|
||||||
void mapFinished(QFutureWatcher<MapResult> *watcher)
|
|
||||||
{
|
|
||||||
int index = m_mapWatcher.indexOf(watcher);
|
|
||||||
int watcherIndex = m_watcherIndex.at(index);
|
|
||||||
m_mapWatcher.removeAt(index); // remove so we can schedule next one
|
|
||||||
m_watcherIndex.removeAt(index);
|
|
||||||
bool didSchedule = false;
|
|
||||||
if (!m_futureInterface.isCanceled()) {
|
|
||||||
// first schedule the next map...
|
|
||||||
didSchedule = schedule();
|
|
||||||
++m_successfullyFinishedMapCount;
|
|
||||||
updateProgress();
|
|
||||||
// ...then reduce
|
|
||||||
reduce(watcher, watcherIndex);
|
|
||||||
}
|
|
||||||
delete watcher;
|
|
||||||
if (!didSchedule && m_mapWatcher.isEmpty())
|
|
||||||
m_loop.quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateProgress()
|
|
||||||
{
|
|
||||||
if (!m_handleProgress) // cannot compute progress
|
|
||||||
return;
|
|
||||||
if (m_size == 0 || m_successfullyFinishedMapCount == m_size) {
|
|
||||||
m_futureInterface.setProgressValue(MAX_PROGRESS);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!m_futureInterface.isProgressUpdateNeeded())
|
|
||||||
return;
|
|
||||||
const double progressPerMap = MAX_PROGRESS / double(m_size);
|
|
||||||
double progress = m_successfullyFinishedMapCount * progressPerMap;
|
|
||||||
for (const QFutureWatcher<MapResult> *watcher : std::as_const(m_mapWatcher)) {
|
|
||||||
if (watcher->progressMinimum() != watcher->progressMaximum()) {
|
|
||||||
const double range = watcher->progressMaximum() - watcher->progressMinimum();
|
|
||||||
progress += (watcher->progressValue() - watcher->progressMinimum()) / range * progressPerMap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_futureInterface.setProgressValue(int(progress));
|
|
||||||
}
|
|
||||||
|
|
||||||
void cancelAll()
|
|
||||||
{
|
|
||||||
for (QFutureWatcher<MapResult> *watcher : std::as_const(m_mapWatcher))
|
|
||||||
watcher->cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
QFutureWatcher<void> m_selfWatcher;
|
|
||||||
QFutureInterface<ReduceResult> &m_futureInterface;
|
|
||||||
ForwardIterator m_iterator;
|
|
||||||
const ForwardIterator m_end;
|
|
||||||
MapFunction m_map;
|
|
||||||
State &m_state;
|
|
||||||
ReduceFunction m_reduce;
|
|
||||||
QEventLoop m_loop;
|
|
||||||
QThreadPool *m_threadPool; // for reusing threads
|
|
||||||
QList<QFutureWatcher<MapResult> *> m_mapWatcher;
|
|
||||||
QList<int> m_watcherIndex;
|
|
||||||
int m_currentIndex = 0;
|
|
||||||
const bool m_handleProgress;
|
|
||||||
const int m_size;
|
|
||||||
int m_successfullyFinishedMapCount = 0;
|
|
||||||
MapReduceOption m_option;
|
|
||||||
};
|
|
||||||
|
|
||||||
// non-void result of map function.
|
|
||||||
template <typename ForwardIterator, typename MapResult, typename MapFunction, typename State, typename ReduceResult, typename ReduceFunction>
|
|
||||||
class MapReduce : public MapReduceBase<ForwardIterator, MapResult, MapFunction, State, ReduceResult, ReduceFunction>
|
|
||||||
{
|
|
||||||
using BaseType = MapReduceBase<ForwardIterator, MapResult, MapFunction, State, ReduceResult, ReduceFunction>;
|
|
||||||
public:
|
|
||||||
MapReduce(QFutureInterface<ReduceResult> &futureInterface, ForwardIterator begin, ForwardIterator end,
|
|
||||||
MapFunction &&map, State &state, ReduceFunction &&reduce, MapReduceOption option,
|
|
||||||
QThreadPool *pool, int size)
|
|
||||||
: BaseType(futureInterface, begin, end, std::forward<MapFunction>(map), state,
|
|
||||||
std::forward<ReduceFunction>(reduce), option, pool, size)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void reduce(QFutureWatcher<MapResult> *watcher, int index) override
|
|
||||||
{
|
|
||||||
if (BaseType::m_option == MapReduceOption::Unordered) {
|
|
||||||
reduceOne(watcher->future().results());
|
|
||||||
} else {
|
|
||||||
if (m_nextIndex == index) {
|
|
||||||
// handle this result and all directly following
|
|
||||||
reduceOne(watcher->future().results());
|
|
||||||
++m_nextIndex;
|
|
||||||
while (!m_pendingResults.isEmpty() && m_pendingResults.firstKey() == m_nextIndex) {
|
|
||||||
reduceOne(m_pendingResults.take(m_nextIndex));
|
|
||||||
++m_nextIndex;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// add result to pending results
|
|
||||||
m_pendingResults.insert(index, watcher->future().results());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void reduceOne(const QList<MapResult> &results)
|
|
||||||
{
|
|
||||||
const int resultCount = results.size();
|
|
||||||
for (int i = 0; i < resultCount; ++i) {
|
|
||||||
Internal::runAsyncImpl(BaseType::m_futureInterface, BaseType::m_reduce,
|
|
||||||
BaseType::m_state, results.at(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QMap<int, QList<MapResult>> m_pendingResults;
|
|
||||||
int m_nextIndex = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// specialization for void result of map function. Reducing is a no-op.
|
|
||||||
template <typename ForwardIterator, typename MapFunction, typename State, typename ReduceResult, typename ReduceFunction>
|
|
||||||
class MapReduce<ForwardIterator, void, MapFunction, State, ReduceResult, ReduceFunction> : public MapReduceBase<ForwardIterator, void, MapFunction, State, ReduceResult, ReduceFunction>
|
|
||||||
{
|
|
||||||
using BaseType = MapReduceBase<ForwardIterator, void, MapFunction, State, ReduceResult, ReduceFunction>;
|
|
||||||
public:
|
|
||||||
MapReduce(QFutureInterface<ReduceResult> &futureInterface, ForwardIterator begin, ForwardIterator end,
|
|
||||||
MapFunction &&map, State &state, ReduceFunction &&reduce, MapReduceOption option,
|
|
||||||
QThreadPool *pool, int size)
|
|
||||||
: BaseType(futureInterface, begin, end, std::forward<MapFunction>(map), state,
|
|
||||||
std::forward<ReduceFunction>(reduce), option, pool, size)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void reduce(QFutureWatcher<void> *, int) override
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename ResultType, typename Function, typename... Args>
|
|
||||||
functionResult_t<Function>
|
|
||||||
callWithMaybeFutureInterfaceDispatch(std::false_type, QFutureInterface<ResultType> &,
|
|
||||||
Function &&function, Args&&... args)
|
|
||||||
{
|
|
||||||
return function(std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename ResultType, typename Function, typename... Args>
|
|
||||||
functionResult_t<Function>
|
|
||||||
callWithMaybeFutureInterfaceDispatch(std::true_type, QFutureInterface<ResultType> &futureInterface,
|
|
||||||
Function &&function, Args&&... args)
|
|
||||||
{
|
|
||||||
return function(futureInterface, std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename ResultType, typename Function, typename... Args>
|
|
||||||
functionResult_t<Function>
|
|
||||||
callWithMaybeFutureInterface(QFutureInterface<ResultType> &futureInterface,
|
|
||||||
Function &&function, Args&&... args)
|
|
||||||
{
|
|
||||||
return callWithMaybeFutureInterfaceDispatch(
|
|
||||||
functionTakesArgument<Function, 0, QFutureInterface<ResultType>&>(),
|
|
||||||
futureInterface, std::forward<Function>(function), std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename ForwardIterator, typename InitFunction, typename MapFunction, typename ReduceResult,
|
|
||||||
typename ReduceFunction, typename CleanUpFunction>
|
|
||||||
void blockingIteratorMapReduce(QFutureInterface<ReduceResult> &futureInterface, ForwardIterator begin, ForwardIterator end,
|
|
||||||
InitFunction &&init, MapFunction &&map,
|
|
||||||
ReduceFunction &&reduce, CleanUpFunction &&cleanup,
|
|
||||||
MapReduceOption option, QThreadPool *pool, int size)
|
|
||||||
{
|
|
||||||
auto state = callWithMaybeFutureInterface<ReduceResult, InitFunction>
|
|
||||||
(futureInterface, std::forward<InitFunction>(init));
|
|
||||||
MapReduce<ForwardIterator, typename Internal::resultType<MapFunction>::type, MapFunction, decltype(state), ReduceResult, ReduceFunction>
|
|
||||||
mr(futureInterface, begin, end, std::forward<MapFunction>(map), state,
|
|
||||||
std::forward<ReduceFunction>(reduce), option, pool, size);
|
|
||||||
mr.exec();
|
|
||||||
callWithMaybeFutureInterface<ReduceResult, CleanUpFunction, std::remove_reference_t<decltype(state)>&>
|
|
||||||
(futureInterface, std::forward<CleanUpFunction>(cleanup), state);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Container, typename InitFunction, typename MapFunction, typename ReduceResult,
|
|
||||||
typename ReduceFunction, typename CleanUpFunction>
|
|
||||||
void blockingContainerMapReduce(QFutureInterface<ReduceResult> &futureInterface, Container &&container,
|
|
||||||
InitFunction &&init, MapFunction &&map,
|
|
||||||
ReduceFunction &&reduce, CleanUpFunction &&cleanup,
|
|
||||||
MapReduceOption option, QThreadPool *pool)
|
|
||||||
{
|
|
||||||
blockingIteratorMapReduce(futureInterface, std::begin(container), std::end(container),
|
|
||||||
std::forward<InitFunction>(init), std::forward<MapFunction>(map),
|
|
||||||
std::forward<ReduceFunction>(reduce),
|
|
||||||
std::forward<CleanUpFunction>(cleanup),
|
|
||||||
option, pool, static_cast<int>(container.size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Container, typename InitFunction, typename MapFunction, typename ReduceResult,
|
|
||||||
typename ReduceFunction, typename CleanUpFunction>
|
|
||||||
void blockingContainerRefMapReduce(QFutureInterface<ReduceResult> &futureInterface,
|
|
||||||
std::reference_wrapper<Container> containerWrapper,
|
|
||||||
InitFunction &&init, MapFunction &&map,
|
|
||||||
ReduceFunction &&reduce, CleanUpFunction &&cleanup,
|
|
||||||
MapReduceOption option, QThreadPool *pool)
|
|
||||||
{
|
|
||||||
blockingContainerMapReduce(futureInterface, containerWrapper.get(),
|
|
||||||
std::forward<InitFunction>(init), std::forward<MapFunction>(map),
|
|
||||||
std::forward<ReduceFunction>(reduce),
|
|
||||||
std::forward<CleanUpFunction>(cleanup),
|
|
||||||
option, pool);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename ReduceResult>
|
|
||||||
static void *dummyInit() { return nullptr; }
|
|
||||||
|
|
||||||
// copies or moves state to member, and then moves it to the result of the call operator
|
|
||||||
template <typename State>
|
|
||||||
struct StateWrapper {
|
|
||||||
using StateResult = std::decay_t<State>; // State is const& or & for lvalues
|
|
||||||
StateWrapper(State &&state) : m_state(std::forward<State>(state)) { }
|
|
||||||
StateResult operator()()
|
|
||||||
{
|
|
||||||
return std::move(m_state); // invalidates m_state
|
|
||||||
}
|
|
||||||
|
|
||||||
StateResult m_state;
|
|
||||||
};
|
|
||||||
|
|
||||||
// copies or moves reduce function to member, calls the reduce function with state and mapped value
|
|
||||||
template <typename StateResult, typename MapResult, typename ReduceFunction>
|
|
||||||
struct ReduceWrapper {
|
|
||||||
using Reduce = std::decay_t<ReduceFunction>;
|
|
||||||
ReduceWrapper(ReduceFunction &&reduce) : m_reduce(std::forward<ReduceFunction>(reduce)) { }
|
|
||||||
void operator()(QFutureInterface<StateResult> &, StateResult &state, const MapResult &mapResult)
|
|
||||||
{
|
|
||||||
m_reduce(state, mapResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
Reduce m_reduce;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename MapResult>
|
|
||||||
struct DummyReduce {
|
|
||||||
MapResult operator()(void *, const MapResult &result) const { return result; }
|
|
||||||
};
|
|
||||||
template <>
|
|
||||||
struct DummyReduce<void> {
|
|
||||||
void operator()() const { } // needed for resultType<DummyReduce> with MSVC2013
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename ReduceResult>
|
|
||||||
static void dummyCleanup(void *) { }
|
|
||||||
|
|
||||||
template <typename StateResult>
|
|
||||||
static void cleanupReportingState(QFutureInterface<StateResult> &fi, StateResult &state)
|
|
||||||
{
|
|
||||||
fi.reportResult(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // Internal
|
|
||||||
|
|
||||||
template <typename ForwardIterator, typename InitFunction, typename MapFunction,
|
|
||||||
typename ReduceFunction, typename CleanUpFunction,
|
|
||||||
typename ReduceResult = typename Internal::resultType<ReduceFunction>::type>
|
|
||||||
QFuture<ReduceResult>
|
|
||||||
mapReduce(ForwardIterator begin, ForwardIterator end, InitFunction &&init, MapFunction &&map,
|
|
||||||
ReduceFunction &&reduce, CleanUpFunction &&cleanup,
|
|
||||||
MapReduceOption option = MapReduceOption::Unordered,
|
|
||||||
QThreadPool *pool = nullptr, QThread::Priority priority = QThread::InheritPriority,
|
|
||||||
int size = -1)
|
|
||||||
{
|
|
||||||
return runAsync(priority,
|
|
||||||
Internal::blockingIteratorMapReduce<
|
|
||||||
ForwardIterator,
|
|
||||||
std::decay_t<InitFunction>,
|
|
||||||
std::decay_t<MapFunction>,
|
|
||||||
std::decay_t<ReduceResult>,
|
|
||||||
std::decay_t<ReduceFunction>,
|
|
||||||
std::decay_t<CleanUpFunction>>,
|
|
||||||
begin, end, std::forward<InitFunction>(init), std::forward<MapFunction>(map),
|
|
||||||
std::forward<ReduceFunction>(reduce), std::forward<CleanUpFunction>(cleanup),
|
|
||||||
option, pool, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Calls the map function on all items in \a container in parallel through Utils::runAsync.
|
|
||||||
|
|
||||||
The reduce function is called in the mapReduce thread with each of the reported results from
|
|
||||||
the map function, in arbitrary order, but never in parallel.
|
|
||||||
It gets passed a reference to a user defined state object, and a result from the map function.
|
|
||||||
If it takes a QFutureInterface reference as its first argument, it can report results
|
|
||||||
for the mapReduce operation through that. Otherwise, any values returned by the reduce function
|
|
||||||
are reported as results of the mapReduce operation.
|
|
||||||
|
|
||||||
The init function is called in the mapReduce thread before the actual mapping starts,
|
|
||||||
and must return the initial state object for the reduce function. It gets the QFutureInterface
|
|
||||||
of the mapReduce operation passed as an argument.
|
|
||||||
|
|
||||||
The cleanup function is called in the mapReduce thread after all map and reduce calls have
|
|
||||||
finished, with the QFutureInterface of the mapReduce operation and the final state object
|
|
||||||
as arguments, and can be used to clean up any resources, or report a final result of the
|
|
||||||
mapReduce.
|
|
||||||
|
|
||||||
Container<ItemType>
|
|
||||||
|
|
||||||
StateType InitFunction(QFutureInterface<ReduceResultType>&)
|
|
||||||
or
|
|
||||||
StateType InitFunction()
|
|
||||||
|
|
||||||
void MapFunction(QFutureInterface<MapResultType>&, const ItemType&)
|
|
||||||
or
|
|
||||||
MapResultType MapFunction(const ItempType&)
|
|
||||||
|
|
||||||
void ReduceFunction(QFutureInterface<ReduceResultType>&, StateType&, const MapResultType&)
|
|
||||||
or
|
|
||||||
ReduceResultType ReduceFunction(StateType&, const MapResultType&)
|
|
||||||
|
|
||||||
void CleanUpFunction(QFutureInterface<ReduceResultType>&, StateType&)
|
|
||||||
or
|
|
||||||
void CleanUpFunction(StateType&)
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
\list
|
|
||||||
\li Container can be a move-only type or a temporary. If it is a lvalue reference, it will
|
|
||||||
be copied to the mapReduce thread. You can avoid that by using
|
|
||||||
the version that takes iterators, or by using std::ref/cref to pass a reference_wrapper.
|
|
||||||
\li ItemType can be a move-only type, if the map function takes (const) references to ItemType.
|
|
||||||
\li StateType can be a move-only type.
|
|
||||||
\li The init, map, reduce and cleanup functions can be move-only types and are moved to the
|
|
||||||
mapReduce thread if they are rvalues.
|
|
||||||
\endlist
|
|
||||||
|
|
||||||
*/
|
|
||||||
template <typename Container, typename InitFunction, typename MapFunction,
|
|
||||||
typename ReduceFunction, typename CleanUpFunction,
|
|
||||||
typename ReduceResult = typename Internal::resultType<ReduceFunction>::type>
|
|
||||||
QFuture<ReduceResult>
|
|
||||||
mapReduce(Container &&container, InitFunction &&init, MapFunction &&map,
|
|
||||||
ReduceFunction &&reduce, CleanUpFunction &&cleanup,
|
|
||||||
MapReduceOption option = MapReduceOption::Unordered,
|
|
||||||
QThreadPool *pool = nullptr, QThread::Priority priority = QThread::InheritPriority)
|
|
||||||
{
|
|
||||||
return runAsync(priority,
|
|
||||||
Internal::blockingContainerMapReduce<
|
|
||||||
std::decay_t<Container>,
|
|
||||||
std::decay_t<InitFunction>,
|
|
||||||
std::decay_t<MapFunction>,
|
|
||||||
std::decay_t<ReduceResult>,
|
|
||||||
std::decay_t<ReduceFunction>,
|
|
||||||
std::decay_t<CleanUpFunction>>,
|
|
||||||
std::forward<Container>(container),
|
|
||||||
std::forward<InitFunction>(init), std::forward<MapFunction>(map),
|
|
||||||
std::forward<ReduceFunction>(reduce), std::forward<CleanUpFunction>(cleanup),
|
|
||||||
option, pool);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Container, typename InitFunction, typename MapFunction,
|
|
||||||
typename ReduceFunction, typename CleanUpFunction,
|
|
||||||
typename ReduceResult = typename Internal::resultType<ReduceFunction>::type>
|
|
||||||
QFuture<ReduceResult>
|
|
||||||
mapReduce(std::reference_wrapper<Container> containerWrapper, InitFunction &&init, MapFunction &&map,
|
|
||||||
ReduceFunction &&reduce, CleanUpFunction &&cleanup,
|
|
||||||
MapReduceOption option = MapReduceOption::Unordered,
|
|
||||||
QThreadPool *pool = nullptr, QThread::Priority priority = QThread::InheritPriority)
|
|
||||||
{
|
|
||||||
return runAsync(priority,
|
|
||||||
Internal::blockingContainerRefMapReduce<
|
|
||||||
Container,
|
|
||||||
std::decay_t<InitFunction>,
|
|
||||||
std::decay_t<MapFunction>,
|
|
||||||
std::decay_t<ReduceResult>,
|
|
||||||
std::decay_t<ReduceFunction>,
|
|
||||||
std::decay_t<CleanUpFunction>>,
|
|
||||||
containerWrapper,
|
|
||||||
std::forward<InitFunction>(init), std::forward<MapFunction>(map),
|
|
||||||
std::forward<ReduceFunction>(reduce), std::forward<CleanUpFunction>(cleanup),
|
|
||||||
option, pool);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename ForwardIterator, typename MapFunction, typename State, typename ReduceFunction,
|
|
||||||
typename StateResult = std::decay_t<State>, // State = T& or const T& for lvalues, so decay that away
|
|
||||||
typename MapResult = typename Internal::resultType<MapFunction>::type>
|
|
||||||
QFuture<StateResult>
|
|
||||||
mapReduce(ForwardIterator begin, ForwardIterator end, MapFunction &&map, State &&initialState,
|
|
||||||
ReduceFunction &&reduce, MapReduceOption option = MapReduceOption::Unordered,
|
|
||||||
QThreadPool *pool = nullptr, QThread::Priority priority = QThread::InheritPriority,
|
|
||||||
int size = -1)
|
|
||||||
{
|
|
||||||
return mapReduce(begin, end,
|
|
||||||
Internal::StateWrapper<State>(std::forward<State>(initialState)),
|
|
||||||
std::forward<MapFunction>(map),
|
|
||||||
Internal::ReduceWrapper<StateResult, MapResult, ReduceFunction>(std::forward<ReduceFunction>(reduce)),
|
|
||||||
&Internal::cleanupReportingState<StateResult>,
|
|
||||||
option, pool, priority, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Container, typename MapFunction, typename State, typename ReduceFunction,
|
|
||||||
typename StateResult = std::decay_t<State>, // State = T& or const T& for lvalues, so decay that away
|
|
||||||
typename MapResult = typename Internal::resultType<MapFunction>::type>
|
|
||||||
QFuture<StateResult>
|
|
||||||
mapReduce(Container &&container, MapFunction &&map, State &&initialState, ReduceFunction &&reduce,
|
|
||||||
MapReduceOption option = MapReduceOption::Unordered,
|
|
||||||
QThreadPool *pool = nullptr, QThread::Priority priority = QThread::InheritPriority)
|
|
||||||
{
|
|
||||||
return mapReduce(std::forward<Container>(container),
|
|
||||||
Internal::StateWrapper<State>(std::forward<State>(initialState)),
|
|
||||||
std::forward<MapFunction>(map),
|
|
||||||
Internal::ReduceWrapper<StateResult, MapResult, ReduceFunction>(std::forward<ReduceFunction>(reduce)),
|
|
||||||
&Internal::cleanupReportingState<StateResult>,
|
|
||||||
option, pool, priority);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename ForwardIterator, typename MapFunction, typename State, typename ReduceFunction,
|
|
||||||
typename StateResult = std::decay_t<State>, // State = T& or const T& for lvalues, so decay that away
|
|
||||||
typename MapResult = typename Internal::resultType<MapFunction>::type>
|
|
||||||
Q_REQUIRED_RESULT
|
|
||||||
StateResult
|
|
||||||
mappedReduced(ForwardIterator begin, ForwardIterator end, MapFunction &&map, State &&initialState,
|
|
||||||
ReduceFunction &&reduce, MapReduceOption option = MapReduceOption::Unordered,
|
|
||||||
QThreadPool *pool = nullptr, QThread::Priority priority = QThread::InheritPriority,
|
|
||||||
int size = -1)
|
|
||||||
{
|
|
||||||
return mapReduce(begin, end,
|
|
||||||
std::forward<MapFunction>(map), std::forward<State>(initialState),
|
|
||||||
std::forward<ReduceFunction>(reduce),
|
|
||||||
option, pool, priority, size).result();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Container, typename MapFunction, typename State, typename ReduceFunction,
|
|
||||||
typename StateResult = std::decay_t<State>, // State = T& or const T& for lvalues, so decay that away
|
|
||||||
typename MapResult = typename Internal::resultType<MapFunction>::type>
|
|
||||||
Q_REQUIRED_RESULT
|
|
||||||
StateResult
|
|
||||||
mappedReduced(Container &&container, MapFunction &&map, State &&initialState, ReduceFunction &&reduce,
|
|
||||||
MapReduceOption option = MapReduceOption::Unordered,
|
|
||||||
QThreadPool *pool = nullptr, QThread::Priority priority = QThread::InheritPriority)
|
|
||||||
{
|
|
||||||
return mapReduce(std::forward<Container>(container), std::forward<MapFunction>(map),
|
|
||||||
std::forward<State>(initialState), std::forward<ReduceFunction>(reduce),
|
|
||||||
option, pool, priority).result();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename ForwardIterator, typename MapFunction,
|
|
||||||
typename MapResult = typename Internal::resultType<MapFunction>::type>
|
|
||||||
QFuture<MapResult>
|
|
||||||
map(ForwardIterator begin, ForwardIterator end, MapFunction &&map,
|
|
||||||
MapReduceOption option = MapReduceOption::Ordered,
|
|
||||||
QThreadPool *pool = nullptr, QThread::Priority priority = QThread::InheritPriority,
|
|
||||||
int size = -1)
|
|
||||||
{
|
|
||||||
return mapReduce(begin, end,
|
|
||||||
&Internal::dummyInit<MapResult>,
|
|
||||||
std::forward<MapFunction>(map),
|
|
||||||
Internal::DummyReduce<MapResult>(),
|
|
||||||
&Internal::dummyCleanup<MapResult>,
|
|
||||||
option, pool, priority, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Container, typename MapFunction,
|
|
||||||
typename MapResult = typename Internal::resultType<MapFunction>::type>
|
|
||||||
QFuture<MapResult>
|
|
||||||
map(Container &&container, MapFunction &&map, MapReduceOption option = MapReduceOption::Ordered,
|
|
||||||
QThreadPool *pool = nullptr, QThread::Priority priority = QThread::InheritPriority)
|
|
||||||
{
|
|
||||||
return mapReduce(std::forward<Container>(container),
|
|
||||||
Internal::dummyInit<MapResult>,
|
|
||||||
std::forward<MapFunction>(map),
|
|
||||||
Internal::DummyReduce<MapResult>(),
|
|
||||||
Internal::dummyCleanup<MapResult>,
|
|
||||||
option, pool, priority);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <template<typename> class ResultContainer, typename ForwardIterator, typename MapFunction,
|
|
||||||
typename MapResult = typename Internal::resultType<MapFunction>::type>
|
|
||||||
Q_REQUIRED_RESULT
|
|
||||||
ResultContainer<MapResult>
|
|
||||||
mapped(ForwardIterator begin, ForwardIterator end, MapFunction &&mapFun,
|
|
||||||
MapReduceOption option = MapReduceOption::Ordered,
|
|
||||||
QThreadPool *pool = nullptr, QThread::Priority priority = QThread::InheritPriority, int size = -1)
|
|
||||||
{
|
|
||||||
return Utils::transform<ResultContainer>(map(begin, end,
|
|
||||||
std::forward<MapFunction>(mapFun),
|
|
||||||
option, pool, priority, size).results(),
|
|
||||||
[](const MapResult &r) { return r; });
|
|
||||||
}
|
|
||||||
|
|
||||||
template <template<typename> class ResultContainer, typename Container, typename MapFunction,
|
|
||||||
typename MapResult = typename Internal::resultType<MapFunction>::type>
|
|
||||||
Q_REQUIRED_RESULT
|
|
||||||
ResultContainer<MapResult>
|
|
||||||
mapped(Container &&container, MapFunction &&mapFun,
|
|
||||||
MapReduceOption option = MapReduceOption::Ordered,
|
|
||||||
QThreadPool *pool = nullptr, QThread::Priority priority = QThread::InheritPriority)
|
|
||||||
{
|
|
||||||
return Utils::transform<ResultContainer>(map(container,
|
|
||||||
std::forward<MapFunction>(mapFun),
|
|
||||||
option, pool, priority).results(),
|
|
||||||
[](const MapResult &r) { return r; });
|
|
||||||
}
|
|
||||||
|
|
||||||
} // Utils
|
|
@@ -1,23 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
#include "runextensions.h"
|
|
||||||
|
|
||||||
namespace Utils {
|
|
||||||
namespace Internal {
|
|
||||||
|
|
||||||
RunnableThread::RunnableThread(QRunnable *runnable, QObject *parent)
|
|
||||||
: QThread(parent),
|
|
||||||
m_runnable(runnable)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void RunnableThread::run()
|
|
||||||
{
|
|
||||||
m_runnable->run();
|
|
||||||
if (m_runnable->autoDelete())
|
|
||||||
delete m_runnable;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // Internal
|
|
||||||
} // Utils
|
|
@@ -1,479 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utils_global.h"
|
|
||||||
|
|
||||||
#include "functiontraits.h"
|
|
||||||
|
|
||||||
#include <QCoreApplication>
|
|
||||||
#include <QFuture>
|
|
||||||
#include <QFutureInterface>
|
|
||||||
#include <QFutureWatcher>
|
|
||||||
#include <QRunnable>
|
|
||||||
#include <QThread>
|
|
||||||
#include <QThreadPool>
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
// hasCallOperator & Co must be outside of any namespace
|
|
||||||
// because of internal compiler error with MSVC2015 Update 2
|
|
||||||
|
|
||||||
using testCallOperatorYes = char;
|
|
||||||
using testCallOperatorNo = struct { char foo[2]; };
|
|
||||||
|
|
||||||
template<typename C>
|
|
||||||
static testCallOperatorYes testCallOperator(decltype(&C::operator()));
|
|
||||||
|
|
||||||
template<typename>
|
|
||||||
static testCallOperatorNo testCallOperator(...);
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct hasCallOperator
|
|
||||||
{
|
|
||||||
static const bool value = (sizeof(testCallOperator<T>(nullptr)) == sizeof(testCallOperatorYes));
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace Utils {
|
|
||||||
|
|
||||||
namespace Internal {
|
|
||||||
|
|
||||||
/*
|
|
||||||
resultType<F>::type
|
|
||||||
|
|
||||||
Returns the type of results that would be reported by a callable of type F
|
|
||||||
when called through the runAsync methods. I.e. the ResultType in
|
|
||||||
|
|
||||||
void f(QFutureInterface<Result> &fi, ...)
|
|
||||||
ResultType f(...)
|
|
||||||
|
|
||||||
Returns void if F is not callable, and if F is a callable that does not take
|
|
||||||
a QFutureInterface& as its first parameter and returns void.
|
|
||||||
*/
|
|
||||||
|
|
||||||
template <typename Function>
|
|
||||||
struct resultType;
|
|
||||||
|
|
||||||
template <typename Function, typename Arg>
|
|
||||||
struct resultTypeWithArgument;
|
|
||||||
|
|
||||||
template <typename Function, int index, bool>
|
|
||||||
struct resultTypeTakesArguments;
|
|
||||||
|
|
||||||
template <typename Function, bool>
|
|
||||||
struct resultTypeIsMemberFunction;
|
|
||||||
|
|
||||||
template <typename Function, bool>
|
|
||||||
struct resultTypeIsFunctionLike;
|
|
||||||
|
|
||||||
template <typename Function, bool>
|
|
||||||
struct resultTypeHasCallOperator;
|
|
||||||
|
|
||||||
template <typename Function, typename ResultType>
|
|
||||||
struct resultTypeWithArgument<Function, QFutureInterface<ResultType>&>
|
|
||||||
{
|
|
||||||
using type = ResultType;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Function, typename Arg>
|
|
||||||
struct resultTypeWithArgument
|
|
||||||
{
|
|
||||||
using type = functionResult_t<Function>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Function, int index>
|
|
||||||
struct resultTypeTakesArguments<Function, index, true>
|
|
||||||
: public resultTypeWithArgument<Function, typename functionTraits<Function>::template argument<index>::type>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Function, int index>
|
|
||||||
struct resultTypeTakesArguments<Function, index, false>
|
|
||||||
{
|
|
||||||
using type = functionResult_t<Function>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Function>
|
|
||||||
struct resultTypeIsFunctionLike<Function, true>
|
|
||||||
: public resultTypeTakesArguments<Function, 0, (functionTraits<Function>::arity > 0)>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Function>
|
|
||||||
struct resultTypeIsMemberFunction<Function, true>
|
|
||||||
: public resultTypeTakesArguments<Function, 1, (functionTraits<Function>::arity > 1)>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Function>
|
|
||||||
struct resultTypeIsMemberFunction<Function, false>
|
|
||||||
{
|
|
||||||
using type = void;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Function>
|
|
||||||
struct resultTypeIsFunctionLike<Function, false>
|
|
||||||
: public resultTypeIsMemberFunction<Function, std::is_member_function_pointer<Function>::value>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Function>
|
|
||||||
struct resultTypeHasCallOperator<Function, false>
|
|
||||||
: public resultTypeIsFunctionLike<Function, std::is_function<std::remove_pointer_t<std::decay_t<Function>>>::value>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Callable>
|
|
||||||
struct resultTypeHasCallOperator<Callable, true>
|
|
||||||
: public resultTypeTakesArguments<Callable, 0, (functionTraits<Callable>::arity > 0)>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Function>
|
|
||||||
struct resultType
|
|
||||||
: public resultTypeHasCallOperator<Function, hasCallOperator<Function>::value>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Function>
|
|
||||||
struct resultType<Function&> : public resultType<Function>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Function>
|
|
||||||
struct resultType<const Function&> : public resultType<Function>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Function>
|
|
||||||
struct resultType<Function &&> : public resultType<Function>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Function>
|
|
||||||
struct resultType<std::reference_wrapper<Function>> : public resultType<Function>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
template <typename Function>
|
|
||||||
struct resultType<std::reference_wrapper<const Function>> : public resultType<Function>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Callable object that wraps a member function pointer with the object it
|
|
||||||
will be called on.
|
|
||||||
*/
|
|
||||||
|
|
||||||
template <typename Function>
|
|
||||||
class MemberCallable;
|
|
||||||
|
|
||||||
template <typename Result, typename Obj, typename... Args>
|
|
||||||
class MemberCallable<Result(Obj::*)(Args...) const>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MemberCallable(Result(Obj::* function)(Args...) const, const Obj *obj)
|
|
||||||
: m_function(function),
|
|
||||||
m_obj(obj)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Result operator()(Args&&... args) const
|
|
||||||
{
|
|
||||||
return ((*m_obj).*m_function)(std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Result(Obj::* m_function)(Args...) const;
|
|
||||||
const Obj *m_obj;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Result, typename Obj, typename... Args>
|
|
||||||
class MemberCallable<Result(Obj::*)(Args...)>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MemberCallable(Result(Obj::* function)(Args...), Obj *obj)
|
|
||||||
: m_function(function),
|
|
||||||
m_obj(obj)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Result operator()(Args&&... args) const
|
|
||||||
{
|
|
||||||
return ((*m_obj).*m_function)(std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Result(Obj::* m_function)(Args...);
|
|
||||||
Obj *m_obj;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Helper functions for runAsync that run in the started thread.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// void function that does not take QFutureInterface
|
|
||||||
template <typename ResultType, typename Function, typename... Args>
|
|
||||||
void runAsyncReturnVoidDispatch(std::true_type, QFutureInterface<ResultType> &, Function &&function, Args&&... args)
|
|
||||||
{
|
|
||||||
function(std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
// non-void function that does not take QFutureInterface
|
|
||||||
template <typename ResultType, typename Function, typename... Args>
|
|
||||||
void runAsyncReturnVoidDispatch(std::false_type, QFutureInterface<ResultType> &futureInterface, Function &&function, Args&&... args)
|
|
||||||
{
|
|
||||||
futureInterface.reportResult(function(std::forward<Args>(args)...));
|
|
||||||
}
|
|
||||||
|
|
||||||
// function that takes QFutureInterface
|
|
||||||
template <typename ResultType, typename Function, typename... Args>
|
|
||||||
void runAsyncQFutureInterfaceDispatch(std::true_type, QFutureInterface<ResultType> &futureInterface, Function &&function, Args&&... args)
|
|
||||||
{
|
|
||||||
function(futureInterface, std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
// function that does not take QFutureInterface
|
|
||||||
template <typename ResultType, typename Function, typename... Args>
|
|
||||||
void runAsyncQFutureInterfaceDispatch(std::false_type, QFutureInterface<ResultType> &futureInterface, Function &&function, Args&&... args)
|
|
||||||
{
|
|
||||||
runAsyncReturnVoidDispatch(std::is_void<std::invoke_result_t<Function, Args...>>(),
|
|
||||||
futureInterface, std::forward<Function>(function), std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
// function, function pointer, or other callable object that is no member pointer
|
|
||||||
template <typename ResultType, typename Function, typename... Args,
|
|
||||||
typename = std::enable_if_t<!std::is_member_pointer<std::decay_t<Function>>::value>
|
|
||||||
>
|
|
||||||
void runAsyncMemberDispatch(QFutureInterface<ResultType> &futureInterface, Function &&function, Args&&... args)
|
|
||||||
{
|
|
||||||
runAsyncQFutureInterfaceDispatch(functionTakesArgument<Function, 0, QFutureInterface<ResultType>&>(),
|
|
||||||
futureInterface, std::forward<Function>(function), std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function = member function
|
|
||||||
template <typename ResultType, typename Function, typename Obj, typename... Args,
|
|
||||||
typename = std::enable_if_t<std::is_member_pointer<std::decay_t<Function>>::value>
|
|
||||||
>
|
|
||||||
void runAsyncMemberDispatch(QFutureInterface<ResultType> &futureInterface, Function &&function, Obj &&obj, Args&&... args)
|
|
||||||
{
|
|
||||||
// Wrap member function with object into callable
|
|
||||||
runAsyncImpl(futureInterface,
|
|
||||||
MemberCallable<std::decay_t<Function>>(std::forward<Function>(function), std::forward<Obj>(obj)),
|
|
||||||
std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
// cref to function/callable
|
|
||||||
template <typename ResultType, typename Function, typename... Args>
|
|
||||||
void runAsyncImpl(QFutureInterface<ResultType> &futureInterface,
|
|
||||||
std::reference_wrapper<Function> functionWrapper, Args&&... args)
|
|
||||||
{
|
|
||||||
runAsyncMemberDispatch(futureInterface, functionWrapper.get(), std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
// function/callable, no cref
|
|
||||||
template <typename ResultType, typename Function, typename... Args>
|
|
||||||
void runAsyncImpl(QFutureInterface<ResultType> &futureInterface,
|
|
||||||
Function &&function, Args&&... args)
|
|
||||||
{
|
|
||||||
runAsyncMemberDispatch(futureInterface, std::forward<Function>(function),
|
|
||||||
std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
AsyncJob is a QRunnable that wraps a function with the
|
|
||||||
arguments that are passed to it when it is run in a thread.
|
|
||||||
*/
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
std::decay_t<T>
|
|
||||||
decayCopy(T&& v)
|
|
||||||
{
|
|
||||||
return std::forward<T>(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename ResultType, typename Function, typename... Args>
|
|
||||||
class AsyncJob : public QRunnable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
AsyncJob(Function &&function, Args&&... args)
|
|
||||||
// decay copy like std::thread
|
|
||||||
: data(decayCopy(std::forward<Function>(function)), decayCopy(std::forward<Args>(args))...)
|
|
||||||
{
|
|
||||||
// we need to report it as started even though it isn't yet, because someone might
|
|
||||||
// call waitForFinished on the future, which does _not_ block if the future is not started
|
|
||||||
futureInterface.setRunnable(this);
|
|
||||||
futureInterface.reportStarted();
|
|
||||||
}
|
|
||||||
|
|
||||||
~AsyncJob() override
|
|
||||||
{
|
|
||||||
// QThreadPool can delete runnables even if they were never run (e.g. QThreadPool::clear).
|
|
||||||
// Since we reported them as started, we make sure that we always report them as finished.
|
|
||||||
// reportFinished only actually sends the signal if it wasn't already finished.
|
|
||||||
futureInterface.reportFinished();
|
|
||||||
}
|
|
||||||
|
|
||||||
QFuture<ResultType> future() { return futureInterface.future(); }
|
|
||||||
|
|
||||||
void run() override
|
|
||||||
{
|
|
||||||
if (priority != QThread::InheritPriority)
|
|
||||||
if (QThread *thread = QThread::currentThread())
|
|
||||||
if (thread != qApp->thread())
|
|
||||||
thread->setPriority(priority);
|
|
||||||
if (futureInterface.isCanceled()) {
|
|
||||||
futureInterface.reportFinished();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
runHelper(std::make_index_sequence<std::tuple_size<Data>::value>());
|
|
||||||
}
|
|
||||||
|
|
||||||
void setThreadPool(QThreadPool *pool)
|
|
||||||
{
|
|
||||||
futureInterface.setThreadPool(pool);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setThreadPriority(QThread::Priority p)
|
|
||||||
{
|
|
||||||
priority = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
using Data = std::tuple<std::decay_t<Function>, std::decay_t<Args>...>;
|
|
||||||
|
|
||||||
template <std::size_t... index>
|
|
||||||
void runHelper(std::index_sequence<index...>)
|
|
||||||
{
|
|
||||||
// invalidates data, which is moved into the call
|
|
||||||
runAsyncImpl(futureInterface, std::move(std::get<index>(data))...);
|
|
||||||
if (futureInterface.isPaused())
|
|
||||||
futureInterface.waitForResume();
|
|
||||||
futureInterface.reportFinished();
|
|
||||||
}
|
|
||||||
|
|
||||||
Data data;
|
|
||||||
QFutureInterface<ResultType> futureInterface;
|
|
||||||
QThread::Priority priority = QThread::InheritPriority;
|
|
||||||
};
|
|
||||||
|
|
||||||
class QTCREATOR_UTILS_EXPORT RunnableThread : public QThread
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit RunnableThread(QRunnable *runnable, QObject *parent = nullptr);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void run() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QRunnable *m_runnable;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename Function,
|
|
||||||
typename... Args,
|
|
||||||
typename ResultType = typename Internal::resultType<Function>::type>
|
|
||||||
QFuture<ResultType> runAsync_internal(QThreadPool *pool,
|
|
||||||
QThread::Priority priority,
|
|
||||||
Function &&function,
|
|
||||||
Args &&... args)
|
|
||||||
{
|
|
||||||
auto job = new Internal::AsyncJob<ResultType,Function,Args...>
|
|
||||||
(std::forward<Function>(function), std::forward<Args>(args)...);
|
|
||||||
job->setThreadPriority(priority);
|
|
||||||
QFuture<ResultType> future = job->future();
|
|
||||||
if (pool) {
|
|
||||||
job->setThreadPool(pool);
|
|
||||||
pool->start(job);
|
|
||||||
} else {
|
|
||||||
auto thread = new Internal::RunnableThread(job);
|
|
||||||
thread->moveToThread(qApp->thread()); // make sure thread gets deleteLater on main thread
|
|
||||||
QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater);
|
|
||||||
thread->start(priority);
|
|
||||||
}
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // Internal
|
|
||||||
|
|
||||||
/*!
|
|
||||||
The interface of \c {runAsync} is similar to the std::thread constructor and \c {std::invoke}.
|
|
||||||
|
|
||||||
The \a function argument can be a member function,
|
|
||||||
an object with \c {operator()} (with no overloads),
|
|
||||||
a \c {std::function}, lambda, function pointer or function reference.
|
|
||||||
The \a args are passed to the function call after they are copied/moved to the thread.
|
|
||||||
|
|
||||||
The \a function can take a \c {QFutureInterface<ResultType>&} as its first argument, followed by
|
|
||||||
other custom arguments which need to be passed to this function.
|
|
||||||
If it does not take a \c {QFutureInterface<ResultType>&} as its first argument
|
|
||||||
and its return type is not void, the function call's result is reported to the QFuture.
|
|
||||||
If \a function is a (non-static) member function, the first argument in \a args is expected
|
|
||||||
to be the object that the function is called on.
|
|
||||||
|
|
||||||
If a thread \a pool is given, the function is run there. Otherwise a new, independent thread
|
|
||||||
is started.
|
|
||||||
|
|
||||||
\sa std::thread
|
|
||||||
\sa std::invoke
|
|
||||||
\sa QThreadPool
|
|
||||||
\sa QThread::Priority
|
|
||||||
*/
|
|
||||||
template <typename Function, typename... Args,
|
|
||||||
typename ResultType = typename Internal::resultType<Function>::type>
|
|
||||||
QFuture<ResultType>
|
|
||||||
runAsync(QThreadPool *pool, QThread::Priority priority, Function &&function, Args&&... args)
|
|
||||||
{
|
|
||||||
return Internal::runAsync_internal(pool,
|
|
||||||
priority,
|
|
||||||
std::forward<Function>(function),
|
|
||||||
std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Runs \a function with \a args in a new thread with given thread \a priority.
|
|
||||||
\sa runAsync(QThreadPool*,QThread::Priority,Function&&,Args&&...)
|
|
||||||
\sa QThread::Priority
|
|
||||||
*/
|
|
||||||
template <typename Function, typename... Args,
|
|
||||||
typename ResultType = typename Internal::resultType<Function>::type>
|
|
||||||
QFuture<ResultType>
|
|
||||||
runAsync(QThread::Priority priority, Function &&function, Args&&... args)
|
|
||||||
{
|
|
||||||
return runAsync(static_cast<QThreadPool *>(nullptr), priority,
|
|
||||||
std::forward<Function>(function), std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Runs \a function with \a args in a new thread with thread priority QThread::InheritPriority.
|
|
||||||
\sa runAsync(QThreadPool*,QThread::Priority,Function&&,Args&&...)
|
|
||||||
\sa QThread::Priority
|
|
||||||
*/
|
|
||||||
template <typename Function, typename... Args,
|
|
||||||
typename = std::enable_if_t<
|
|
||||||
!std::is_same<std::decay_t<Function>, QThreadPool>::value
|
|
||||||
&& !std::is_same<std::decay_t<Function>, QThread::Priority>::value
|
|
||||||
>,
|
|
||||||
typename ResultType = typename Internal::resultType<Function>::type>
|
|
||||||
QFuture<ResultType>
|
|
||||||
runAsync(Function &&function, Args&&... args)
|
|
||||||
{
|
|
||||||
return runAsync(static_cast<QThreadPool *>(nullptr),
|
|
||||||
QThread::InheritPriority, std::forward<Function>(function),
|
|
||||||
std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Runs \a function with \a args in a thread \a pool with thread priority QThread::InheritPriority.
|
|
||||||
\sa runAsync(QThreadPool*,QThread::Priority,Function&&,Args&&...)
|
|
||||||
\sa QThread::Priority
|
|
||||||
*/
|
|
||||||
template <typename Function, typename... Args,
|
|
||||||
typename = std::enable_if_t<!std::is_same<std::decay_t<Function>, QThread::Priority>::value>,
|
|
||||||
typename ResultType = typename Internal::resultType<Function>::type>
|
|
||||||
QFuture<ResultType>
|
|
||||||
runAsync(QThreadPool *pool, Function &&function, Args&&... args)
|
|
||||||
{
|
|
||||||
return runAsync(pool, QThread::InheritPriority, std::forward<Function>(function),
|
|
||||||
std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Utils
|
|
@@ -40,7 +40,6 @@ Project {
|
|||||||
Depends { name: "ptyqt" }
|
Depends { name: "ptyqt" }
|
||||||
|
|
||||||
files: [
|
files: [
|
||||||
"QtConcurrentTools",
|
|
||||||
"algorithm.h",
|
"algorithm.h",
|
||||||
"ansiescapecodehandler.cpp",
|
"ansiescapecodehandler.cpp",
|
||||||
"ansiescapecodehandler.h",
|
"ansiescapecodehandler.h",
|
||||||
@@ -147,7 +146,6 @@ Project {
|
|||||||
"fixedsizeclicklabel.h",
|
"fixedsizeclicklabel.h",
|
||||||
"flowlayout.cpp",
|
"flowlayout.cpp",
|
||||||
"flowlayout.h",
|
"flowlayout.h",
|
||||||
"functiontraits.h",
|
|
||||||
"futuresynchronizer.cpp",
|
"futuresynchronizer.cpp",
|
||||||
"futuresynchronizer.h",
|
"futuresynchronizer.h",
|
||||||
"fuzzymatcher.cpp",
|
"fuzzymatcher.cpp",
|
||||||
@@ -193,7 +191,6 @@ Project {
|
|||||||
"listutils.h",
|
"listutils.h",
|
||||||
"macroexpander.cpp",
|
"macroexpander.cpp",
|
||||||
"macroexpander.h",
|
"macroexpander.h",
|
||||||
"mapreduce.h",
|
|
||||||
"mathutils.cpp",
|
"mathutils.cpp",
|
||||||
"mathutils.h",
|
"mathutils.h",
|
||||||
"mimeutils.h",
|
"mimeutils.h",
|
||||||
@@ -270,8 +267,6 @@ Project {
|
|||||||
"reloadpromptutils.h",
|
"reloadpromptutils.h",
|
||||||
"removefiledialog.cpp",
|
"removefiledialog.cpp",
|
||||||
"removefiledialog.h",
|
"removefiledialog.h",
|
||||||
"runextensions.cpp",
|
|
||||||
"runextensions.h",
|
|
||||||
"savefile.cpp",
|
"savefile.cpp",
|
||||||
"savefile.h",
|
"savefile.h",
|
||||||
"scopedswap.h",
|
"scopedswap.h",
|
||||||
|
@@ -39,7 +39,7 @@
|
|||||||
\c init() is called in the GUI thread and can be used to query the
|
\c init() is called in the GUI thread and can be used to query the
|
||||||
project for any information you need.
|
project for any information you need.
|
||||||
|
|
||||||
\c run() is run via Utils::runAsync in a separate thread. If you need an
|
\c run() is run via Utils::asyncRun in a separate thread. If you need an
|
||||||
event loop, you need to create it yourself.
|
event loop, you need to create it yourself.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@@ -28,7 +28,6 @@
|
|||||||
#include <texteditor/tabsettings.h>
|
#include <texteditor/tabsettings.h>
|
||||||
#include <texteditor/texteditorsettings.h>
|
#include <texteditor/texteditorsettings.h>
|
||||||
|
|
||||||
#include <utils/QtConcurrentTools>
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
#include <utils/async.h>
|
#include <utils/async.h>
|
||||||
#include <utils/filesystemwatcher.h>
|
#include <utils/filesystemwatcher.h>
|
||||||
|
@@ -13,11 +13,9 @@ add_subdirectory(filesearch)
|
|||||||
add_subdirectory(haskell)
|
add_subdirectory(haskell)
|
||||||
add_subdirectory(json)
|
add_subdirectory(json)
|
||||||
add_subdirectory(languageserverprotocol)
|
add_subdirectory(languageserverprotocol)
|
||||||
add_subdirectory(mapreduce)
|
|
||||||
add_subdirectory(pointeralgorithm)
|
add_subdirectory(pointeralgorithm)
|
||||||
add_subdirectory(profilewriter)
|
add_subdirectory(profilewriter)
|
||||||
add_subdirectory(qml)
|
add_subdirectory(qml)
|
||||||
add_subdirectory(runextensions)
|
|
||||||
add_subdirectory(sdktool)
|
add_subdirectory(sdktool)
|
||||||
add_subdirectory(solutions)
|
add_subdirectory(solutions)
|
||||||
add_subdirectory(texteditor)
|
add_subdirectory(texteditor)
|
||||||
|
@@ -20,7 +20,6 @@ Project {
|
|||||||
"languageserverprotocol/languageserverprotocol.qbs",
|
"languageserverprotocol/languageserverprotocol.qbs",
|
||||||
"profilewriter/profilewriter.qbs",
|
"profilewriter/profilewriter.qbs",
|
||||||
"qml/qml.qbs",
|
"qml/qml.qbs",
|
||||||
"runextensions/runextensions.qbs",
|
|
||||||
"sdktool/sdktool.qbs",
|
"sdktool/sdktool.qbs",
|
||||||
"solutions/solutions.qbs",
|
"solutions/solutions.qbs",
|
||||||
"texteditor/texteditor.qbs",
|
"texteditor/texteditor.qbs",
|
||||||
|
@@ -1,4 +0,0 @@
|
|||||||
add_qtc_test(tst_mapreduce
|
|
||||||
DEPENDS Utils
|
|
||||||
SOURCES tst_mapreduce.cpp
|
|
||||||
)
|
|
@@ -1,10 +0,0 @@
|
|||||||
import qbs
|
|
||||||
|
|
||||||
QtcAutotest {
|
|
||||||
name: "Map reduce autotest"
|
|
||||||
Depends { name: "Utils" }
|
|
||||||
|
|
||||||
files: [
|
|
||||||
"tst_mapreduce.cpp",
|
|
||||||
]
|
|
||||||
}
|
|
@@ -1,345 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
|
||||||
#include <utils/mapreduce.h>
|
|
||||||
|
|
||||||
#include <QThreadPool>
|
|
||||||
#include <QtTest>
|
|
||||||
|
|
||||||
#if !defined(Q_CC_MSVC) || _MSC_VER >= 1900 // MSVC2015
|
|
||||||
#define SUPPORTS_MOVE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class tst_MapReduce : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void mapReduce();
|
|
||||||
void mapReduceRvalueContainer();
|
|
||||||
void map();
|
|
||||||
void orderedMapReduce();
|
|
||||||
#ifdef SUPPORTS_MOVE
|
|
||||||
void moveOnlyType();
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
static int returnxx(int x)
|
|
||||||
{
|
|
||||||
return x*x;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void returnxxThroughFutureInterface(QFutureInterface<int> &fi, int x)
|
|
||||||
{
|
|
||||||
fi.reportResult(x*x);
|
|
||||||
}
|
|
||||||
|
|
||||||
void tst_MapReduce::mapReduce()
|
|
||||||
{
|
|
||||||
QThreadPool pool;
|
|
||||||
const auto initWithFutureInterface = [](QFutureInterface<double> &fi) -> double {
|
|
||||||
fi.reportResult(0.);
|
|
||||||
return 0.;
|
|
||||||
};
|
|
||||||
const auto reduceWithFutureInterface = [](QFutureInterface<double> &fi, double &state, int value) {
|
|
||||||
state += value;
|
|
||||||
fi.reportResult(value);
|
|
||||||
};
|
|
||||||
const auto reduceWithReturn = [](double &state, int value) -> double {
|
|
||||||
state += value;
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
const auto cleanupWithFutureInterface = [](QFutureInterface<double> &fi, double &state) {
|
|
||||||
state /= 2.;
|
|
||||||
fi.reportResult(state);
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
|
||||||
// map without future interface
|
|
||||||
QList<double> results = Utils::mapReduce(QVector<int>({1, 2, 3, 4, 5}),
|
|
||||||
initWithFutureInterface,
|
|
||||||
returnxx,
|
|
||||||
reduceWithFutureInterface,
|
|
||||||
cleanupWithFutureInterface)
|
|
||||||
.results();
|
|
||||||
Utils::sort(results); // mapping order is undefined
|
|
||||||
QCOMPARE(results, QList<double>({0., 1., 4., 9., 16., 25., 27.5}));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// map with future interface
|
|
||||||
QList<double> results = Utils::mapReduce(QList<int>({1, 2, 3, 4, 5}),
|
|
||||||
initWithFutureInterface, returnxxThroughFutureInterface,
|
|
||||||
reduceWithFutureInterface, cleanupWithFutureInterface)
|
|
||||||
.results();
|
|
||||||
Utils::sort(results); // mapping order is undefined
|
|
||||||
QCOMPARE(results, QList<double>({0., 1., 4., 9., 16., 25., 27.5}));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// reduce without future interface
|
|
||||||
QList<double> results = Utils::mapReduce(QList<int>({1, 2, 3, 4, 5}),
|
|
||||||
initWithFutureInterface, returnxx,
|
|
||||||
reduceWithReturn, cleanupWithFutureInterface)
|
|
||||||
.results();
|
|
||||||
Utils::sort(results); // mapping order is undefined
|
|
||||||
QCOMPARE(results, QList<double>({0., 1., 4., 9., 16., 25., 27.5}));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// reduce with threadpool
|
|
||||||
QList<double> results = Utils::mapReduce(QList<int>({1, 2, 3, 4, 5}),
|
|
||||||
initWithFutureInterface, returnxx,
|
|
||||||
reduceWithReturn, cleanupWithFutureInterface,
|
|
||||||
Utils::MapReduceOption::Unordered, &pool)
|
|
||||||
.results();
|
|
||||||
Utils::sort(results); // mapping order is undefined
|
|
||||||
QCOMPARE(results, QList<double>({0., 1., 4., 9., 16., 25., 27.5}));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// lvalue ref container
|
|
||||||
QList<int> container({1, 2, 3, 4, 5});
|
|
||||||
QList<double> results = Utils::mapReduce(container,
|
|
||||||
initWithFutureInterface, returnxx,
|
|
||||||
reduceWithReturn, cleanupWithFutureInterface)
|
|
||||||
.results();
|
|
||||||
Utils::sort(results); // mapping order is undefined
|
|
||||||
QCOMPARE(results, QList<double>({0., 1., 4., 9., 16., 25., 27.5}));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// std::cref
|
|
||||||
QList<int> container({1, 2, 3, 4, 5});
|
|
||||||
QCOMPARE(Utils::mapReduce(std::cref(container),
|
|
||||||
initWithFutureInterface, returnxx,
|
|
||||||
reduceWithReturn, cleanupWithFutureInterface,
|
|
||||||
Utils::MapReduceOption::Ordered).results(),
|
|
||||||
QList<double>({0., 1., 4., 9., 16., 25., 27.5}));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// std::cref with threadpool
|
|
||||||
QList<int> container({1, 2, 3, 4, 5});
|
|
||||||
QCOMPARE(Utils::mapReduce(std::cref(container),
|
|
||||||
initWithFutureInterface, returnxx,
|
|
||||||
reduceWithReturn, cleanupWithFutureInterface,
|
|
||||||
Utils::MapReduceOption::Ordered, &pool).results(),
|
|
||||||
QList<double>({0., 1., 4., 9., 16., 25., 27.5}));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// std::ref
|
|
||||||
QList<int> container({1, 2, 3, 4, 5});
|
|
||||||
QCOMPARE(Utils::mapReduce(std::ref(container),
|
|
||||||
initWithFutureInterface, returnxx,
|
|
||||||
reduceWithReturn, cleanupWithFutureInterface,
|
|
||||||
Utils::MapReduceOption::Ordered).results(),
|
|
||||||
QList<double>({0., 1., 4., 9., 16., 25., 27.5}));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// init and cleanup without future interface
|
|
||||||
QCOMPARE(Utils::mapReduce(QList<int>({1, 2, 3}),
|
|
||||||
[]() { return 0.; },
|
|
||||||
[](int v) { return v*2; },
|
|
||||||
[](double &state, int v) { return state += v/4.; },
|
|
||||||
[](double &) { },
|
|
||||||
Utils::MapReduceOption::Ordered).results(),
|
|
||||||
QList<double>({.5, 1.5, 3.}));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// simplified map reduce without init and cleanup
|
|
||||||
QCOMPARE(Utils::mapReduce(QList<QString>({QLatin1String("blubb"), QLatin1String("foo"), QLatin1String("blah")}),
|
|
||||||
[](const QString &val) { return val.size(); },
|
|
||||||
90.,
|
|
||||||
[](double &state, int val) {
|
|
||||||
state /= double(val);
|
|
||||||
},
|
|
||||||
Utils::MapReduceOption::Ordered).result(),
|
|
||||||
1.5);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// simplified map reduce without init and cleanup with threadpool
|
|
||||||
QCOMPARE(Utils::mapReduce(QList<QString>({QLatin1String("blubb"), QLatin1String("foo"), QLatin1String("blah")}),
|
|
||||||
[](const QString &val) { return val.size(); },
|
|
||||||
90.,
|
|
||||||
[](double &state, int val) {
|
|
||||||
state /= double(val);
|
|
||||||
},
|
|
||||||
Utils::MapReduceOption::Ordered, &pool).result(),
|
|
||||||
1.5);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// simplified map reduce
|
|
||||||
// std::cref
|
|
||||||
QList<int> container({1, 2, 3});
|
|
||||||
QCOMPARE(Utils::mapReduce(std::cref(container), [](int val) { return 2*val; }, 10,
|
|
||||||
[](int &state, int val) { state += val; }).result(),
|
|
||||||
22);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// simplified map reduce
|
|
||||||
// std::cref with threadpool
|
|
||||||
QList<int> container({1, 2, 3});
|
|
||||||
QCOMPARE(Utils::mapReduce(std::cref(container), [](int val) { return 2*val; }, 10,
|
|
||||||
[](int &state, int val) { state += val; },
|
|
||||||
Utils::MapReduceOption::Unordered, &pool).result(),
|
|
||||||
22);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// simplified map reduce
|
|
||||||
// std::ref
|
|
||||||
QList<int> container({1, 2, 3});
|
|
||||||
QCOMPARE(Utils::mapReduce(std::ref(container), [](int &val) { return 2*val; }, 10,
|
|
||||||
[](int &state, int val) { state += val; }).result(),
|
|
||||||
22);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// blocking mapReduce = mappedReduced
|
|
||||||
QCOMPARE(Utils::mappedReduced(QList<int>({1, 2, 3}), [](int &val) { return 2*val; }, 10,
|
|
||||||
[](int &state, int val) { state += val; }),
|
|
||||||
22);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// blocking mapReduce = mappedReduced
|
|
||||||
// with threadpool
|
|
||||||
QCOMPARE(Utils::mappedReduced(QList<int>({1, 2, 3}), [](int &val) { return 2*val; }, 10,
|
|
||||||
[](int &state, int val) { state += val; },
|
|
||||||
Utils::MapReduceOption::Unordered, &pool),
|
|
||||||
22);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void tst_MapReduce::mapReduceRvalueContainer()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
QFuture<int> future = Utils::mapReduce(QList<int>({1, 2, 3, 4, 5}),
|
|
||||||
[]() { return 0; },
|
|
||||||
[](int value) { return value; },
|
|
||||||
[](QFutureInterface<int> &, int &state, int value) { state += value; },
|
|
||||||
[](QFutureInterface<int> &fi, int &state) { fi.reportResult(state); });
|
|
||||||
// here, lifetime of the QList temporary ends
|
|
||||||
QCOMPARE(future.results(), QList<int>({15}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void tst_MapReduce::map()
|
|
||||||
{
|
|
||||||
QCOMPARE(Utils::map(QList<int>({2, 5, 1}), [](int x) { return x*2.5; }).results(),
|
|
||||||
QList<double>({5., 12.5, 2.5}));
|
|
||||||
{
|
|
||||||
// void result
|
|
||||||
QList<int> results;
|
|
||||||
QMutex mutex;
|
|
||||||
Utils::map(
|
|
||||||
// container
|
|
||||||
QList<int>({2, 5, 1}),
|
|
||||||
// map
|
|
||||||
[&mutex, &results](int x) { QMutexLocker l(&mutex); results.append(x); }
|
|
||||||
).waitForFinished();
|
|
||||||
// Utils::map is "ordered" by default, but that means that result reporting is ordered,
|
|
||||||
// the map function is still called out-of-order
|
|
||||||
Utils::sort(results);
|
|
||||||
QCOMPARE(results, QList<int>({1, 2, 5}));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// inplace editing
|
|
||||||
QList<int> container({2, 5, 1});
|
|
||||||
Utils::map(std::ref(container), [](int &x) { x *= 2; }).waitForFinished();
|
|
||||||
QCOMPARE(container, QList<int>({4, 10, 2}));
|
|
||||||
|
|
||||||
Utils::map(container.begin(), container.end(), [](int &x) { x *= 2; },
|
|
||||||
Utils::MapReduceOption::Unordered,
|
|
||||||
nullptr, QThread::InheritPriority, 3).waitForFinished();
|
|
||||||
QCOMPARE(container, QList<int>({8, 20, 4}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// blocking map = mapped
|
|
||||||
{
|
|
||||||
const QSet<qsizetype> sizes = Utils::mapped<QSet>(
|
|
||||||
QStringList({QLatin1String("foo"), QLatin1String("bar"), QLatin1String("blah")}),
|
|
||||||
[](const QString &s) { return s.size(); });
|
|
||||||
QList<qsizetype> vals = sizes.values();
|
|
||||||
Utils::sort(vals);
|
|
||||||
QCOMPARE(vals, QList<qsizetype>({3, 4}));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const QStringList list({QLatin1String("foo"), QLatin1String("bar"), QLatin1String("blah")});
|
|
||||||
const QSet<qsizetype> sizes = Utils::mapped<QSet>(list.cbegin(),
|
|
||||||
list.cend(),
|
|
||||||
[](const QString &s) {
|
|
||||||
return s.size();
|
|
||||||
});
|
|
||||||
QList<qsizetype> vals = sizes.values();
|
|
||||||
Utils::sort(vals);
|
|
||||||
QCOMPARE(vals, QList<qsizetype>({3, 4}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void tst_MapReduce::orderedMapReduce()
|
|
||||||
{
|
|
||||||
QCOMPARE(Utils::mapReduce(QList<int>({1, 2, 3, 4}),
|
|
||||||
[]() { return 0; },
|
|
||||||
[](int i) { return i*2; },
|
|
||||||
[](int &state, int val) { state += val; return state; },
|
|
||||||
[](int &) { },
|
|
||||||
Utils::MapReduceOption::Ordered).results(),
|
|
||||||
QList<int>({2, 6, 12, 20}));
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SUPPORTS_MOVE
|
|
||||||
|
|
||||||
class MoveOnlyType
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MoveOnlyType() noexcept {} // <- with GCC 5 the defaulted one is noexcept(false)
|
|
||||||
MoveOnlyType(const MoveOnlyType &) = delete;
|
|
||||||
MoveOnlyType(MoveOnlyType &&) = default;
|
|
||||||
MoveOnlyType &operator=(const MoveOnlyType &) = delete;
|
|
||||||
MoveOnlyType &operator=(MoveOnlyType &&) = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MoveOnlyState : public MoveOnlyType
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
int count = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MoveOnlyInit : public MoveOnlyType
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MoveOnlyState operator()(QFutureInterface<int> &) const { return MoveOnlyState(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
class MoveOnlyMap : public MoveOnlyType
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
int operator()(const MoveOnlyType &) const { return 1; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class MoveOnlyReduce : public MoveOnlyType
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void operator()(QFutureInterface<int> &, MoveOnlyState &state, int) { ++state.count; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class MoveOnlyList : public std::vector<MoveOnlyType>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MoveOnlyList() { emplace_back(MoveOnlyType()); emplace_back(MoveOnlyType()); }
|
|
||||||
MoveOnlyList(const MoveOnlyList &) = delete;
|
|
||||||
MoveOnlyList(MoveOnlyList &&) = default;
|
|
||||||
MoveOnlyList &operator=(const MoveOnlyList &) = delete;
|
|
||||||
MoveOnlyList &operator=(MoveOnlyList &&) = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
void tst_MapReduce::moveOnlyType()
|
|
||||||
{
|
|
||||||
QCOMPARE(Utils::mapReduce(MoveOnlyList(),
|
|
||||||
MoveOnlyInit(),
|
|
||||||
MoveOnlyMap(),
|
|
||||||
MoveOnlyReduce(),
|
|
||||||
[](QFutureInterface<int> &fi, MoveOnlyState &state) { fi.reportResult(state.count); }
|
|
||||||
).results(),
|
|
||||||
QList<int>({2}));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QTEST_GUILESS_MAIN(tst_MapReduce)
|
|
||||||
|
|
||||||
#include "tst_mapreduce.moc"
|
|
@@ -1,4 +0,0 @@
|
|||||||
add_qtc_test(tst_runextensions
|
|
||||||
DEPENDS Utils
|
|
||||||
SOURCES tst_runextensions.cpp
|
|
||||||
)
|
|
@@ -1,10 +0,0 @@
|
|||||||
import qbs
|
|
||||||
|
|
||||||
QtcAutotest {
|
|
||||||
name: "Run extensions autotest"
|
|
||||||
Depends { name: "Utils" }
|
|
||||||
|
|
||||||
files: [
|
|
||||||
"tst_runextensions.cpp",
|
|
||||||
]
|
|
||||||
}
|
|
@@ -1,540 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
#include <utils/runextensions.h>
|
|
||||||
|
|
||||||
#include <QtTest>
|
|
||||||
|
|
||||||
#if !defined(Q_CC_MSVC) || _MSC_VER >= 1900 // MSVC2015
|
|
||||||
#define SUPPORTS_MOVE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class tst_RunExtensions : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void runAsync();
|
|
||||||
void runInThreadPool();
|
|
||||||
#ifdef SUPPORTS_MOVE
|
|
||||||
void moveOnlyType();
|
|
||||||
#endif
|
|
||||||
void threadPriority();
|
|
||||||
void runAsyncNoFutureInterface();
|
|
||||||
void crefFunction();
|
|
||||||
};
|
|
||||||
|
|
||||||
void report3(QFutureInterface<int> &fi)
|
|
||||||
{
|
|
||||||
fi.reportResults({0, 2, 1});
|
|
||||||
}
|
|
||||||
|
|
||||||
void reportN(QFutureInterface<double> &fi, int n)
|
|
||||||
{
|
|
||||||
fi.reportResults(QVector<double>(n, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
void reportString1(QFutureInterface<QString> &fi, const QString &s)
|
|
||||||
{
|
|
||||||
fi.reportResult(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
void reportString2(QFutureInterface<QString> &fi, QString s)
|
|
||||||
{
|
|
||||||
fi.reportResult(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
class Callable {
|
|
||||||
public:
|
|
||||||
void operator()(QFutureInterface<double> &fi, int n) const
|
|
||||||
{
|
|
||||||
fi.reportResults(QVector<double>(n, 0));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class MyObject {
|
|
||||||
public:
|
|
||||||
static void staticMember0(QFutureInterface<double> &fi)
|
|
||||||
{
|
|
||||||
fi.reportResults({0, 2, 1});
|
|
||||||
}
|
|
||||||
|
|
||||||
static void staticMember1(QFutureInterface<double> &fi, int n)
|
|
||||||
{
|
|
||||||
fi.reportResults(QVector<double>(n, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
void member0(QFutureInterface<double> &fi) const
|
|
||||||
{
|
|
||||||
fi.reportResults({0, 2, 1});
|
|
||||||
}
|
|
||||||
|
|
||||||
void member1(QFutureInterface<double> &fi, int n) const
|
|
||||||
{
|
|
||||||
fi.reportResults(QVector<double>(n, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
void memberString1(QFutureInterface<QString> &fi, const QString &s) const
|
|
||||||
{
|
|
||||||
fi.reportResult(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
void memberString2(QFutureInterface<QString> &fi, QString s) const
|
|
||||||
{
|
|
||||||
fi.reportResult(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nonConstMember(QFutureInterface<double> &fi)
|
|
||||||
{
|
|
||||||
fi.reportResults({0, 2, 1});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void voidFunction(bool *value) // can be useful to get QFuture for watching when it is finished
|
|
||||||
{
|
|
||||||
*value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int one()
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int identity(int input)
|
|
||||||
{
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString stringIdentity1(const QString &s)
|
|
||||||
{
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString stringIdentity2(QString s)
|
|
||||||
{
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
class CallableWithoutQFutureInterface {
|
|
||||||
public:
|
|
||||||
void operator()(bool *value) const
|
|
||||||
{
|
|
||||||
*value = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class MyObjectWithoutQFutureInterface {
|
|
||||||
public:
|
|
||||||
static void staticMember0(bool *value)
|
|
||||||
{
|
|
||||||
*value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static double staticMember1(int n)
|
|
||||||
{
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
void member0(bool *value) const
|
|
||||||
{
|
|
||||||
*value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
double member1(int n) const
|
|
||||||
{
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString memberString1(const QString &s) const
|
|
||||||
{
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString memberString2(QString s) const
|
|
||||||
{
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
double nonConstMember(int n)
|
|
||||||
{
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void tst_RunExtensions::runAsync()
|
|
||||||
{
|
|
||||||
// free function pointer
|
|
||||||
QCOMPARE(Utils::runAsync(&report3).results(),
|
|
||||||
QList<int>({0, 2, 1}));
|
|
||||||
QCOMPARE(Utils::runAsync(report3).results(),
|
|
||||||
QList<int>({0, 2, 1}));
|
|
||||||
|
|
||||||
QCOMPARE(Utils::runAsync(reportN, 4).results(),
|
|
||||||
QList<double>({0, 0, 0, 0}));
|
|
||||||
QCOMPARE(Utils::runAsync(reportN, 2).results(),
|
|
||||||
QList<double>({0, 0}));
|
|
||||||
|
|
||||||
QString s = QLatin1String("string");
|
|
||||||
const QString &crs = QLatin1String("cr string");
|
|
||||||
const QString cs = QLatin1String("c string");
|
|
||||||
|
|
||||||
QCOMPARE(Utils::runAsync(reportString1, s).results(),
|
|
||||||
QList<QString>({s}));
|
|
||||||
QCOMPARE(Utils::runAsync(reportString1, crs).results(),
|
|
||||||
QList<QString>({crs}));
|
|
||||||
QCOMPARE(Utils::runAsync(reportString1, cs).results(),
|
|
||||||
QList<QString>({cs}));
|
|
||||||
QCOMPARE(Utils::runAsync(reportString1, QString(QLatin1String("rvalue"))).results(),
|
|
||||||
QList<QString>({QString(QLatin1String("rvalue"))}));
|
|
||||||
|
|
||||||
QCOMPARE(Utils::runAsync(reportString2, s).results(),
|
|
||||||
QList<QString>({s}));
|
|
||||||
QCOMPARE(Utils::runAsync(reportString2, crs).results(),
|
|
||||||
QList<QString>({crs}));
|
|
||||||
QCOMPARE(Utils::runAsync(reportString2, cs).results(),
|
|
||||||
QList<QString>({cs}));
|
|
||||||
QCOMPARE(Utils::runAsync(reportString2, QString(QLatin1String("rvalue"))).results(),
|
|
||||||
QList<QString>({QString(QLatin1String("rvalue"))}));
|
|
||||||
|
|
||||||
// lambda
|
|
||||||
QCOMPARE(Utils::runAsync([](QFutureInterface<double> &fi, int n) {
|
|
||||||
fi.reportResults(QVector<double>(n, 0));
|
|
||||||
}, 3).results(),
|
|
||||||
QList<double>({0, 0, 0}));
|
|
||||||
|
|
||||||
// std::function
|
|
||||||
const std::function<void(QFutureInterface<double>&,int)> fun = [](QFutureInterface<double> &fi, int n) {
|
|
||||||
fi.reportResults(QVector<double>(n, 0));
|
|
||||||
};
|
|
||||||
QCOMPARE(Utils::runAsync(fun, 2).results(),
|
|
||||||
QList<double>({0, 0}));
|
|
||||||
|
|
||||||
// operator()
|
|
||||||
QCOMPARE(Utils::runAsync(Callable(), 3).results(),
|
|
||||||
QList<double>({0, 0, 0}));
|
|
||||||
const Callable c{};
|
|
||||||
QCOMPARE(Utils::runAsync(c, 2).results(),
|
|
||||||
QList<double>({0, 0}));
|
|
||||||
|
|
||||||
// static member functions
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObject::staticMember0).results(),
|
|
||||||
QList<double>({0, 2, 1}));
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObject::staticMember1, 2).results(),
|
|
||||||
QList<double>({0, 0}));
|
|
||||||
|
|
||||||
// member functions
|
|
||||||
const MyObject obj{};
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObject::member0, &obj).results(),
|
|
||||||
QList<double>({0, 2, 1}));
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObject::member1, &obj, 4).results(),
|
|
||||||
QList<double>({0, 0, 0, 0}));
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObject::memberString1, &obj, s).results(),
|
|
||||||
QList<QString>({s}));
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObject::memberString1, &obj, crs).results(),
|
|
||||||
QList<QString>({crs}));
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObject::memberString1, &obj, cs).results(),
|
|
||||||
QList<QString>({cs}));
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObject::memberString1, &obj, QString(QLatin1String("rvalue"))).results(),
|
|
||||||
QList<QString>({QString(QLatin1String("rvalue"))}));
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObject::memberString2, &obj, s).results(),
|
|
||||||
QList<QString>({s}));
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObject::memberString2, &obj, crs).results(),
|
|
||||||
QList<QString>({crs}));
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObject::memberString2, &obj, cs).results(),
|
|
||||||
QList<QString>({cs}));
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObject::memberString2, &obj, QString(QLatin1String("rvalue"))).results(),
|
|
||||||
QList<QString>({QString(QLatin1String("rvalue"))}));
|
|
||||||
MyObject nonConstObj{};
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObject::nonConstMember, &nonConstObj).results(),
|
|
||||||
QList<double>({0, 2, 1}));
|
|
||||||
}
|
|
||||||
|
|
||||||
void tst_RunExtensions::runInThreadPool()
|
|
||||||
{
|
|
||||||
QScopedPointer<QThreadPool> pool(new QThreadPool);
|
|
||||||
// free function pointer
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), &report3).results(),
|
|
||||||
QList<int>({0, 2, 1}));
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), report3).results(),
|
|
||||||
QList<int>({0, 2, 1}));
|
|
||||||
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), reportN, 4).results(),
|
|
||||||
QList<double>({0, 0, 0, 0}));
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), reportN, 2).results(),
|
|
||||||
QList<double>({0, 0}));
|
|
||||||
|
|
||||||
QString s = QLatin1String("string");
|
|
||||||
const QString &crs = QLatin1String("cr string");
|
|
||||||
const QString cs = QLatin1String("c string");
|
|
||||||
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), reportString1, s).results(),
|
|
||||||
QList<QString>({s}));
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), reportString1, crs).results(),
|
|
||||||
QList<QString>({crs}));
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), reportString1, cs).results(),
|
|
||||||
QList<QString>({cs}));
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), reportString1, QString(QLatin1String("rvalue"))).results(),
|
|
||||||
QList<QString>({QString(QLatin1String("rvalue"))}));
|
|
||||||
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), reportString2, s).results(),
|
|
||||||
QList<QString>({s}));
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), reportString2, crs).results(),
|
|
||||||
QList<QString>({crs}));
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), reportString2, cs).results(),
|
|
||||||
QList<QString>({cs}));
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), reportString2, QString(QLatin1String("rvalue"))).results(),
|
|
||||||
QList<QString>({QString(QLatin1String("rvalue"))}));
|
|
||||||
|
|
||||||
// lambda
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), [](QFutureInterface<double> &fi, int n) {
|
|
||||||
fi.reportResults(QVector<double>(n, 0));
|
|
||||||
}, 3).results(),
|
|
||||||
QList<double>({0, 0, 0}));
|
|
||||||
|
|
||||||
// std::function
|
|
||||||
const std::function<void(QFutureInterface<double>&,int)> fun = [](QFutureInterface<double> &fi, int n) {
|
|
||||||
fi.reportResults(QVector<double>(n, 0));
|
|
||||||
};
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), fun, 2).results(),
|
|
||||||
QList<double>({0, 0}));
|
|
||||||
|
|
||||||
// operator()
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), Callable(), 3).results(),
|
|
||||||
QList<double>({0, 0, 0}));
|
|
||||||
const Callable c{};
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), c, 2).results(),
|
|
||||||
QList<double>({0, 0}));
|
|
||||||
|
|
||||||
// static member functions
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), &MyObject::staticMember0).results(),
|
|
||||||
QList<double>({0, 2, 1}));
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), &MyObject::staticMember1, 2).results(),
|
|
||||||
QList<double>({0, 0}));
|
|
||||||
|
|
||||||
// member functions
|
|
||||||
const MyObject obj{};
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), &MyObject::member0, &obj).results(),
|
|
||||||
QList<double>({0, 2, 1}));
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), &MyObject::member1, &obj, 4).results(),
|
|
||||||
QList<double>({0, 0, 0, 0}));
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString1, &obj, s).results(),
|
|
||||||
QList<QString>({s}));
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString1, &obj, crs).results(),
|
|
||||||
QList<QString>({crs}));
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString1, &obj, cs).results(),
|
|
||||||
QList<QString>({cs}));
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString1, &obj, QString(QLatin1String("rvalue"))).results(),
|
|
||||||
QList<QString>({QString(QLatin1String("rvalue"))}));
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString2, &obj, s).results(),
|
|
||||||
QList<QString>({s}));
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString2, &obj, crs).results(),
|
|
||||||
QList<QString>({crs}));
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString2, &obj, cs).results(),
|
|
||||||
QList<QString>({cs}));
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString2, &obj, QString(QLatin1String("rvalue"))).results(),
|
|
||||||
QList<QString>({QString(QLatin1String("rvalue"))}));
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SUPPORTS_MOVE
|
|
||||||
|
|
||||||
class MoveOnlyType
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MoveOnlyType() = default;
|
|
||||||
MoveOnlyType(const MoveOnlyType &) = delete;
|
|
||||||
MoveOnlyType(MoveOnlyType &&) = default;
|
|
||||||
MoveOnlyType &operator=(const MoveOnlyType &) = delete;
|
|
||||||
MoveOnlyType &operator=(MoveOnlyType &&) = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MoveOnlyCallable : public MoveOnlyType
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void operator()(QFutureInterface<int> &fi, const MoveOnlyType &)
|
|
||||||
{
|
|
||||||
fi.reportResult(1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void tst_RunExtensions::moveOnlyType()
|
|
||||||
{
|
|
||||||
QCOMPARE(Utils::runAsync(MoveOnlyCallable(), MoveOnlyType()).results(),
|
|
||||||
QList<int>({1}));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void tst_RunExtensions::threadPriority()
|
|
||||||
{
|
|
||||||
QScopedPointer<QThreadPool> pool(new QThreadPool);
|
|
||||||
// with pool
|
|
||||||
QCOMPARE(Utils::runAsync(pool.data(), QThread::LowestPriority, &report3).results(),
|
|
||||||
QList<int>({0, 2, 1}));
|
|
||||||
|
|
||||||
// without pool
|
|
||||||
QCOMPARE(Utils::runAsync(QThread::LowestPriority, report3).results(),
|
|
||||||
QList<int>({0, 2, 1}));
|
|
||||||
}
|
|
||||||
|
|
||||||
void tst_RunExtensions::runAsyncNoFutureInterface()
|
|
||||||
{
|
|
||||||
// free function pointer
|
|
||||||
bool value = false;
|
|
||||||
Utils::runAsync(voidFunction, &value).waitForFinished();
|
|
||||||
QCOMPARE(value, true);
|
|
||||||
|
|
||||||
QCOMPARE(Utils::runAsync(one).results(),
|
|
||||||
QList<int>({1}));
|
|
||||||
QCOMPARE(Utils::runAsync(identity, 5).results(),
|
|
||||||
QList<int>({5}));
|
|
||||||
|
|
||||||
QString s = QLatin1String("string");
|
|
||||||
const QString &crs = QLatin1String("cr string");
|
|
||||||
const QString cs = QLatin1String("c string");
|
|
||||||
|
|
||||||
QCOMPARE(Utils::runAsync(stringIdentity1, s).results(),
|
|
||||||
QList<QString>({s}));
|
|
||||||
QCOMPARE(Utils::runAsync(stringIdentity1, crs).results(),
|
|
||||||
QList<QString>({crs}));
|
|
||||||
QCOMPARE(Utils::runAsync(stringIdentity1, cs).results(),
|
|
||||||
QList<QString>({cs}));
|
|
||||||
QCOMPARE(Utils::runAsync(stringIdentity1, QString(QLatin1String("rvalue"))).results(),
|
|
||||||
QList<QString>({QString(QLatin1String("rvalue"))}));
|
|
||||||
QCOMPARE(Utils::runAsync(stringIdentity2, s).results(),
|
|
||||||
QList<QString>({s}));
|
|
||||||
QCOMPARE(Utils::runAsync(stringIdentity2, crs).results(),
|
|
||||||
QList<QString>({crs}));
|
|
||||||
QCOMPARE(Utils::runAsync(stringIdentity2, cs).results(),
|
|
||||||
QList<QString>({cs}));
|
|
||||||
QCOMPARE(Utils::runAsync(stringIdentity2, QString(QLatin1String("rvalue"))).results(),
|
|
||||||
QList<QString>({QString(QLatin1String("rvalue"))}));
|
|
||||||
|
|
||||||
// lambda
|
|
||||||
QCOMPARE(Utils::runAsync([](int n) -> double {
|
|
||||||
return n + 1;
|
|
||||||
}, 3).results(),
|
|
||||||
QList<double>({4}));
|
|
||||||
|
|
||||||
// std::function
|
|
||||||
const std::function<double(int)> fun = [](int n) {
|
|
||||||
return n + 1;
|
|
||||||
};
|
|
||||||
QCOMPARE(Utils::runAsync(fun, 2).results(),
|
|
||||||
QList<double>({3}));
|
|
||||||
|
|
||||||
// operator()
|
|
||||||
value = false;
|
|
||||||
Utils::runAsync(CallableWithoutQFutureInterface(), &value).waitForFinished();
|
|
||||||
QCOMPARE(value, true);
|
|
||||||
value = false;
|
|
||||||
const CallableWithoutQFutureInterface c{};
|
|
||||||
Utils::runAsync(c, &value).waitForFinished();
|
|
||||||
QCOMPARE(value, true);
|
|
||||||
|
|
||||||
// static member functions
|
|
||||||
value = false;
|
|
||||||
Utils::runAsync(&MyObjectWithoutQFutureInterface::staticMember0, &value).waitForFinished();
|
|
||||||
QCOMPARE(value, true);
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::staticMember1, 2).results(),
|
|
||||||
QList<double>({2}));
|
|
||||||
|
|
||||||
// member functions
|
|
||||||
const MyObjectWithoutQFutureInterface obj{};
|
|
||||||
value = false;
|
|
||||||
Utils::runAsync(&MyObjectWithoutQFutureInterface::member0, &obj, &value).waitForFinished();
|
|
||||||
QCOMPARE(value, true);
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::member1, &obj, 4).results(),
|
|
||||||
QList<double>({4}));
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString1, &obj, s).results(),
|
|
||||||
QList<QString>({s}));
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString1, &obj, crs).results(),
|
|
||||||
QList<QString>({crs}));
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString1, &obj, cs).results(),
|
|
||||||
QList<QString>({cs}));
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString1, &obj, QString(QLatin1String("rvalue"))).results(),
|
|
||||||
QList<QString>({QString(QLatin1String("rvalue"))}));
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString2, &obj, s).results(),
|
|
||||||
QList<QString>({s}));
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString2, &obj, crs).results(),
|
|
||||||
QList<QString>({crs}));
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString2, &obj, cs).results(),
|
|
||||||
QList<QString>({cs}));
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString2, &obj, QString(QLatin1String("rvalue"))).results(),
|
|
||||||
QList<QString>({QString(QLatin1String("rvalue"))}));
|
|
||||||
MyObjectWithoutQFutureInterface nonConstObj{};
|
|
||||||
QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::nonConstMember, &nonConstObj, 4).results(),
|
|
||||||
QList<double>({4}));
|
|
||||||
}
|
|
||||||
|
|
||||||
void tst_RunExtensions::crefFunction()
|
|
||||||
{
|
|
||||||
// free function pointer with future interface
|
|
||||||
auto fun = &report3;
|
|
||||||
QCOMPARE(Utils::runAsync(std::cref(fun)).results(),
|
|
||||||
QList<int>({0, 2, 1}));
|
|
||||||
|
|
||||||
// lambda with future interface
|
|
||||||
auto lambda = [](QFutureInterface<double> &fi, int n) {
|
|
||||||
fi.reportResults(QVector<double>(n, 0));
|
|
||||||
};
|
|
||||||
QCOMPARE(Utils::runAsync(std::cref(lambda), 3).results(),
|
|
||||||
QList<double>({0, 0, 0}));
|
|
||||||
|
|
||||||
// std::function with future interface
|
|
||||||
const std::function<void(QFutureInterface<double>&,int)> funObj = [](QFutureInterface<double> &fi, int n) {
|
|
||||||
fi.reportResults(QVector<double>(n, 0));
|
|
||||||
};
|
|
||||||
QCOMPARE(Utils::runAsync(std::cref(funObj), 2).results(),
|
|
||||||
QList<double>({0, 0}));
|
|
||||||
|
|
||||||
// callable with future interface
|
|
||||||
const Callable c{};
|
|
||||||
QCOMPARE(Utils::runAsync(std::cref(c), 2).results(),
|
|
||||||
QList<double>({0, 0}));
|
|
||||||
|
|
||||||
// member functions with future interface
|
|
||||||
auto member = &MyObject::member0;
|
|
||||||
const MyObject obj{};
|
|
||||||
QCOMPARE(Utils::runAsync(std::cref(member), &obj).results(),
|
|
||||||
QList<double>({0, 2, 1}));
|
|
||||||
|
|
||||||
// free function pointer without future interface
|
|
||||||
bool value = false;
|
|
||||||
auto voidFun = &voidFunction;
|
|
||||||
Utils::runAsync(std::cref(voidFun), &value).waitForFinished();
|
|
||||||
QCOMPARE(value, true);
|
|
||||||
|
|
||||||
auto oneFun = &one;
|
|
||||||
QCOMPARE(Utils::runAsync(std::cref(oneFun)).results(),
|
|
||||||
QList<int>({1}));
|
|
||||||
|
|
||||||
// lambda without future interface
|
|
||||||
auto lambda2 = [](int n) -> double {
|
|
||||||
return n + 1;
|
|
||||||
};
|
|
||||||
QCOMPARE(Utils::runAsync(std::cref(lambda2), 3).results(),
|
|
||||||
QList<double>({4}));
|
|
||||||
|
|
||||||
// std::function
|
|
||||||
const std::function<double(int)> funObj2 = [](int n) {
|
|
||||||
return n + 1;
|
|
||||||
};
|
|
||||||
QCOMPARE(Utils::runAsync(std::cref(funObj2), 2).results(),
|
|
||||||
QList<double>({3}));
|
|
||||||
|
|
||||||
// callable without future interface
|
|
||||||
const CallableWithoutQFutureInterface c2{};
|
|
||||||
Utils::runAsync(std::cref(c2), &value).waitForFinished();
|
|
||||||
QCOMPARE(value, true);
|
|
||||||
|
|
||||||
// member functions without future interface
|
|
||||||
const MyObjectWithoutQFutureInterface obj2{};
|
|
||||||
auto member2 = &MyObjectWithoutQFutureInterface::member0;
|
|
||||||
value = false;
|
|
||||||
Utils::runAsync(std::cref(member2), &obj2, &value).waitForFinished();
|
|
||||||
QCOMPARE(value, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
QTEST_GUILESS_MAIN(tst_RunExtensions)
|
|
||||||
|
|
||||||
#include "tst_runextensions.moc"
|
|
Reference in New Issue
Block a user