forked from qt-creator/qt-creator
TaskTree: Unify static assertions for all handlers
Introduce GroupItem::isInvocable() helper. Add more compile tests. Task-number: QTCREATORBUG-29834 Change-Id: I444efeda77d1fa584567403224595b821f2a2d43 Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -214,6 +214,17 @@ protected:
|
|||||||
static GroupItem withTimeout(const GroupItem &item, std::chrono::milliseconds timeout,
|
static GroupItem withTimeout(const GroupItem &item, std::chrono::milliseconds timeout,
|
||||||
const std::function<void()> &handler = {});
|
const std::function<void()> &handler = {});
|
||||||
|
|
||||||
|
// Checks if Function may be invoked with Args and if Function's return type is Result.
|
||||||
|
template <typename Result, typename Function, typename ...Args,
|
||||||
|
typename DecayedFunction = std::decay_t<Function>>
|
||||||
|
static constexpr bool isInvocable()
|
||||||
|
{
|
||||||
|
// Note, that std::is_invocable_r_v doesn't check Result type properly.
|
||||||
|
if constexpr (std::is_invocable_r_v<Result, DecayedFunction, Args...>)
|
||||||
|
return std::is_same_v<Result, std::invoke_result_t<DecayedFunction, Args...>>;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Type m_type = Type::Group;
|
Type m_type = Type::Group;
|
||||||
QList<GroupItem> m_children;
|
QList<GroupItem> m_children;
|
||||||
@@ -254,40 +265,33 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<typename Handler>
|
template <typename Handler>
|
||||||
static GroupSetupHandler wrapGroupSetup(Handler &&handler)
|
static GroupSetupHandler wrapGroupSetup(Handler &&handler)
|
||||||
{
|
{
|
||||||
static constexpr bool isDynamic
|
// S, V stands for: [S]etupResult, [V]oid
|
||||||
= std::is_same_v<SetupResult, std::invoke_result_t<std::decay_t<Handler>>>;
|
static constexpr bool isS = isInvocable<SetupResult, Handler>();
|
||||||
constexpr bool isVoid
|
static constexpr bool isV = isInvocable<void, Handler>();
|
||||||
= std::is_same_v<void, std::invoke_result_t<std::decay_t<Handler>>>;
|
static_assert(isS || isV,
|
||||||
static_assert(isDynamic || isVoid,
|
"Group setup handler needs to take no arguments and has to return void or SetupResult. "
|
||||||
"Group setup handler needs to take no arguments and has to return "
|
"The passed handler doesn't fulfill these requirements.");
|
||||||
"void or SetupResult. The passed handler doesn't fulfill these requirements.");
|
|
||||||
return [=] {
|
return [=] {
|
||||||
if constexpr (isDynamic)
|
if constexpr (isS)
|
||||||
return std::invoke(handler);
|
return std::invoke(handler);
|
||||||
std::invoke(handler);
|
std::invoke(handler);
|
||||||
return SetupResult::Continue;
|
return SetupResult::Continue;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
template<typename Handler>
|
template <typename Handler>
|
||||||
static GroupDoneHandler wrapGroupDone(Handler &&handler)
|
static GroupDoneHandler wrapGroupDone(Handler &&handler)
|
||||||
{
|
{
|
||||||
static constexpr bool isBD // stands for [B]ool, [D]oneWith
|
// B, V, D stands for: [B]ool, [V]oid, [D]oneWith
|
||||||
= std::is_invocable_r_v<bool, std::decay_t<Handler>, DoneWith>;
|
static constexpr bool isBD = isInvocable<bool, Handler, DoneWith>();
|
||||||
static constexpr bool isB
|
static constexpr bool isB = isInvocable<bool, Handler>();
|
||||||
= std::is_invocable_r_v<bool, std::decay_t<Handler>>;
|
static constexpr bool isVD = isInvocable<void, Handler, DoneWith>();
|
||||||
static constexpr bool isVD // stands for [V]oid, [D]oneWith
|
static constexpr bool isV = isInvocable<void, Handler>();
|
||||||
= std::is_invocable_r_v<void, std::decay_t<Handler>, DoneWith>;
|
static_assert(isBD || isB || isVD || isV,
|
||||||
static constexpr bool isV
|
"Group done handler needs to take (DoneWith) or (void) as an argument and has to "
|
||||||
= std::is_invocable_r_v<void, std::decay_t<Handler>>;
|
"return void or bool. The passed handler doesn't fulfill these requirements.");
|
||||||
static constexpr bool isInvocable = isBD || isB || isVD || isV;
|
|
||||||
|
|
||||||
static_assert(isInvocable,
|
|
||||||
"Group done handler needs to take (DoneWith) or (void) "
|
|
||||||
"as arguments and has to return void or bool. "
|
|
||||||
"The passed handler doesn't fulfill these requirements.");
|
|
||||||
return [=](DoneWith result) {
|
return [=](DoneWith result) {
|
||||||
if constexpr (isBD)
|
if constexpr (isBD)
|
||||||
return std::invoke(handler, result);
|
return std::invoke(handler, result);
|
||||||
@@ -338,24 +342,28 @@ public:
|
|||||||
class TASKING_EXPORT Sync final : public GroupItem
|
class TASKING_EXPORT Sync final : public GroupItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
template<typename Function>
|
template <typename Handler>
|
||||||
Sync(Function &&function) { addChildren({init(std::forward<Function>(function))}); }
|
Sync(Handler &&handler) {
|
||||||
|
addChildren({ onGroupSetup(wrapHandler(std::forward<Handler>(handler))) });
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<typename Function>
|
template <typename Handler>
|
||||||
static GroupItem init(Function &&function) {
|
static GroupSetupHandler wrapHandler(Handler &&handler) {
|
||||||
constexpr bool isInvocable = std::is_invocable_v<std::decay_t<Function>>;
|
// B, V stands for: [B]ool, [V]oid
|
||||||
static_assert(isInvocable,
|
static constexpr bool isB = isInvocable<bool, Handler>();
|
||||||
"Sync element: The synchronous function can't take any arguments.");
|
static constexpr bool isV = isInvocable<void, Handler>();
|
||||||
constexpr bool isBool = std::is_same_v<bool, std::invoke_result_t<std::decay_t<Function>>>;
|
static_assert(isB || isV,
|
||||||
constexpr bool isVoid = std::is_same_v<void, std::invoke_result_t<std::decay_t<Function>>>;
|
"Sync handler needs to take no arguments and has to return void or bool. "
|
||||||
static_assert(isBool || isVoid,
|
"The passed handler doesn't fulfill these requirements.");
|
||||||
"Sync element: The synchronous function has to return void or bool.");
|
return [=] {
|
||||||
if constexpr (isBool) {
|
if constexpr (isB) {
|
||||||
return onGroupSetup([function] { return function() ? SetupResult::StopWithSuccess
|
return std::invoke(handler) ? SetupResult::StopWithSuccess
|
||||||
: SetupResult::StopWithError; });
|
: SetupResult::StopWithError;
|
||||||
}
|
}
|
||||||
return onGroupSetup([function] { function(); return SetupResult::StopWithSuccess; });
|
std::invoke(handler);
|
||||||
|
return SetupResult::StopWithSuccess;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -401,49 +409,39 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<typename Handler>
|
template <typename Handler>
|
||||||
static GroupItem::TaskSetupHandler wrapSetup(Handler &&handler) {
|
static GroupItem::TaskSetupHandler wrapSetup(Handler &&handler) {
|
||||||
if constexpr (std::is_same_v<Handler, SetupFunction>)
|
if constexpr (std::is_same_v<Handler, SetupFunction>)
|
||||||
return {}; // When user passed {} for the setup handler.
|
return {}; // When user passed {} for the setup handler.
|
||||||
static constexpr bool isDynamic
|
// S, V stands for: [S]etupResult, [V]oid
|
||||||
= std::is_same_v<SetupResult, std::invoke_result_t<std::decay_t<Handler>, Task &>>;
|
static constexpr bool isS = isInvocable<SetupResult, Handler, Task &>();
|
||||||
constexpr bool isVoid
|
static constexpr bool isV = isInvocable<void, Handler, Task &>();
|
||||||
= std::is_same_v<void, std::invoke_result_t<std::decay_t<Handler>, Task &>>;
|
static_assert(isS || isV,
|
||||||
static_assert(isDynamic || isVoid,
|
"Task setup handler needs to take (Task &) as an argument and has to return void or "
|
||||||
"Task setup handler needs to take (Task &) as an argument and has to return "
|
"SetupResult. The passed handler doesn't fulfill these requirements.");
|
||||||
"void or SetupResult. The passed handler doesn't fulfill these requirements.");
|
|
||||||
return [=](TaskInterface &taskInterface) {
|
return [=](TaskInterface &taskInterface) {
|
||||||
Adapter &adapter = static_cast<Adapter &>(taskInterface);
|
Adapter &adapter = static_cast<Adapter &>(taskInterface);
|
||||||
if constexpr (isDynamic)
|
if constexpr (isS)
|
||||||
return std::invoke(handler, *adapter.task());
|
return std::invoke(handler, *adapter.task());
|
||||||
std::invoke(handler, *adapter.task());
|
std::invoke(handler, *adapter.task());
|
||||||
return SetupResult::Continue;
|
return SetupResult::Continue;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Handler>
|
template <typename Handler>
|
||||||
static GroupItem::TaskDoneHandler wrapDone(Handler &&handler) {
|
static GroupItem::TaskDoneHandler wrapDone(Handler &&handler) {
|
||||||
if constexpr (std::is_same_v<Handler, DoneFunction>)
|
if constexpr (std::is_same_v<Handler, DoneFunction>)
|
||||||
return {}; // When user passed {} for the done handler.
|
return {}; // When user passed {} for the done handler.
|
||||||
static constexpr bool isBTD // stands for [B]ool, [T]ask, [D]oneWith
|
// B, V, T, D stands for: [B]ool, [V]oid, [T]ask, [D]oneWith
|
||||||
= std::is_invocable_r_v<bool, std::decay_t<Handler>, const Task &, DoneWith>;
|
static constexpr bool isBTD = isInvocable<bool, Handler, const Task &, DoneWith>();
|
||||||
static constexpr bool isBT
|
static constexpr bool isBT = isInvocable<bool, Handler, const Task &>();
|
||||||
= std::is_invocable_r_v<bool, std::decay_t<Handler>, const Task &>;
|
static constexpr bool isBD = isInvocable<bool, Handler, DoneWith>();
|
||||||
static constexpr bool isBD
|
static constexpr bool isB = isInvocable<bool, Handler>();
|
||||||
= std::is_invocable_r_v<bool, std::decay_t<Handler>, DoneWith>;
|
static constexpr bool isVTD = isInvocable<void, Handler, const Task &, DoneWith>();
|
||||||
static constexpr bool isB
|
static constexpr bool isVT = isInvocable<void, Handler, const Task &>();
|
||||||
= std::is_invocable_r_v<bool, std::decay_t<Handler>>;
|
static constexpr bool isVD = isInvocable<void, Handler, DoneWith>();
|
||||||
static constexpr bool isVTD // stands for [V]oid, [T]ask, [D]oneWith
|
static constexpr bool isV = isInvocable<void, Handler>();
|
||||||
= std::is_invocable_r_v<void, std::decay_t<Handler>, const Task &, DoneWith>;
|
static_assert(isBTD || isBT || isBD || isB || isVTD || isVT || isVD || isV,
|
||||||
static constexpr bool isVT
|
|
||||||
= std::is_invocable_r_v<void, std::decay_t<Handler>, const Task &>;
|
|
||||||
static constexpr bool isVD
|
|
||||||
= std::is_invocable_r_v<void, std::decay_t<Handler>, DoneWith>;
|
|
||||||
static constexpr bool isV
|
|
||||||
= std::is_invocable_r_v<void, std::decay_t<Handler>>;
|
|
||||||
static constexpr bool isInvocable = isBTD || isBT || isBD || isB
|
|
||||||
|| isVTD || isVT || isVD || isV;
|
|
||||||
static_assert(isInvocable,
|
|
||||||
"Task done handler needs to take (const Task &, DoneWith), (const Task &), "
|
"Task done handler needs to take (const Task &, DoneWith), (const Task &), "
|
||||||
"(DoneWith) or (void) as arguments and has to return void or bool. "
|
"(DoneWith) or (void) as arguments and has to return void or bool. "
|
||||||
"The passed handler doesn't fulfill these requirements.");
|
"The passed handler doesn't fulfill these requirements.");
|
||||||
@@ -503,23 +501,19 @@ public:
|
|||||||
|
|
||||||
template <typename StorageStruct, typename StorageHandler>
|
template <typename StorageStruct, typename StorageHandler>
|
||||||
void onStorageSetup(const TreeStorage<StorageStruct> &storage, StorageHandler &&handler) {
|
void onStorageSetup(const TreeStorage<StorageStruct> &storage, StorageHandler &&handler) {
|
||||||
constexpr bool isInvocable = std::is_invocable_v<std::decay_t<StorageHandler>,
|
static_assert(std::is_invocable_v<std::decay_t<StorageHandler>, StorageStruct &>,
|
||||||
StorageStruct &>;
|
|
||||||
static_assert(isInvocable,
|
|
||||||
"Storage setup handler needs to take (Storage &) as an argument. "
|
"Storage setup handler needs to take (Storage &) as an argument. "
|
||||||
"The passed handler doesn't fulfill these requirements.");
|
"The passed handler doesn't fulfill this requirement.");
|
||||||
setupStorageHandler(storage,
|
setupStorageHandler(storage,
|
||||||
wrapHandler<StorageStruct>(std::forward<StorageHandler>(handler)), {});
|
wrapHandler<StorageStruct>(std::forward<StorageHandler>(handler)), {});
|
||||||
}
|
}
|
||||||
template <typename StorageStruct, typename StorageHandler>
|
template <typename StorageStruct, typename StorageHandler>
|
||||||
void onStorageDone(const TreeStorage<StorageStruct> &storage, StorageHandler &&handler) {
|
void onStorageDone(const TreeStorage<StorageStruct> &storage, StorageHandler &&handler) {
|
||||||
constexpr bool isInvocable = std::is_invocable_v<std::decay_t<StorageHandler>,
|
static_assert(std::is_invocable_v<std::decay_t<StorageHandler>, const StorageStruct &>,
|
||||||
const StorageStruct &>;
|
|
||||||
static_assert(isInvocable,
|
|
||||||
"Storage done handler needs to take (const Storage &) as an argument. "
|
"Storage done handler needs to take (const Storage &) as an argument. "
|
||||||
"The passed handler doesn't fulfill these requirements.");
|
"The passed handler doesn't fulfill this requirement.");
|
||||||
setupStorageHandler(storage,
|
setupStorageHandler(storage, {},
|
||||||
{}, wrapHandler<StorageStruct>(std::forward<StorageHandler>(handler)));
|
wrapHandler<const StorageStruct>(std::forward<StorageHandler>(handler)));
|
||||||
}
|
}
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@@ -536,7 +530,7 @@ private:
|
|||||||
template <typename StorageStruct, typename StorageHandler>
|
template <typename StorageStruct, typename StorageHandler>
|
||||||
StorageVoidHandler wrapHandler(StorageHandler &&handler) {
|
StorageVoidHandler wrapHandler(StorageHandler &&handler) {
|
||||||
return [=](void *voidStruct) {
|
return [=](void *voidStruct) {
|
||||||
StorageStruct *storageStruct = static_cast<StorageStruct *>(voidStruct);
|
auto *storageStruct = static_cast<StorageStruct *>(voidStruct);
|
||||||
std::invoke(handler, *storageStruct);
|
std::invoke(handler, *storageStruct);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -139,28 +139,89 @@ void tst_Tasking::validConstructs()
|
|||||||
|
|
||||||
// When turning each of below blocks on, you should see the specific compiler error message.
|
// When turning each of below blocks on, you should see the specific compiler error message.
|
||||||
|
|
||||||
|
// Sync handler needs to take no arguments and has to return void or bool.
|
||||||
#if 0
|
#if 0
|
||||||
{
|
Sync([] { return 7; });
|
||||||
// "Sync element: The synchronous function has to return void or bool."
|
#endif
|
||||||
const auto setupSync = [] { return 3; };
|
#if 0
|
||||||
const Sync sync(setupSync);
|
Sync([](int) { });
|
||||||
}
|
#endif
|
||||||
|
#if 0
|
||||||
|
Sync([](int) { return true; });
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Group setup handler needs to take no arguments and has to return void or SetupResult.
|
||||||
#if 0
|
#if 0
|
||||||
{
|
onGroupSetup([] { return 7; });
|
||||||
// "Sync element: The synchronous function can't take any arguments."
|
#endif
|
||||||
const auto setupSync = [](int) { };
|
#if 0
|
||||||
const Sync sync(setupSync);
|
onGroupSetup([](int) { });
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Group done handler needs to take (DoneWith) or (void) as an argument and has to
|
||||||
|
// return void or bool.
|
||||||
#if 0
|
#if 0
|
||||||
{
|
onGroupDone([] { return 7; });
|
||||||
// "Sync element: The synchronous function can't take any arguments."
|
#endif
|
||||||
const auto setupSync = [](int) { return true; };
|
#if 0
|
||||||
const Sync sync(setupSync);
|
onGroupDone([](DoneWith) { return 7; });
|
||||||
}
|
#endif
|
||||||
|
#if 0
|
||||||
|
onGroupDone([](int) { });
|
||||||
|
#endif
|
||||||
|
#if 0
|
||||||
|
onGroupDone([](DoneWith, int) { });
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Task setup handler needs to take (Task &) as an argument and has to return void or
|
||||||
|
// SetupResult.
|
||||||
|
#if 0
|
||||||
|
TestTask([] {});
|
||||||
|
#endif
|
||||||
|
#if 0
|
||||||
|
TestTask([] { return 7; });
|
||||||
|
#endif
|
||||||
|
#if 0
|
||||||
|
TestTask([](TaskObject &) { return 7; });
|
||||||
|
#endif
|
||||||
|
#if 0
|
||||||
|
TestTask([](TaskObject &, int) { return SetupResult::Continue; });
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Task done handler needs to take (const Task &, DoneWith), (const Task &),
|
||||||
|
// (DoneWith) or (void) as arguments and has to return void or bool.
|
||||||
|
#if 0
|
||||||
|
TestTask({}, [](const TaskObject &, DoneWith) { return 7; });
|
||||||
|
#endif
|
||||||
|
#if 0
|
||||||
|
TestTask({}, [](const TaskObject &) { return 7; });
|
||||||
|
#endif
|
||||||
|
#if 0
|
||||||
|
TestTask({}, [] { return 7; });
|
||||||
|
#endif
|
||||||
|
#if 0
|
||||||
|
TestTask({}, [](const TaskObject &, DoneWith, int) {});
|
||||||
|
#endif
|
||||||
|
#if 0
|
||||||
|
TestTask({}, [](const TaskObject &, int) {});
|
||||||
|
#endif
|
||||||
|
#if 0
|
||||||
|
TestTask({}, [](DoneWith, int) {});
|
||||||
|
#endif
|
||||||
|
#if 0
|
||||||
|
TestTask({}, [](int) {});
|
||||||
|
#endif
|
||||||
|
#if 0
|
||||||
|
TestTask({}, [](const TaskObject &, DoneWith, int) { return true; });
|
||||||
|
#endif
|
||||||
|
#if 0
|
||||||
|
TestTask({}, [](const TaskObject &, int) { return true; });
|
||||||
|
#endif
|
||||||
|
#if 0
|
||||||
|
TestTask({}, [](DoneWith, int) { return true; });
|
||||||
|
#endif
|
||||||
|
#if 0
|
||||||
|
TestTask({}, [](int) { return true; });
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user