Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Implement QPromise<T>::convert<U>() #41

Merged
merged 13 commits into from
Nov 22, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/qtpromise/qpromise.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ class QPromiseBase

inline QPromise<T> wait() const;

template<typename U>
inline QPromise<U> as() const;

public: // STATIC
template<typename E>
inline static QPromise<T> reject(E&& error);
Expand Down
7 changes: 7 additions & 0 deletions src/qtpromise/qpromise.inl
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,13 @@ inline QPromise<T> QPromiseBase<T>::wait() const
return *this;
}

template<typename T>
template<typename U>
inline QPromise<U> QPromiseBase<T>::as() const
{
return QtPromisePrivate::PromiseConverter<T, U>::call(*this);
}

template<typename T>
template<typename E>
inline QPromise<T> QPromiseBase<T>::reject(E&& error)
Expand Down
3 changes: 3 additions & 0 deletions src/qtpromise/qpromise_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <QtCore/QSharedData>
#include <QtCore/QSharedPointer>
#include <QtCore/QThread>
#include <QtCore/QVariant>
#include <QtCore/QVector>

namespace QtPromise {
Expand All @@ -30,6 +31,8 @@ class QPromiseResolve;
template<typename T>
class QPromiseReject;

class QPromiseConversionException;

} // namespace QtPromise

namespace QtPromisePrivate {
Expand Down
10 changes: 10 additions & 0 deletions src/qtpromise/qpromiseexceptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@ class QPromiseUndefinedException : public QException
}
};

class QPromiseConversionException : public QException
{
public:
void raise() const Q_DECL_OVERRIDE { throw *this; }
QPromiseConversionException* clone() const Q_DECL_OVERRIDE
{
return new QPromiseConversionException{*this};
}
};

// QPromiseError is provided for backward compatibility and will be
// removed in the next major version: it wasn't intended to be used
// directly and thus should not be part of the public API.
Expand Down
55 changes: 55 additions & 0 deletions src/qtpromise/qpromisehelpers_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,61 @@ void connectDestroyedToReject(const QtPromise::QPromiseConnections& connections,
});
}

template<typename T, typename U>
struct AreArithmeticTypes
: std::integral_constant<bool, std::is_arithmetic<T>::value && std::is_arithmetic<U>::value>
{ };

template<typename From, typename To, bool = AreArithmeticTypes<From, To>::value>
struct PromiseConverter;

template<typename From, typename To>
struct PromiseConverter<From, To, false>
{
static inline QtPromise::QPromise<To> call(const QtPromise::QPromise<From>& p)
{
return p.then([](const From& value) {
return To{value};
});
}
};

template<typename From, typename To>
struct PromiseConverter<From, To, true>
{
static inline QtPromise::QPromise<To> call(const QtPromise::QPromise<From>& p)
{
return p.then([](From value) {
return static_cast<To>(value);
});
}
};

template<typename From>
struct PromiseConverter<From, void, false>
{
static inline QtPromise::QPromise<void> call(const QtPromise::QPromise<From>& p)
{
return p.then([]() {});
}
};

template<typename To>
struct PromiseConverter<QVariant, To, false>
{
static inline QtPromise::QPromise<To> call(const QtPromise::QPromise<QVariant>& p)
{
return p.then([](QVariant value) {
// https://doc.qt.io/qt-5/qvariant.html#using-canconvert-and-convert-consecutively
if (value.canConvert<To>() && value.convert(qMetaTypeId<To>())) {
return value.value<To>();
} else {
throw QtPromise::QPromiseConversionException{};
}
});
}
};

} // namespace QtPromisePrivate

#endif // QTPROMISE_QPROMISEHELPERS_P_H
1 change: 1 addition & 0 deletions tests/auto/qtpromise/qpromise/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ qtpromise_add_tests(qpromise
tst_tapfail.cpp
tst_then.cpp
tst_timeout.cpp
tst_as.cpp
)
103 changes: 103 additions & 0 deletions tests/auto/qtpromise/qpromise/tst_as.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright (c) Simon Brunel, https://github.com/simonbrunel
* Copyright (c) Dmitriy Purgin, https://github.com/dpurgin
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this additional copyright line actually mean?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've modified a substantial portion of the file (the 'substantiality' is of course subjective, but, in fact, this file has been added by me), so the line refers to this file. Would you prefer another format to address the others? Like one line 'Simon Brunel and contributors'?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand why you added it only in this file, but what does that mean legally at the file level but also at the project level?

Anyway, I don't want to have to maintain different copyright in each file and keep track of who created a file or who changed most of it. The contribution history is available in git and easily accessible in many places in GitHub (contributors, blame, etc.). I also want the same header in all files but I'm not sure yet how I want to change it. Let me think about it, however I would like to make this change in a separate commit outside this PR.

*
* This source code is licensed under the MIT license found in
* the LICENSE file in the root directory of this source tree.
*/

#include "../shared/utils.h"

#include <QtPromise>
#include <QtTest>

#include <chrono>

using namespace QtPromise;

class tst_qpromise_as : public QObject
{
Q_OBJECT

private Q_SLOTS:
void fulfillIntAsReal();
void fulfillRealAsInt();
void fulfillIntAsVoid();
void fulfillQVariantAsInt();
void fulfillQVariantQStringAsInt();
void fulfillQByteArrayAsQString();

void rejectUnconvertibleQVariant();
};

QTEST_MAIN(tst_qpromise_as)
#include "tst_as.moc"

void tst_qpromise_as::fulfillIntAsReal()
{
auto p = QtPromise::resolve(42).as<qreal>();

Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<qreal>>::value));

QCOMPARE(waitForValue(p, -1.0), 42.0);
QVERIFY(p.isFulfilled());
}

void tst_qpromise_as::fulfillRealAsInt()
{
auto p = QtPromise::resolve(42.13).as<int>();

Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));

QCOMPARE(waitForValue(p, -1), 42);
QVERIFY(p.isFulfilled());
}

void tst_qpromise_as::fulfillIntAsVoid()
{
auto p = QtPromise::resolve(42).as<void>();

Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));

QCOMPARE(waitForValue(p, -1, 42), 42);
QVERIFY(p.isFulfilled());
}

void tst_qpromise_as::fulfillQVariantAsInt()
{
auto p = QtPromise::resolve(QVariant{42}).as<int>();

Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));

QCOMPARE(waitForValue(p, -1), 42);
QVERIFY(p.isFulfilled());
}

void tst_qpromise_as::fulfillQVariantQStringAsInt()
{
auto p = QtPromise::resolve(QVariant{"42"}).as<int>();

Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));

QCOMPARE(waitForValue(p, -1), 42);
QVERIFY(p.isFulfilled());
}

void tst_qpromise_as::fulfillQByteArrayAsQString()
{
auto p = QtPromise::resolve(QByteArray{"foo"}).as<QString>();

Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QString>>::value));

QCOMPARE(waitForValue(p, {}), QString{"foo"});
QVERIFY(p.isFulfilled());
}

void tst_qpromise_as::rejectUnconvertibleQVariant()
{
auto p = QtPromise::resolve(QVariant{"42foo"}).as<int>();

Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));

QVERIFY(waitForRejected<QPromiseConversionException>(p));
}