From 78417b58139f78e1293a5f58c2d1e61a922dbe82 Mon Sep 17 00:00:00 2001 From: Simon Brunel Date: Thu, 6 Feb 2020 22:31:36 +0100 Subject: [PATCH] Fix support for std::function as continuation handler --- src/qtpromise/qpromise.h | 2 +- src/qtpromise/qpromise.inl | 2 +- src/qtpromise/qpromiseglobal.h | 88 ++++------ tests/auto/qtpromise/CMakeLists.txt | 1 + tests/auto/qtpromise/internals/CMakeLists.txt | 4 + tests/auto/qtpromise/internals/tst_argsof.cpp | 139 +++++++++++++++ .../auto/qtpromise/qpromise/fail/tst_fail.cpp | 155 +++++++++++++++++ .../auto/qtpromise/qpromise/then/tst_then.cpp | 160 +++++++++++++++++- 8 files changed, 493 insertions(+), 58 deletions(-) create mode 100644 tests/auto/qtpromise/internals/CMakeLists.txt create mode 100644 tests/auto/qtpromise/internals/tst_argsof.cpp diff --git a/src/qtpromise/qpromise.h b/src/qtpromise/qpromise.h index 0f81721..50959f1 100644 --- a/src/qtpromise/qpromise.h +++ b/src/qtpromise/qpromise.h @@ -27,7 +27,7 @@ public: template ::count == 1, int>::type = 0> inline QPromiseBase(F resolver); - template ::count != 1, int>::type = 0> + template ::count == 2, int>::type = 0> inline QPromiseBase(F resolver); QPromiseBase(const QPromiseBase& other): m_d(other.m_d) {} diff --git a/src/qtpromise/qpromise.inl b/src/qtpromise/qpromise.inl index a6e2b5c..00e76a0 100644 --- a/src/qtpromise/qpromise.inl +++ b/src/qtpromise/qpromise.inl @@ -30,7 +30,7 @@ inline QPromiseBase::QPromiseBase(F callback) } template -template ::count != 1, int>::type> +template ::count == 2, int>::type> inline QPromiseBase::QPromiseBase(F callback) : m_d(new QtPromisePrivate::PromiseData()) { diff --git a/src/qtpromise/qpromiseglobal.h b/src/qtpromise/qpromiseglobal.h index 3170485..b3af3c7 100644 --- a/src/qtpromise/qpromiseglobal.h +++ b/src/qtpromise/qpromiseglobal.h @@ -28,12 +28,8 @@ using Unqualified = typename std::remove_cv::t template struct HasCallOperator { - template - static char check(decltype(&U::operator(), char(0))); - - template - static char (&check(...))[2]; - + template static char check(decltype(&U::operator(), char(0))); + template static char (&check(...))[2]; static const bool value = (sizeof(check(0)) == 1); }; @@ -58,73 +54,57 @@ struct ArgsTraits<> static const size_t count = 0; }; -template -struct ArgsOf : public ArgsTraits<> +// Fallback implementation (type not supported). +template +struct ArgsOf { }; +// Partial specialization for null function. +template <> +struct ArgsOf : public ArgsTraits<> +{ }; + +// Partial specialization for type with a non-overloaded operator(). +// This applies to lambda, std::function but not to std::bind result. template struct ArgsOf::value>::type> : public ArgsOf { }; -template -struct ArgsOf : public ArgsTraits -{ }; - -template -struct ArgsOf : public ArgsTraits -{ }; - -template -struct ArgsOf : public ArgsTraits -{ }; - -template -struct ArgsOf : public ArgsTraits -{ }; - -template -struct ArgsOf : public ArgsTraits -{ }; - -template -struct ArgsOf : public ArgsTraits -{ }; - -template -struct ArgsOf> : public ArgsOf -{ }; - +// Partial specialization to remove reference and rvalue (e.g. lambda, std::function, etc.). template struct ArgsOf : public ArgsOf { }; -template -struct ArgsOf : public ArgsOf -{ }; - -template -struct ArgsOf : public ArgsOf -{ }; - -template -struct ArgsOf : public ArgsOf -{ }; - template struct ArgsOf : public ArgsOf { }; -template -struct ArgsOf : public ArgsOf +// Partial specialization for function type. +template +struct ArgsOf : public ArgsTraits { }; -template -struct ArgsOf : public ArgsOf +// Partial specialization for function pointer. +template +struct ArgsOf : public ArgsTraits { }; -template -struct ArgsOf : public ArgsOf +// Partial specialization for pointer-to-member-function (i.e. operator()'s). +template +struct ArgsOf : public ArgsTraits +{ }; + +template +struct ArgsOf : public ArgsTraits +{ }; + +template +struct ArgsOf : public ArgsTraits +{ }; + +template +struct ArgsOf : public ArgsTraits { }; } // namespace QtPromisePrivate diff --git a/tests/auto/qtpromise/CMakeLists.txt b/tests/auto/qtpromise/CMakeLists.txt index bef7671..fbcecf5 100644 --- a/tests/auto/qtpromise/CMakeLists.txt +++ b/tests/auto/qtpromise/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory(deprecations) add_subdirectory(exceptions) add_subdirectory(future) add_subdirectory(helpers) +add_subdirectory(internals) add_subdirectory(qpromise) add_subdirectory(qpromiseconnections) add_subdirectory(requirements) diff --git a/tests/auto/qtpromise/internals/CMakeLists.txt b/tests/auto/qtpromise/internals/CMakeLists.txt new file mode 100644 index 0000000..074aac8 --- /dev/null +++ b/tests/auto/qtpromise/internals/CMakeLists.txt @@ -0,0 +1,4 @@ +qtpromise_add_test(internals_argsof + SOURCES + tst_argsof.cpp +) diff --git a/tests/auto/qtpromise/internals/tst_argsof.cpp b/tests/auto/qtpromise/internals/tst_argsof.cpp new file mode 100644 index 0000000..41c084c --- /dev/null +++ b/tests/auto/qtpromise/internals/tst_argsof.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) Simon Brunel, https://github.com/simonbrunel + * + * This source code is licensed under the MIT license found in + * the LICENSE file in the root directory of this source tree. + */ + +// QtPromise +#include + +// Qt +#include + +using namespace QtPromisePrivate; + +class tst_internals_argsof : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void nullFunction(); + void basicFunction(); + void callOperator(); + void stdFunction(); + void lambda(); + +}; // class tst_argsof + +QTEST_MAIN(tst_internals_argsof) +#include "tst_argsof.moc" + +#define TEST_ARGS_FOR_TYPE(T, E) \ + Q_STATIC_ASSERT((std::is_same::first, E>::value)); \ + Q_STATIC_ASSERT((std::is_same::first, E>::value)); \ + Q_STATIC_ASSERT((std::is_same::first, E>::value)); \ + Q_STATIC_ASSERT((std::is_same::first, E>::value)); \ + Q_STATIC_ASSERT((std::is_same::first, E>::value)); \ + Q_STATIC_ASSERT((std::is_same::first, E>::value)); \ + Q_STATIC_ASSERT((std::is_same::first, E>::value)); \ + Q_STATIC_ASSERT((std::is_same::first, E>::value)); \ + Q_STATIC_ASSERT((std::is_same::first, E>::value)); \ + Q_STATIC_ASSERT((std::is_same::first, E>::value)); \ + Q_STATIC_ASSERT((std::is_same::first, E>::value)); \ + Q_STATIC_ASSERT((std::is_same::first, E>::value)); + +namespace { + +const float kRes = 0.42f; + +float fnNoArg() { return kRes; } +float fnOneArg(float v) { return v; } +float fnManyArgs(const float& v0, int, char*) { return v0; } + +struct OpNoArg { float operator()() { return kRes; } }; +struct OpOneArg { float operator()(float v) { return v; } }; +struct OpManyArgs { float operator()(const float& v, int, char*) { return v; } }; + +struct OpCNoArg { float operator()() const { return kRes; } }; +struct OpCOneArg { float operator()(float v) const { return v; } }; +struct OpCManyArgs { float operator()(const float& v, int, char*) const { return v; } }; + +struct OpVNoArg { float operator()() volatile { return kRes; } }; +struct OpVOneArg { float operator()(float v) volatile { return v; } }; +struct OpVManyArgs { float operator()(const float& v, int, char*) volatile { return v; } }; + +struct OpCVNoArg { float operator()() const volatile { return kRes; } }; +struct OpCVOneArg { float operator()(float v) const volatile { return v; } }; +struct OpCVManyArgs { float operator()(const float& v, int, char*) const volatile { return v; } }; + +} // namespace + +void tst_internals_argsof::initTestCase() +{ + Q_UNUSED(fnNoArg()) + Q_UNUSED(fnOneArg(42)) + Q_UNUSED(fnManyArgs(42, 42, nullptr)) +} + +void tst_internals_argsof::nullFunction() +{ + Q_STATIC_ASSERT((std::is_same::first, void>::value)); +} + +void tst_internals_argsof::basicFunction() +{ + // Function type. + Q_STATIC_ASSERT((std::is_same::first, void>::value)); + Q_STATIC_ASSERT((std::is_same::first, float>::value)); + Q_STATIC_ASSERT((std::is_same::first, const float&>::value)); + + // Function pointer type. + Q_STATIC_ASSERT((std::is_same::first, void>::value)); + Q_STATIC_ASSERT((std::is_same::first, float>::value)); + Q_STATIC_ASSERT((std::is_same::first, const float&>::value)); +} + +void tst_internals_argsof::callOperator() +{ + // non-const + TEST_ARGS_FOR_TYPE(OpNoArg, void); + TEST_ARGS_FOR_TYPE(OpOneArg, float); + TEST_ARGS_FOR_TYPE(OpManyArgs, const float&); + + // const + TEST_ARGS_FOR_TYPE(OpCNoArg, void); + TEST_ARGS_FOR_TYPE(OpCOneArg, float); + TEST_ARGS_FOR_TYPE(OpCManyArgs, const float&); + + // volatile + TEST_ARGS_FOR_TYPE(OpVNoArg, void); + TEST_ARGS_FOR_TYPE(OpVOneArg, float); + TEST_ARGS_FOR_TYPE(OpVManyArgs, const float&); + + // const volatile + TEST_ARGS_FOR_TYPE(OpCVNoArg, void); + TEST_ARGS_FOR_TYPE(OpCVOneArg, float); + TEST_ARGS_FOR_TYPE(OpCVManyArgs, const float&); +} + +void tst_internals_argsof::stdFunction() +{ + TEST_ARGS_FOR_TYPE(std::function, void); + TEST_ARGS_FOR_TYPE(std::function, float); + TEST_ARGS_FOR_TYPE(std::function, const float&); +} + +void tst_internals_argsof::lambda() +{ + auto lNoArg = []() {}; + auto lOneArg = [](float) {}; + auto lManyArgs = [](const float&, int, char**) {}; + auto lMutable = [](const float&, int, char**) mutable {}; + + TEST_ARGS_FOR_TYPE(decltype(lNoArg), void); + TEST_ARGS_FOR_TYPE(decltype(lOneArg), float); + TEST_ARGS_FOR_TYPE(decltype(lManyArgs), const float&); + TEST_ARGS_FOR_TYPE(decltype(lMutable), const float&); +} diff --git a/tests/auto/qtpromise/qpromise/fail/tst_fail.cpp b/tests/auto/qtpromise/qpromise/fail/tst_fail.cpp index fc89f30..a77aabc 100644 --- a/tests/auto/qtpromise/qpromise/fail/tst_fail.cpp +++ b/tests/auto/qtpromise/qpromise/fail/tst_fail.cpp @@ -24,11 +24,44 @@ private Q_SLOTS: void baseClass(); void catchAll(); // TODO: sync / async + void functionPtrHandlers(); + void stdFunctionHandlers(); + void stdBindHandlers(); + void lambdaHandlers(); }; QTEST_MAIN(tst_qpromise_fail) #include "tst_fail.moc" +namespace { + +const QString kErr = "0.42"; +const float kRes = 0.42f; +const float kFail = -1.f; + +float fnNoArg() { return kErr.toFloat(); } +float fnArgByVal(QString e) { return e.toFloat(); } +float fnArgByRef(const QString& e) { return e.toFloat(); } + +class Klass { +public: // STATICS + static float kFnNoArg() { return kErr.toFloat(); } + static float kFnArgByVal(QString e) { return e.toFloat(); } + static float kFnArgByRef(const QString& e) { return e.toFloat(); } + +public: + Klass(float v) : m_v{v} {} + + float fnNoArg() const { return m_v; } + float fnArgByVal(QString v) const { return v.toFloat() + m_v; } + float fnArgByRef(const QString& v) const { return v.toFloat() + m_v; } + +private: + const float m_v; +}; + +} // namespace + void tst_qpromise_fail::sameType() { // http://en.cppreference.com/w/cpp/error/exception @@ -87,3 +120,125 @@ void tst_qpromise_fail::catchAll() QCOMPARE(error, QString("bar")); } + +void tst_qpromise_fail::functionPtrHandlers() +{ + { // Global functions. + auto p0 = QPromise::reject(kErr).fail(&fnNoArg); + auto p1 = QPromise::reject(kErr).fail(&fnArgByVal); + auto p2 = QPromise::reject(kErr).fail(&fnArgByRef); + + QCOMPARE(waitForValue(p0, kFail), kRes); + QCOMPARE(waitForValue(p1, kFail), kRes); + QCOMPARE(waitForValue(p2, kFail), kRes); + } + { // Static member functions. + auto p0 = QPromise::reject(kErr).fail(&Klass::kFnNoArg); + auto p1 = QPromise::reject(kErr).fail(&Klass::kFnArgByVal); + auto p2 = QPromise::reject(kErr).fail(&Klass::kFnArgByRef); + + QCOMPARE(waitForValue(p0, kFail), kRes); + QCOMPARE(waitForValue(p1, kFail), kRes); + QCOMPARE(waitForValue(p2, kFail), kRes); + } +} + +// https://github.com/simonbrunel/qtpromise/issues/29 +void tst_qpromise_fail::stdFunctionHandlers() +{ + { // lvalue. + std::function stdFnNoArg = fnNoArg; + std::function stdFnArgByVal = fnArgByVal; + std::function stdFnArgByRef = fnArgByRef; + + auto p0 = QPromise::reject(kErr).fail(stdFnNoArg); + auto p1 = QPromise::reject(kErr).fail(stdFnArgByVal); + auto p2 = QPromise::reject(kErr).fail(stdFnArgByRef); + + QCOMPARE(waitForValue(p0, kFail), kRes); + QCOMPARE(waitForValue(p1, kFail), kRes); + QCOMPARE(waitForValue(p2, kFail), kRes); + } + { // const lvalue. + const std::function stdFnNoArg = fnNoArg; + const std::function stdFnArgByVal = fnArgByVal; + const std::function stdFnArgByRef = fnArgByRef; + + auto p0 = QPromise::reject(kErr).fail(stdFnNoArg); + auto p1 = QPromise::reject(kErr).fail(stdFnArgByVal); + auto p2 = QPromise::reject(kErr).fail(stdFnArgByRef); + + QCOMPARE(waitForValue(p0, kFail), kRes); + QCOMPARE(waitForValue(p1, kFail), kRes); + QCOMPARE(waitForValue(p2, kFail), kRes); + } + { // rvalue. + auto p0 = QPromise::reject(kErr).fail(std::function{fnNoArg}); + auto p1 = QPromise::reject(kErr).fail(std::function{fnArgByVal}); + auto p2 = QPromise::reject(kErr).fail(std::function{fnArgByRef}); + + QCOMPARE(waitForValue(p0, kFail), kRes); + QCOMPARE(waitForValue(p1, kFail), kRes); + QCOMPARE(waitForValue(p2, kFail), kRes); + } +} + +//// https://github.com/simonbrunel/qtpromise/issues/29 +void tst_qpromise_fail::stdBindHandlers() +{ + using namespace std::placeholders; + + const float val{42.f}; + const Klass obj{val}; + + const std::function bindNoArg = std::bind(&Klass::fnNoArg, &obj); + const std::function bindArgByVal = std::bind(&Klass::fnArgByVal, &obj, _1); + const std::function bindArgByRef = std::bind(&Klass::fnArgByRef, &obj, _1); + + auto p0 = QPromise::reject(kErr).fail(bindNoArg); + auto p1 = QPromise::reject(kErr).fail(bindArgByVal); + auto p2 = QPromise::reject(kErr).fail(bindArgByRef); + + QCOMPARE(waitForValue(p0, kFail), val); + QCOMPARE(waitForValue(p1, kFail), val + kRes); + QCOMPARE(waitForValue(p2, kFail), val + kRes); +} + +void tst_qpromise_fail::lambdaHandlers() +{ + { // lvalue. + auto lambdaNoArg = []() { return kRes; }; + auto lambdaArgByVal = [](QString v) { return v.toFloat(); }; + auto lambdaArgByRef = [](const QString& v) { return v.toFloat(); }; + + auto p0 = QPromise::reject(kErr).fail(lambdaNoArg); + auto p1 = QPromise::reject(kErr).fail(lambdaArgByVal); + auto p2 = QPromise::reject(kErr).fail(lambdaArgByRef); + + QCOMPARE(waitForValue(p0, kFail), kRes); + QCOMPARE(waitForValue(p1, kFail), kRes); + QCOMPARE(waitForValue(p2, kFail), kRes); + } + { // const lvalue. + const auto lambdaNoArg = []() { return kRes; }; + const auto lambdaArgByVal = [](QString v) { return v.toFloat(); }; + const auto lambdaArgByRef = [](const QString& v) { return v.toFloat(); }; + + auto p0 = QPromise::reject(kErr).fail(lambdaNoArg); + auto p1 = QPromise::reject(kErr).fail(lambdaArgByVal); + auto p2 = QPromise::reject(kErr).fail(lambdaArgByRef); + + QCOMPARE(waitForValue(p0, kFail), kRes); + QCOMPARE(waitForValue(p1, kFail), kRes); + QCOMPARE(waitForValue(p2, kFail), kRes); + } + { // rvalue. + auto p0 = QPromise::reject(kErr).fail([]() { return kRes; }); + auto p1 = QPromise::reject(kErr).fail([](QString v) { return v.toFloat(); }); + auto p2 = QPromise::reject(kErr).fail([](const QString& v) { return v.toFloat(); }); + + QCOMPARE(waitForValue(p0, kFail), kRes); + QCOMPARE(waitForValue(p1, kFail), kRes); + QCOMPARE(waitForValue(p2, kFail), kRes); + } +} diff --git a/tests/auto/qtpromise/qpromise/then/tst_then.cpp b/tests/auto/qtpromise/qpromise/then/tst_then.cpp index 0e2f943..c4e1b0a 100644 --- a/tests/auto/qtpromise/qpromise/then/tst_then.cpp +++ b/tests/auto/qtpromise/qpromise/then/tst_then.cpp @@ -7,6 +7,8 @@ #include "../../shared/utils.h" +#include + // QtPromise #include @@ -25,12 +27,44 @@ private Q_SLOTS: void rejectSync(); void rejectAsync(); void skipResult(); - void noHandler(); + void nullHandler(); + void functionPtrHandlers(); + void stdFunctionHandlers(); + void stdBindHandlers(); + void lambdaHandlers(); }; QTEST_MAIN(tst_qpromise_then) #include "tst_then.moc" +namespace { + +const float kRes = 0.42f; +const float kFail = -1.f; + +float fnNoArg() { return kRes; } +float fnArgByVal(float v) { return v; } +float fnArgByRef(const float& v) { return v; } + +class Klass { +public: // STATICS + static float kFnNoArg() { return kRes; } + static float kFnArgByVal(float v) { return v; } + static float kFnArgByRef(const float& v) { return v; } + +public: + Klass(float v) : m_v{v} {} + + float fnNoArg() const { return m_v; } + float fnArgByVal(float v) const { return v + m_v; } + float fnArgByRef(const float& v) const { return v + m_v; } + +private: + const float m_v; +}; + +} // namespace + void tst_qpromise_then::resolveSync() { QVariantList values; @@ -115,7 +149,7 @@ void tst_qpromise_then::skipResult() QCOMPARE(value, 43); } -void tst_qpromise_then::noHandler() +void tst_qpromise_then::nullHandler() { { // resolved auto p = QPromise::resolve(42).then(nullptr); @@ -130,3 +164,125 @@ void tst_qpromise_then::noHandler() QCOMPARE(p.isRejected(), true); } } + +void tst_qpromise_then::functionPtrHandlers() +{ + { // Global functions. + auto p0 = QtPromise::resolve().then(&fnNoArg); + auto p1 = QtPromise::resolve(kRes).then(&fnArgByVal); + auto p2 = QtPromise::resolve(kRes).then(&fnArgByRef); + + QCOMPARE(waitForValue(p0, kFail), kRes); + QCOMPARE(waitForValue(p1, kFail), kRes); + QCOMPARE(waitForValue(p2, kFail), kRes); + } + { // Static member functions. + auto p0 = QtPromise::resolve().then(&Klass::kFnNoArg); + auto p1 = QtPromise::resolve(kRes).then(&Klass::kFnArgByVal); + auto p2 = QtPromise::resolve(kRes).then(&Klass::kFnArgByRef); + + QCOMPARE(waitForValue(p0, kFail), kRes); + QCOMPARE(waitForValue(p1, kFail), kRes); + QCOMPARE(waitForValue(p2, kFail), kRes); + } +} + +// https://github.com/simonbrunel/qtpromise/issues/29 +void tst_qpromise_then::stdFunctionHandlers() +{ + { // lvalue. + std::function stdFnNoArg = fnNoArg; + std::function stdFnArgByVal = fnArgByVal; + std::function stdFnArgByRef = fnArgByRef; + + auto p0 = QtPromise::resolve().then(stdFnNoArg); + auto p1 = QtPromise::resolve(kRes).then(stdFnArgByVal); + auto p2 = QtPromise::resolve(kRes).then(stdFnArgByRef); + + QCOMPARE(waitForValue(p0, kFail), kRes); + QCOMPARE(waitForValue(p1, kFail), kRes); + QCOMPARE(waitForValue(p2, kFail), kRes); + } + { // const lvalue. + const std::function stdFnNoArg = fnNoArg; + const std::function stdFnArgByVal = fnArgByVal; + const std::function stdFnArgByRef = fnArgByRef; + + auto p0 = QtPromise::resolve().then(stdFnNoArg); + auto p1 = QtPromise::resolve(kRes).then(stdFnArgByVal); + auto p2 = QtPromise::resolve(kRes).then(stdFnArgByRef); + + QCOMPARE(waitForValue(p0, kFail), kRes); + QCOMPARE(waitForValue(p1, kFail), kRes); + QCOMPARE(waitForValue(p2, kFail), kRes); + } + { // rvalue. + auto p0 = QtPromise::resolve().then(std::function{fnNoArg}); + auto p1 = QtPromise::resolve(kRes).then(std::function{fnArgByVal}); + auto p2 = QtPromise::resolve(kRes).then(std::function{fnArgByRef}); + + QCOMPARE(waitForValue(p0, kFail), kRes); + QCOMPARE(waitForValue(p1, kFail), kRes); + QCOMPARE(waitForValue(p2, kFail), kRes); + } +} + +//// https://github.com/simonbrunel/qtpromise/issues/29 +void tst_qpromise_then::stdBindHandlers() +{ + using namespace std::placeholders; + + const float val{42.f}; + const Klass obj{val}; + + const std::function bindNoArg = std::bind(&Klass::fnNoArg, &obj); + const std::function bindArgByVal = std::bind(&Klass::fnArgByVal, &obj, _1); + const std::function bindArgByRef = std::bind(&Klass::fnArgByRef, &obj, _1); + + auto p0 = QtPromise::resolve().then(bindNoArg); + auto p1 = QtPromise::resolve(kRes).then(bindArgByVal); + auto p2 = QtPromise::resolve(kRes).then(bindArgByRef); + + QCOMPARE(waitForValue(p0, kFail), val); + QCOMPARE(waitForValue(p1, kFail), val + kRes); + QCOMPARE(waitForValue(p2, kFail), val + kRes); +} + +void tst_qpromise_then::lambdaHandlers() +{ + { // lvalue. + auto lambdaNoArg = []() { return kRes; }; + auto lambdaArgByVal = [](float v) { return v; }; + auto lambdaArgByRef = [](const float& v) { return v; }; + + auto p0 = QtPromise::resolve().then(lambdaNoArg); + auto p1 = QtPromise::resolve(kRes).then(lambdaArgByVal); + auto p2 = QtPromise::resolve(kRes).then(lambdaArgByRef); + + QCOMPARE(waitForValue(p0, kFail), kRes); + QCOMPARE(waitForValue(p1, kFail), kRes); + QCOMPARE(waitForValue(p2, kFail), kRes); + } + { // const lvalue. + const auto lambdaNoArg = []() { return kRes; }; + const auto lambdaArgByVal = [](float v) { return v; }; + const auto lambdaArgByRef = [](const float& v) { return v; }; + + auto p0 = QtPromise::resolve().then(lambdaNoArg); + auto p1 = QtPromise::resolve(kRes).then(lambdaArgByVal); + auto p2 = QtPromise::resolve(kRes).then(lambdaArgByRef); + + QCOMPARE(waitForValue(p0, kFail), kRes); + QCOMPARE(waitForValue(p1, kFail), kRes); + QCOMPARE(waitForValue(p2, kFail), kRes); + } + { // rvalue. + auto p0 = QtPromise::resolve().then([]() { return kRes; }); + auto p1 = QtPromise::resolve(kRes).then([](float v) { return v; }); + auto p2 = QtPromise::resolve(kRes).then([](const float& v) { return v; }); + + QCOMPARE(waitForValue(p0, kFail), kRes); + QCOMPARE(waitForValue(p1, kFail), kRes); + QCOMPARE(waitForValue(p2, kFail), kRes); + } +}