add promise unit test.
This commit is contained in:
parent
c82f079bcb
commit
d0a6ac56a4
@ -37,7 +37,7 @@ add_executable(UnitTest main.cpp
|
|||||||
)
|
)
|
||||||
|
|
||||||
target_compile_definitions(UnitTest
|
target_compile_definitions(UnitTest
|
||||||
PUBLIC LOG_FILTER_LEVEL=1
|
PUBLIC LOG_FILTER_LEVEL=2
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(UnitTest
|
target_link_libraries(UnitTest
|
||||||
|
@ -1,9 +1,109 @@
|
|||||||
#include "Promise.h"
|
#include "BoostLog.h"
|
||||||
|
#include "IoContext.h"
|
||||||
|
#include <boost/asio/defer.hpp>
|
||||||
|
#include <boost/asio/io_context.hpp>
|
||||||
|
#include <boost/asio/strand.hpp>
|
||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Promise.h" // 在这之前 #include "IoContext.h" 以支持 delay
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE(PromiseTestCase)
|
||||||
|
|
||||||
|
class PromiseContext {
|
||||||
|
public:
|
||||||
|
PromiseContext() : m_ioContext(std::thread::hardware_concurrency()), m_guarder(m_ioContext.get_executor()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
~PromiseContext() {
|
||||||
|
m_guarder.reset();
|
||||||
|
for (auto &thread : m_threads) {
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void start() {
|
||||||
|
if (m_threads.empty()) {
|
||||||
|
auto threads = std::thread::hardware_concurrency();
|
||||||
|
for (int i = 0; i < threads; i++) {
|
||||||
|
m_threads.emplace_back([this]() { m_ioContext.run(); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setValue(float value) {
|
||||||
|
if (m_value != value) {
|
||||||
|
m_value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline static const float kRes = 0.42f;
|
||||||
|
float functionNoArg() const {
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
float functionArgByVal(float v) const {
|
||||||
|
return v + m_value;
|
||||||
|
}
|
||||||
|
float functionArgByRef(const float &v) const {
|
||||||
|
return v + m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float kFnNoArg() {
|
||||||
|
return kRes;
|
||||||
|
}
|
||||||
|
static float kFnArgByVal(float v) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
static float kFnArgByRef(const float &v) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float stringToFloatNoArg() {
|
||||||
|
return std::stof(kErr);
|
||||||
|
}
|
||||||
|
static float stringToFloatArgByVal(std::string e) {
|
||||||
|
return std::stof(e);
|
||||||
|
}
|
||||||
|
static float stringToFloatArgByRef(const std::string &e) {
|
||||||
|
return std::stof(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Enum1 { Value0, Value1, Value2 };
|
||||||
|
enum class Enum2 { Value0, Value1, Value2 };
|
||||||
|
struct Foo {
|
||||||
|
Foo() = default;
|
||||||
|
Foo(int foo) : m_foo{foo} {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const Foo &rhs) const {
|
||||||
|
return m_foo == rhs.m_foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
int m_foo{-1};
|
||||||
|
};
|
||||||
|
struct Bar {
|
||||||
|
Bar() = default;
|
||||||
|
Bar(const Foo &other) : m_bar{other.m_foo} {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const Bar &rhs) const {
|
||||||
|
return m_bar == rhs.m_bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
int m_bar{-1};
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
boost::asio::io_context m_ioContext;
|
||||||
|
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> m_guarder;
|
||||||
|
|
||||||
|
std::vector<std::thread> m_threads;
|
||||||
|
float m_value = 249.f;
|
||||||
|
|
||||||
|
inline static const std::string kErr{"0.42"};
|
||||||
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static inline T waitForValue(const Promise<T> &promise, const T &initial) {
|
static inline T waitForValue(const Promise<T> &promise, const T &initial) {
|
||||||
T value(initial);
|
T value(initial);
|
||||||
@ -18,6 +118,616 @@ static inline T waitForValue(const Promise<void> &promise, const T &initial, con
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T, typename E>
|
||||||
|
static inline E waitForError(const Promise<T> &promise, const E &initial) {
|
||||||
|
E error(initial);
|
||||||
|
promise
|
||||||
|
.fail([&](const E &err) {
|
||||||
|
error = err;
|
||||||
|
return T();
|
||||||
|
})
|
||||||
|
.wait();
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E>
|
||||||
|
static inline E waitForError(const Promise<void> &promise, const E &initial) {
|
||||||
|
E error(initial);
|
||||||
|
promise.fail([&](const E &err) { error = err; }).wait();
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E, typename T>
|
||||||
|
static inline bool waitForRejected(const T &promise) {
|
||||||
|
bool result = false;
|
||||||
|
promise.tapFail([&](const E &) { result = true; }).wait();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(ConstructResolveSyncOneArg) {
|
||||||
|
Promise<int> p{[](const PromiseResolve<int> &resolve) { resolve(42); }};
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(p.isFulfilled(), true);
|
||||||
|
BOOST_CHECK_EQUAL(waitForError(p, std::string{}), std::string{});
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, -1), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(ConstructResolveSyncOneArgVoid) {
|
||||||
|
Promise<void> p{[](const PromiseResolve<void> &resolve) { resolve(); }};
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(p.isFulfilled(), true);
|
||||||
|
BOOST_CHECK_EQUAL(waitForError(p, std::string{}), std::string{});
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, -1, 42), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(ConstructResolveSyncTwoArgs) {
|
||||||
|
Promise<int> p{[](const PromiseResolve<int> &resolve, const PromiseReject<int> &) { resolve(42); }};
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(p.isFulfilled(), true);
|
||||||
|
BOOST_CHECK_EQUAL(waitForError(p, std::string{}), std::string{});
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, -1), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(ConstructResolveSyncTwoArgsVoid) {
|
||||||
|
Promise<void> p{[](const PromiseResolve<void> &resolve, const PromiseReject<void> &) { resolve(); }};
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(p.isFulfilled(), true);
|
||||||
|
BOOST_CHECK_EQUAL(waitForError(p, std::string{}), std::string{});
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, -1, 42), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(ConstructResolveAsyncOneArg, PromiseContext) {
|
||||||
|
auto id = std::this_thread::get_id();
|
||||||
|
Promise<int> p{[this, &id](const PromiseResolve<int> &resolve) {
|
||||||
|
boost::asio::defer(m_ioContext, [&id, resolve]() {
|
||||||
|
resolve(42);
|
||||||
|
id = std::this_thread::get_id();
|
||||||
|
});
|
||||||
|
}};
|
||||||
|
BOOST_CHECK_EQUAL(p.isPending(), true);
|
||||||
|
start();
|
||||||
|
BOOST_CHECK_EQUAL(waitForError(p, std::string{}), std::string{});
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, -1), 42);
|
||||||
|
BOOST_CHECK_EQUAL(p.isFulfilled(), true);
|
||||||
|
BOOST_CHECK_NE(id, std::this_thread::get_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(ConstructResolveAsyncOneArgVoid, PromiseContext) {
|
||||||
|
auto id = std::this_thread::get_id();
|
||||||
|
Promise<void> p{[&id, this](const PromiseResolve<void> &resolve) {
|
||||||
|
boost::asio::defer(m_ioContext, [&id, resolve]() {
|
||||||
|
resolve();
|
||||||
|
id = std::this_thread::get_id();
|
||||||
|
});
|
||||||
|
}};
|
||||||
|
BOOST_CHECK_EQUAL(p.isPending(), true);
|
||||||
|
start();
|
||||||
|
BOOST_CHECK_EQUAL(waitForError(p, std::string{}), std::string{});
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, -1, 42), 42);
|
||||||
|
BOOST_CHECK_EQUAL(p.isFulfilled(), true);
|
||||||
|
BOOST_CHECK_NE(id, std::this_thread::get_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(ConstructResolveAsyncTwoArgs, PromiseContext) {
|
||||||
|
auto id = std::this_thread::get_id();
|
||||||
|
Promise<int> p{[&id, this](const PromiseResolve<int> &resolve, const PromiseReject<int> &) {
|
||||||
|
boost::asio::defer(m_ioContext, [&id, resolve]() {
|
||||||
|
resolve(42);
|
||||||
|
id = std::this_thread::get_id();
|
||||||
|
});
|
||||||
|
}};
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(p.isPending(), true);
|
||||||
|
start();
|
||||||
|
BOOST_CHECK_EQUAL(waitForError(p, std::string{}), std::string{});
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, -1), 42);
|
||||||
|
BOOST_CHECK_EQUAL(p.isFulfilled(), true);
|
||||||
|
BOOST_CHECK_NE(id, std::this_thread::get_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(ConstructResolveAsyncTwoArgsVoid, PromiseContext) {
|
||||||
|
auto id = std::this_thread::get_id();
|
||||||
|
Promise<void> p{[&id, this](const PromiseResolve<void> &resolve, const PromiseReject<void> &) {
|
||||||
|
boost::asio::defer(m_ioContext, [&id, resolve]() {
|
||||||
|
resolve();
|
||||||
|
id = std::this_thread::get_id();
|
||||||
|
});
|
||||||
|
}};
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(p.isPending(), true);
|
||||||
|
start();
|
||||||
|
BOOST_CHECK_EQUAL(waitForError(p, std::string{}), std::string{});
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, -1, 42), 42);
|
||||||
|
BOOST_CHECK_EQUAL(p.isFulfilled(), true);
|
||||||
|
BOOST_CHECK_NE(id, std::this_thread::get_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(ConstructRejectSync) {
|
||||||
|
Promise<int> p{[](const PromiseResolve<int> &, const PromiseReject<int> &reject) { reject(std::string{"foo"}); }};
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(p.isRejected(), true);
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, -1), -1);
|
||||||
|
BOOST_CHECK_EQUAL(waitForError(p, std::string{}), std::string{"foo"});
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(ConstructRejectSyncVoid) {
|
||||||
|
Promise<void> p{[](const PromiseResolve<void> &, const PromiseReject<void> &reject) { reject(std::string{"foo"}); }};
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(p.isRejected(), true);
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, -1, 42), -1);
|
||||||
|
BOOST_CHECK_EQUAL(waitForError(p, std::string{}), std::string{"foo"});
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(ConstructRejectAsync, PromiseContext) {
|
||||||
|
auto id = std::this_thread::get_id();
|
||||||
|
Promise<int> p{[this, &id](const PromiseResolve<int> &, const PromiseReject<int> &reject) {
|
||||||
|
boost::asio::defer(m_ioContext, [&id, reject]() {
|
||||||
|
reject(std::string{"foo"});
|
||||||
|
id = std::this_thread::get_id();
|
||||||
|
});
|
||||||
|
}};
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(p.isPending(), true);
|
||||||
|
start();
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, -1), -1);
|
||||||
|
BOOST_CHECK_EQUAL(waitForError(p, std::string{}), std::string{"foo"});
|
||||||
|
BOOST_CHECK_EQUAL(p.isRejected(), true);
|
||||||
|
BOOST_CHECK_NE(id, std::this_thread::get_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(ConstructRejectAsyncVoid, PromiseContext) {
|
||||||
|
auto id = std::this_thread::get_id();
|
||||||
|
Promise<void> p{[this, &id](const PromiseResolve<void> &, const PromiseReject<void> &reject) {
|
||||||
|
boost::asio::defer(m_ioContext, [&id, reject]() {
|
||||||
|
reject(std::string{"foo"});
|
||||||
|
id = std::this_thread::get_id();
|
||||||
|
});
|
||||||
|
}};
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(p.isPending(), true);
|
||||||
|
start();
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, -1, 42), -1);
|
||||||
|
BOOST_CHECK_EQUAL(waitForError(p, std::string{}), std::string{"foo"});
|
||||||
|
BOOST_CHECK_EQUAL(p.isRejected(), true);
|
||||||
|
BOOST_CHECK_NE(id, std::this_thread::get_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(ConstructRejectThrowOneArg) {
|
||||||
|
Promise<int> p{[](const PromiseResolve<int> &) { throw std::string{"foo"}; }};
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(p.isRejected(), true);
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, -1), -1);
|
||||||
|
BOOST_CHECK_EQUAL(waitForError(p, std::string{}), std::string{"foo"});
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(ConstructRejectThrowOneArgVoid) {
|
||||||
|
Promise<void> p{[](const PromiseResolve<void> &) { throw std::string{"foo"}; }};
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(p.isRejected(), true);
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, -1, 42), -1);
|
||||||
|
BOOST_CHECK_EQUAL(waitForError(p, std::string{}), std::string{"foo"});
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(ConstructRejectThrowTwoArgs) {
|
||||||
|
Promise<int> p{[](const PromiseResolve<int> &, const PromiseReject<int> &) { throw std::string{"foo"}; }};
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(p.isRejected(), true);
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, -1), -1);
|
||||||
|
BOOST_CHECK_EQUAL(waitForError(p, std::string{}), std::string{"foo"});
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(ConstructRejectThrowTwoArgsVoid) {
|
||||||
|
Promise<void> p{[](const PromiseResolve<void> &, const PromiseReject<void> &) { throw std::string{"foo"}; }};
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(p.isRejected(), true);
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, -1, 42), -1);
|
||||||
|
BOOST_CHECK_EQUAL(waitForError(p, std::string{}), std::string{"foo"});
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(ConstructRejectUndefined, PromiseContext) {
|
||||||
|
auto id = std::this_thread::get_id();
|
||||||
|
Promise<int> p{[this, &id](const PromiseResolve<int> &, const PromiseReject<int> &reject) {
|
||||||
|
boost::asio::defer(m_ioContext, [&id, reject]() {
|
||||||
|
reject();
|
||||||
|
id = std::this_thread::get_id();
|
||||||
|
});
|
||||||
|
}};
|
||||||
|
BOOST_CHECK_EQUAL(p.isPending(), true);
|
||||||
|
start();
|
||||||
|
BOOST_CHECK_EQUAL(waitForRejected<PromiseUndefinedException>(p), true);
|
||||||
|
BOOST_CHECK_NE(id, std::this_thread::get_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(ConstructRejectUndefinedVoid, PromiseContext) {
|
||||||
|
auto id = std::this_thread::get_id();
|
||||||
|
Promise<void> p{[this, &id](const PromiseResolve<void> &, const PromiseReject<void> &reject) {
|
||||||
|
boost::asio::defer(m_ioContext, [&id, reject]() {
|
||||||
|
reject();
|
||||||
|
id = std::this_thread::get_id();
|
||||||
|
});
|
||||||
|
}};
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(p.isPending(), true);
|
||||||
|
start();
|
||||||
|
BOOST_CHECK_EQUAL(waitForRejected<PromiseUndefinedException>(p), true);
|
||||||
|
BOOST_CHECK_NE(id, std::this_thread::get_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(ConvertFulfillTAsU, PromiseContext) {
|
||||||
|
// Static cast between primitive types.
|
||||||
|
{
|
||||||
|
auto p = Promise<float>::resolve(42.13).convert<int>();
|
||||||
|
|
||||||
|
static_assert((std::is_same<decltype(p), Promise<int>>::value));
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, -1), 42);
|
||||||
|
BOOST_CHECK_EQUAL(p.isFulfilled(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert enum class to int.
|
||||||
|
{
|
||||||
|
auto p = Promise<Enum1>::resolve(Enum1::Value1).convert<int>();
|
||||||
|
|
||||||
|
static_assert((std::is_same<decltype(p), Promise<int>>::value));
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, -1), 1);
|
||||||
|
BOOST_CHECK_EQUAL(p.isFulfilled(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert int to enum class.
|
||||||
|
{
|
||||||
|
auto p = Promise<int>::resolve(1).convert<Enum1>();
|
||||||
|
|
||||||
|
static_assert((std::is_same<decltype(p), Promise<Enum1>>::value));
|
||||||
|
|
||||||
|
BOOST_TEST((waitForValue(p, Enum1::Value0) == Enum1::Value1));
|
||||||
|
BOOST_CHECK_EQUAL(p.isFulfilled(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert between enums
|
||||||
|
{
|
||||||
|
auto p = Promise<Enum1>::resolve(Enum1::Value1).convert<Enum2>();
|
||||||
|
|
||||||
|
static_assert((std::is_same<decltype(p), Promise<Enum2>>::value));
|
||||||
|
|
||||||
|
BOOST_TEST((waitForValue(p, Enum2::Value0) == Enum2::Value1));
|
||||||
|
BOOST_CHECK_EQUAL(p.isFulfilled(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converting constructor for non-Qt types.
|
||||||
|
// https://en.cppreference.com/w/cpp/language/converting_constructor
|
||||||
|
{
|
||||||
|
auto p = Promise<Foo>::resolve(Foo{42}).convert<Bar>();
|
||||||
|
|
||||||
|
static_assert((std::is_same<decltype(p), Promise<Bar>>::value));
|
||||||
|
|
||||||
|
BOOST_TEST((waitForValue(p, Bar{}) == Bar{42}));
|
||||||
|
BOOST_CHECK_EQUAL(p.isFulfilled(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto p = Promise<int>::resolve(42).convert<std::any>();
|
||||||
|
|
||||||
|
static_assert((std::is_same<decltype(p), Promise<std::any>>::value));
|
||||||
|
BOOST_CHECK_EQUAL(std::any_cast<int>(waitForValue(p, std::any{})), 42);
|
||||||
|
BOOST_CHECK_EQUAL(p.isFulfilled(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
using Variant = std::variant<int, Foo>;
|
||||||
|
auto p = Promise<Foo>::resolve(Foo{42}).convert<Variant>();
|
||||||
|
|
||||||
|
static_assert((std::is_same<decltype(p), Promise<Variant>>::value));
|
||||||
|
|
||||||
|
BOOST_TEST((std::get<Foo>(waitForValue(p, Variant{})) == Foo{42}));
|
||||||
|
BOOST_CHECK_EQUAL(p.isFulfilled(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(ConvertFulfillTAsVoid) {
|
||||||
|
auto p = Promise<int>::resolve(42).convert<void>();
|
||||||
|
|
||||||
|
static_assert((std::is_same<decltype(p), Promise<void>>::value));
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, -1, 42), 42);
|
||||||
|
BOOST_CHECK_EQUAL(p.isFulfilled(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(ConvertRejectUnconvertibleTypes) {
|
||||||
|
// A string incompatible with int due to its value.
|
||||||
|
{
|
||||||
|
auto p = Promise<std::string>::resolve(std::string{"42foo"}).convert<int>();
|
||||||
|
|
||||||
|
static_assert((std::is_same<decltype(p), Promise<int>>::value));
|
||||||
|
|
||||||
|
BOOST_TEST(waitForRejected<PromiseConversionException>(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
// A standard library type unconvertible to a primitive type because there is no converter.
|
||||||
|
{
|
||||||
|
auto p = Promise<std::vector<int>>::resolve(std::vector<int>{42, -42}).convert<int>();
|
||||||
|
|
||||||
|
static_assert((std::is_same<decltype(p), Promise<int>>::value));
|
||||||
|
|
||||||
|
BOOST_TEST(waitForRejected<PromiseConversionException>(p));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(DelayFulfilled, PromiseContext) {
|
||||||
|
using namespace std::chrono;
|
||||||
|
auto begin = system_clock::now();
|
||||||
|
int64_t elapsed = -1;
|
||||||
|
auto p = Promise<int>::resolve(42).delay(m_ioContext, 1000).finally([&]() {
|
||||||
|
elapsed = duration_cast<milliseconds>(system_clock::now() - begin).count();
|
||||||
|
});
|
||||||
|
start();
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, -1), 42);
|
||||||
|
BOOST_CHECK_EQUAL(p.isFulfilled(), true);
|
||||||
|
|
||||||
|
BOOST_TEST(elapsed >= static_cast<int64_t>(1000 * 0.94));
|
||||||
|
BOOST_TEST(elapsed <= static_cast<int64_t>(1000 * 1.06));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(DelayRejected, PromiseContext) {
|
||||||
|
using namespace std::chrono;
|
||||||
|
int64_t elapsed = -1;
|
||||||
|
auto begin = system_clock::now();
|
||||||
|
|
||||||
|
auto p = Promise<int>::reject(std::string{"foo"}).delay(m_ioContext, 1000).finally([&]() {
|
||||||
|
elapsed = duration_cast<milliseconds>(system_clock::now() - begin).count();
|
||||||
|
});
|
||||||
|
start();
|
||||||
|
BOOST_CHECK_EQUAL(waitForError(p, std::string{}), std::string{"foo"});
|
||||||
|
BOOST_CHECK_EQUAL(p.isRejected(), true);
|
||||||
|
BOOST_TEST(elapsed <= 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(DelayFulfilledStdChrono, PromiseContext) {
|
||||||
|
using namespace std::chrono;
|
||||||
|
int64_t elapsed = -1;
|
||||||
|
|
||||||
|
auto begin = system_clock::now();
|
||||||
|
|
||||||
|
auto p = Promise<int>::resolve(42).delay(m_ioContext, std::chrono::seconds{1}).finally([&]() {
|
||||||
|
elapsed = duration_cast<milliseconds>(system_clock::now() - begin).count();
|
||||||
|
});
|
||||||
|
start();
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, -1), 42);
|
||||||
|
BOOST_CHECK_EQUAL(p.isFulfilled(), true);
|
||||||
|
|
||||||
|
BOOST_TEST(elapsed >= static_cast<int64_t>(1000 * 0.94));
|
||||||
|
BOOST_TEST(elapsed <= static_cast<int64_t>(1000 * 1.06));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(DelayRejectedStdChrono, PromiseContext) {
|
||||||
|
using namespace std::chrono;
|
||||||
|
int64_t elapsed = -1;
|
||||||
|
|
||||||
|
auto begin = system_clock::now();
|
||||||
|
|
||||||
|
auto p = Promise<int>::reject(std::string{"foo"}).delay(m_ioContext, std::chrono::seconds{1}).finally([&]() {
|
||||||
|
elapsed = duration_cast<milliseconds>(system_clock::now() - begin).count();
|
||||||
|
});
|
||||||
|
start();
|
||||||
|
BOOST_CHECK_EQUAL(waitForError(p, std::string{}), std::string{"foo"});
|
||||||
|
BOOST_CHECK_EQUAL(p.isRejected(), true);
|
||||||
|
BOOST_TEST(elapsed <= 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(EachEmptySequence) {
|
||||||
|
std::vector<int> values;
|
||||||
|
auto p = Promise<std::vector<int>>::resolve({}).each([&](int v, ...) { values.push_back(v); });
|
||||||
|
|
||||||
|
static_assert((std::is_same<decltype(p), Promise<std::vector<int>>>::value));
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, std::vector<int>{}), std::vector<int>{});
|
||||||
|
BOOST_CHECK_EQUAL(values, (std::vector<int>{}));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(EachPreserveValues) {
|
||||||
|
std::vector<int> values;
|
||||||
|
auto p = Promise<std::vector<int>>::resolve({42, 43, 44}).each([&](int v, ...) { values.push_back(v + 1); });
|
||||||
|
|
||||||
|
static_assert((std::is_same<decltype(p), Promise<std::vector<int>>>::value));
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, std::vector<int>{}), (std::vector<int>{42, 43, 44}));
|
||||||
|
BOOST_CHECK_EQUAL(values, (std::vector<int>{43, 44, 45}));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(EachIgnoreResult) {
|
||||||
|
std::vector<int> values;
|
||||||
|
auto p = Promise<std::vector<int>>::resolve({42, 43, 44}).each([&](int v, ...) {
|
||||||
|
values.push_back(v + 1);
|
||||||
|
return "Foo";
|
||||||
|
});
|
||||||
|
|
||||||
|
static_assert((std::is_same<decltype(p), Promise<std::vector<int>>>::value));
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, std::vector<int>{}), (std::vector<int>{42, 43, 44}));
|
||||||
|
BOOST_CHECK_EQUAL(values, (std::vector<int>{43, 44, 45}));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(EachDelayedFulfilled, PromiseContext) {
|
||||||
|
auto strand = boost::asio::make_strand(m_ioContext);
|
||||||
|
std::map<int, int> values;
|
||||||
|
auto p = Promise<std::vector<int>>::resolve({42, 43, 44}).each([&](int v, int index) {
|
||||||
|
return Promise<void>::resolve().delay(strand, 50).then([&values, v, index]() {
|
||||||
|
values[v] = index;
|
||||||
|
return 42;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
start();
|
||||||
|
static_assert((std::is_same<decltype(p), Promise<std::vector<int>>>::value));
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, std::vector<int>{}), (std::vector<int>{42, 43, 44}));
|
||||||
|
std::map<int, int> expected{{42, 0}, {43, 1}, {44, 2}};
|
||||||
|
BOOST_CHECK_EQUAL(values, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(EachDelayedRejected, PromiseContext) {
|
||||||
|
auto id = std::this_thread::get_id();
|
||||||
|
auto p = Promise<std::vector<int>>::resolve({42, 43, 44}).each([&id, this](int v, ...) {
|
||||||
|
return Promise<int>{[&, this](const PromiseResolve<int> &resolve, const PromiseReject<int> &reject) {
|
||||||
|
boost::asio::defer(m_ioContext, [&id, v, reject, resolve]() {
|
||||||
|
if (v == 44) {
|
||||||
|
reject(std::string{"foo"});
|
||||||
|
} else {
|
||||||
|
resolve(v);
|
||||||
|
}
|
||||||
|
id = std::this_thread::get_id();
|
||||||
|
});
|
||||||
|
}};
|
||||||
|
});
|
||||||
|
start();
|
||||||
|
static_assert((std::is_same<decltype(p), Promise<std::vector<int>>>::value));
|
||||||
|
BOOST_CHECK_EQUAL(waitForError(p, std::string{}), std::string{"foo"});
|
||||||
|
BOOST_CHECK_NE(id, std::this_thread::get_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(EachFunctorThrows, PromiseContext) {
|
||||||
|
auto p = Promise<std::vector<int>>::resolve({42, 43, 44}).each([](int v, ...) {
|
||||||
|
if (v == 44) {
|
||||||
|
throw std::string{"foo"};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
static_assert((std::is_same<decltype(p), Promise<std::vector<int>>>::value));
|
||||||
|
BOOST_CHECK_EQUAL(waitForError(p, std::string{}), std::string{"foo"});
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(EachFunctorArguments, PromiseContext) {
|
||||||
|
std::vector<int> values;
|
||||||
|
auto p = Promise<std::vector<int>>::resolve({42, 43, 44}).each([&](int v, int i) {
|
||||||
|
values.push_back(i);
|
||||||
|
values.push_back(v);
|
||||||
|
});
|
||||||
|
static_assert((std::is_same<decltype(p), Promise<std::vector<int>>>::value));
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, std::vector<int>{}), (std::vector<int>{42, 43, 44}));
|
||||||
|
BOOST_CHECK_EQUAL(values, (std::vector<int>{0, 42, 1, 43, 2, 44}));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Sequence>
|
||||||
|
struct SequenceTester {
|
||||||
|
static void exec() {
|
||||||
|
std::vector<int> values;
|
||||||
|
auto p = PromiseHelper::resolve(Sequence{42, 43, 44})
|
||||||
|
.each([&](int v, int i) {
|
||||||
|
values.push_back(i);
|
||||||
|
values.push_back(v);
|
||||||
|
})
|
||||||
|
.each([&](int v, ...) {
|
||||||
|
values.push_back(v);
|
||||||
|
return std::string{"foo"};
|
||||||
|
})
|
||||||
|
.each([&](int v, ...) {
|
||||||
|
values.push_back(v + 1);
|
||||||
|
return PromiseHelper::resolve(std::string{"foo"}).then([&]() { values.push_back(-1); });
|
||||||
|
})
|
||||||
|
.each([&](int v, ...) { values.push_back(v + 2); });
|
||||||
|
|
||||||
|
static_assert((std::is_same<decltype(p), Promise<Sequence>>::value));
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p, Sequence{}), (Sequence{42, 43, 44}));
|
||||||
|
|
||||||
|
std::vector<int> expected{0, 42, 1, 43, 2, 44, 42, 43, 44, 43, -1, 44, -1, 45, -1, 44, 45, 46};
|
||||||
|
BOOST_CHECK_EQUAL(values, expected);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(EachSequenceTypes) {
|
||||||
|
SequenceTester<std::list<int>>::exec();
|
||||||
|
SequenceTester<std::vector<int>>::exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(FailSameType) {
|
||||||
|
// http://en.cppreference.com/w/cpp/error/exception
|
||||||
|
auto p = Promise<int>::reject(std::out_of_range("foo"));
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
p.fail([&](const std::domain_error &e) {
|
||||||
|
error += std::string{e.what()} + "0";
|
||||||
|
return -1;
|
||||||
|
})
|
||||||
|
.fail([&](const std::out_of_range &e) {
|
||||||
|
error += std::string{e.what()} + "1";
|
||||||
|
return -1;
|
||||||
|
})
|
||||||
|
.fail([&](const std::exception &e) {
|
||||||
|
error += std::string{e.what()} + "2";
|
||||||
|
return -1;
|
||||||
|
})
|
||||||
|
.wait();
|
||||||
|
BOOST_CHECK_EQUAL(error, std::string{"foo1"});
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(FailBaseClass) {
|
||||||
|
// http://en.cppreference.com/w/cpp/error/exception
|
||||||
|
auto p = Promise<int>::reject(std::out_of_range("foo"));
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
p.fail([&](const std::runtime_error &e) {
|
||||||
|
error += std::string{e.what()} + "0";
|
||||||
|
return -1;
|
||||||
|
})
|
||||||
|
.fail([&](const std::logic_error &e) {
|
||||||
|
error += std::string{e.what()} + "1";
|
||||||
|
return -1;
|
||||||
|
})
|
||||||
|
.fail([&](const std::exception &e) {
|
||||||
|
error += std::string{e.what()} + "2";
|
||||||
|
return -1;
|
||||||
|
})
|
||||||
|
.wait();
|
||||||
|
BOOST_CHECK_EQUAL(error, std::string{"foo1"});
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(FailCatchAll) {
|
||||||
|
auto p = Promise<int>::reject(std::out_of_range("foo"));
|
||||||
|
std::string error;
|
||||||
|
p.fail([&](const std::runtime_error &e) {
|
||||||
|
error += std::string{e.what()} + "0";
|
||||||
|
return -1;
|
||||||
|
})
|
||||||
|
.fail([&]() {
|
||||||
|
error += "bar";
|
||||||
|
return -1;
|
||||||
|
})
|
||||||
|
.fail([&](const std::exception &e) {
|
||||||
|
error += std::string{e.what()} + "2";
|
||||||
|
return -1;
|
||||||
|
})
|
||||||
|
.wait();
|
||||||
|
BOOST_CHECK_EQUAL(error, std::string{"bar"});
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string kErr{"0.42"};
|
||||||
|
float stringToFloatNoArg() {
|
||||||
|
return std::stof(kErr);
|
||||||
|
}
|
||||||
|
float stringToFloatArgByVal(std::string e) {
|
||||||
|
return std::stof(e);
|
||||||
|
}
|
||||||
|
float stringToFloatArgByRef(const std::string &e) {
|
||||||
|
return std::stof(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
const float kFail = -1.f;
|
||||||
|
BOOST_FIXTURE_TEST_CASE(FailFunctionPtrHandlers, PromiseContext) {
|
||||||
|
{ // Global functions.
|
||||||
|
auto p0 = Promise<float>::reject(kErr).fail(&stringToFloatNoArg);
|
||||||
|
auto p1 = Promise<float>::reject(kErr).fail(&stringToFloatArgByVal);
|
||||||
|
auto p2 = Promise<float>::reject(kErr).fail(&stringToFloatArgByRef);
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), kRes);
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), kRes);
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), kRes);
|
||||||
|
}
|
||||||
|
{ // Static member functions.
|
||||||
|
auto p0 = Promise<float>::reject(kErr).fail(&PromiseContext::stringToFloatNoArg);
|
||||||
|
auto p1 = Promise<float>::reject(kErr).fail(&PromiseContext::stringToFloatArgByVal);
|
||||||
|
auto p2 = Promise<float>::reject(kErr).fail(&PromiseContext::stringToFloatArgByRef);
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), kRes);
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), kRes);
|
||||||
|
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), kRes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(Then) {
|
BOOST_AUTO_TEST_CASE(Then) {
|
||||||
std::vector<std::variant<int, std::string>> values;
|
std::vector<std::variant<int, std::string>> values;
|
||||||
auto input = Promise<int>::resolve(42);
|
auto input = Promise<int>::resolve(42);
|
||||||
@ -34,8 +744,6 @@ BOOST_AUTO_TEST_CASE(Then) {
|
|||||||
|
|
||||||
BOOST_CHECK_EQUAL(input.isFulfilled(), true);
|
BOOST_CHECK_EQUAL(input.isFulfilled(), true);
|
||||||
BOOST_CHECK_EQUAL(output.isFulfilled(), true);
|
BOOST_CHECK_EQUAL(output.isFulfilled(), true);
|
||||||
|
|
||||||
// auto p= new int;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(ThenResolveAsync) {
|
BOOST_AUTO_TEST_CASE(ThenResolveAsync) {
|
||||||
@ -140,7 +848,6 @@ BOOST_AUTO_TEST_CASE(ThenNullHandler) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const float kRes = 0.42f;
|
const float kRes = 0.42f;
|
||||||
const float kFail = -1.f;
|
|
||||||
|
|
||||||
float fnNoArg() {
|
float fnNoArg() {
|
||||||
return kRes;
|
return kRes;
|
||||||
@ -153,54 +860,24 @@ float fnArgByRef(const float &v) {
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Klass {
|
BOOST_FIXTURE_TEST_CASE(ThenFunctionPtrHandlers, PromiseContext) {
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(ThenFunctionPtrHandlers) {
|
|
||||||
{ // Global functions.
|
{ // Global functions.
|
||||||
auto p0 = Promise<void>::resolve().then(&fnNoArg);
|
auto p0 = Promise<void>::resolve().then(&fnNoArg);
|
||||||
auto p1 = Promise<float>::resolve(kRes).then(&fnArgByVal);
|
auto p1 = Promise<float>::resolve(PromiseContext::kRes).then(&fnArgByVal);
|
||||||
auto p2 = Promise<float>::resolve(kRes).then(&fnArgByRef);
|
auto p2 = Promise<float>::resolve(kRes).then(&fnArgByRef);
|
||||||
|
|
||||||
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), kRes);
|
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), PromiseContext::kRes);
|
||||||
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), kRes);
|
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), PromiseContext::kRes);
|
||||||
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), kRes);
|
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), PromiseContext::kRes);
|
||||||
}
|
}
|
||||||
{ // Static member functions.
|
{ // Static member functions.
|
||||||
auto p0 = Promise<void>::resolve().then(&Klass::kFnNoArg);
|
auto p0 = Promise<void>::resolve().then(&PromiseContext::kFnNoArg);
|
||||||
auto p1 = Promise<float>::resolve(kRes).then(&Klass::kFnArgByVal);
|
auto p1 = Promise<float>::resolve(PromiseContext::kRes).then(&PromiseContext::kFnArgByVal);
|
||||||
auto p2 = Promise<float>::resolve(kRes).then(&Klass::kFnArgByRef);
|
auto p2 = Promise<float>::resolve(PromiseContext::kRes).then(&PromiseContext::kFnArgByRef);
|
||||||
|
|
||||||
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), kRes);
|
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), PromiseContext::kRes);
|
||||||
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), kRes);
|
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), PromiseContext::kRes);
|
||||||
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), kRes);
|
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), PromiseContext::kRes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,23 +919,22 @@ BOOST_AUTO_TEST_CASE(ThenStdFunctionHandlers) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(ThenStdBindHandlers) {
|
BOOST_FIXTURE_TEST_CASE(ThenStdBindHandlers, PromiseContext) {
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
|
const float value = 42.f;
|
||||||
|
this->setValue(value);
|
||||||
|
|
||||||
const float val{42.f};
|
const std::function<float()> bindNoArg = std::bind(&PromiseContext::functionNoArg, this);
|
||||||
const Klass obj{val};
|
const std::function<float(float)> bindArgByVal = std::bind(&PromiseContext::functionArgByVal, this, _1);
|
||||||
|
const std::function<float(const float &)> bindArgByRef = std::bind(&PromiseContext::functionArgByRef, this, _1);
|
||||||
const std::function<float()> bindNoArg = std::bind(&Klass::fnNoArg, &obj);
|
|
||||||
const std::function<float(float)> bindArgByVal = std::bind(&Klass::fnArgByVal, &obj, _1);
|
|
||||||
const std::function<float(const float &)> bindArgByRef = std::bind(&Klass::fnArgByRef, &obj, _1);
|
|
||||||
|
|
||||||
auto p0 = Promise<void>::resolve().then(bindNoArg);
|
auto p0 = Promise<void>::resolve().then(bindNoArg);
|
||||||
auto p1 = Promise<float>::resolve(kRes).then(bindArgByVal);
|
auto p1 = Promise<float>::resolve(kRes).then(bindArgByVal);
|
||||||
auto p2 = Promise<float>::resolve(kRes).then(bindArgByRef);
|
auto p2 = Promise<float>::resolve(kRes).then(bindArgByRef);
|
||||||
|
|
||||||
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), val);
|
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), value);
|
||||||
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), val + kRes);
|
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), value + kRes);
|
||||||
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), val + kRes);
|
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), value + kRes);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(ThenLambdaHandlers) {
|
BOOST_AUTO_TEST_CASE(ThenLambdaHandlers) {
|
||||||
@ -298,3 +974,5 @@ BOOST_AUTO_TEST_CASE(ThenLambdaHandlers) {
|
|||||||
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), kRes);
|
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), kRes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
@ -26,8 +26,7 @@ void initialize(const std::string &filename, const std::string &target, boost::l
|
|||||||
#else
|
#else
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << filename << "_%Y-%m-%d_%H.%M.%S_%N.log";
|
oss << filename << "_%Y-%m-%d_%H.%M.%S_%N.log";
|
||||||
auto fileSink =
|
auto fileSink = add_file_log(keywords::file_name = oss.str(), keywords::auto_flush = true, keywords::target = target);
|
||||||
add_file_log(keywords::file_name = oss.str(), keywords::auto_flush = true, keywords::target = target);
|
|
||||||
fileSink->set_formatter(&defaultFormatter);
|
fileSink->set_formatter(&defaultFormatter);
|
||||||
#endif
|
#endif
|
||||||
initialized = true;
|
initialized = true;
|
||||||
@ -41,12 +40,11 @@ void defaultFormatter(const boost::log::record_view &record, boost::log::formatt
|
|||||||
boost::log::formatting_ostream oss(level);
|
boost::log::formatting_ostream oss(level);
|
||||||
oss << "[" << record[boost::log::trivial::severity] << "]";
|
oss << "[" << record[boost::log::trivial::severity] << "]";
|
||||||
|
|
||||||
auto dateTimeFormatter = expressions::stream << expressions::format_date_time<boost::posix_time::ptime>(
|
auto dateTimeFormatter = expressions::stream
|
||||||
"TimeStamp", "[%m-%d %H:%M:%S.%f]");
|
<< expressions::format_date_time<boost::posix_time::ptime>("TimeStamp", "[%m-%d %H:%M:%S.%f]");
|
||||||
|
|
||||||
dateTimeFormatter(record, ostream);
|
dateTimeFormatter(record, ostream);
|
||||||
ostream << "[" << boost::log::extract<boost::log::attributes::current_thread_id::value_type>("ThreadID", record)
|
ostream << "[" << boost::log::extract<boost::log::attributes::current_thread_id::value_type>("ThreadID", record) << "]";
|
||||||
<< "]";
|
|
||||||
ostream << std::setw(8) << std::left << level;
|
ostream << std::setw(8) << std::left << level;
|
||||||
auto &&category = record[AmassKeywords::category];
|
auto &&category = record[AmassKeywords::category];
|
||||||
if (!category.empty()) ostream << " [" << category << "]";
|
if (!category.empty()) ostream << " [" << category << "]";
|
||||||
@ -82,3 +80,14 @@ BOOST_LOG_GLOBAL_LOGGER_INIT(location_logger, LocationLogger<boost::log::trivial
|
|||||||
}
|
}
|
||||||
return lg;
|
return lg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
ostream &operator<<(ostream &os, const map<int, int> &m) {
|
||||||
|
os << "{ ";
|
||||||
|
for (const auto &pair : m) {
|
||||||
|
os << "{" << pair.first << ", " << pair.second << "} ";
|
||||||
|
}
|
||||||
|
os << "}";
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
} // namespace std
|
@ -21,7 +21,9 @@ public:
|
|||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
inline bool doOnceInterface() const { return static_cast<const Derived *>(this)->doOnce(); }
|
inline bool doOnceInterface() const {
|
||||||
|
return static_cast<const Derived *>(this)->doOnce();
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
mutable bool m_status = true;
|
mutable bool m_status = true;
|
||||||
@ -29,8 +31,11 @@ protected:
|
|||||||
|
|
||||||
class NumberPredicator : public LogPredicator<NumberPredicator> {
|
class NumberPredicator : public LogPredicator<NumberPredicator> {
|
||||||
public:
|
public:
|
||||||
NumberPredicator(int limit) : m_limit(limit) {}
|
NumberPredicator(int limit) : m_limit(limit) {
|
||||||
inline bool doOnce() const { return (m_current++ % m_limit) == 0; }
|
}
|
||||||
|
inline bool doOnce() const {
|
||||||
|
return (m_current++ % m_limit) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_limit;
|
int m_limit;
|
||||||
@ -39,8 +44,11 @@ private:
|
|||||||
|
|
||||||
class CountPredicator : public LogPredicator<CountPredicator> {
|
class CountPredicator : public LogPredicator<CountPredicator> {
|
||||||
public:
|
public:
|
||||||
CountPredicator(int limit) : m_limit(limit) {}
|
CountPredicator(int limit) : m_limit(limit) {
|
||||||
inline bool doOnce() const { return m_current++ < m_limit; }
|
}
|
||||||
|
inline bool doOnce() const {
|
||||||
|
return m_current++ < m_limit;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_limit;
|
int m_limit;
|
||||||
@ -49,7 +57,8 @@ private:
|
|||||||
|
|
||||||
class TimePredicator : public LogPredicator<TimePredicator> {
|
class TimePredicator : public LogPredicator<TimePredicator> {
|
||||||
public:
|
public:
|
||||||
TimePredicator(int milliseconds) : m_limit(std::chrono::milliseconds(milliseconds)) {}
|
TimePredicator(int milliseconds) : m_limit(std::chrono::milliseconds(milliseconds)) {
|
||||||
|
}
|
||||||
|
|
||||||
inline bool doOnce() const {
|
inline bool doOnce() const {
|
||||||
auto now = std::chrono::system_clock::now();
|
auto now = std::chrono::system_clock::now();
|
||||||
@ -65,8 +74,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <typename BaseT>
|
template <typename BaseT>
|
||||||
CategoryTaggerFeature<BaseT>::CategoryTaggerFeature(const CategoryTaggerFeature &obj)
|
CategoryTaggerFeature<BaseT>::CategoryTaggerFeature(const CategoryTaggerFeature &obj) : BaseT(static_cast<const BaseT &>(obj)) {
|
||||||
: BaseT(static_cast<const BaseT &>(obj)) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BaseT>
|
template <typename BaseT>
|
||||||
@ -96,13 +104,13 @@ boost::log::record CategoryTaggerFeature<BaseT>::open_record_unlocked(const Args
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename BaseT>
|
template <typename BaseT>
|
||||||
FilenameTaggerFeature<BaseT>::FilenameTaggerFeature(const FilenameTaggerFeature &obj)
|
FilenameTaggerFeature<BaseT>::FilenameTaggerFeature(const FilenameTaggerFeature &obj) : BaseT(static_cast<const BaseT &>(obj)) {
|
||||||
: BaseT(static_cast<const BaseT &>(obj)) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BaseT>
|
template <typename BaseT>
|
||||||
template <typename ArgsT>
|
template <typename ArgsT>
|
||||||
FilenameTaggerFeature<BaseT>::FilenameTaggerFeature(const ArgsT &args) : BaseT(args) {}
|
FilenameTaggerFeature<BaseT>::FilenameTaggerFeature(const ArgsT &args) : BaseT(args) {
|
||||||
|
}
|
||||||
|
|
||||||
template <typename BaseT>
|
template <typename BaseT>
|
||||||
template <typename ArgsT>
|
template <typename ArgsT>
|
||||||
@ -126,13 +134,13 @@ boost::log::record FilenameTaggerFeature<BaseT>::open_record_unlocked(const Args
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename BaseT>
|
template <typename BaseT>
|
||||||
FunctionTaggerFeature<BaseT>::FunctionTaggerFeature(const FunctionTaggerFeature &obj)
|
FunctionTaggerFeature<BaseT>::FunctionTaggerFeature(const FunctionTaggerFeature &obj) : BaseT(static_cast<const BaseT &>(obj)) {
|
||||||
: BaseT(static_cast<const BaseT &>(obj)) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BaseT>
|
template <typename BaseT>
|
||||||
template <typename ArgsT>
|
template <typename ArgsT>
|
||||||
FunctionTaggerFeature<BaseT>::FunctionTaggerFeature(const ArgsT &args) : BaseT(args) {}
|
FunctionTaggerFeature<BaseT>::FunctionTaggerFeature(const ArgsT &args) : BaseT(args) {
|
||||||
|
}
|
||||||
|
|
||||||
template <typename BaseT>
|
template <typename BaseT>
|
||||||
template <typename ArgsT>
|
template <typename ArgsT>
|
||||||
@ -157,10 +165,12 @@ boost::log::record FunctionTaggerFeature<BaseT>::open_record_unlocked(const Args
|
|||||||
|
|
||||||
template <typename BaseT>
|
template <typename BaseT>
|
||||||
template <typename ArgsT>
|
template <typename ArgsT>
|
||||||
LineTaggerFeature<BaseT>::LineTaggerFeature(const ArgsT &args) : BaseT(args) {}
|
LineTaggerFeature<BaseT>::LineTaggerFeature(const ArgsT &args) : BaseT(args) {
|
||||||
|
}
|
||||||
|
|
||||||
template <typename BaseT>
|
template <typename BaseT>
|
||||||
LineTaggerFeature<BaseT>::LineTaggerFeature(const LineTaggerFeature &obj) : BaseT(static_cast<const BaseT &>(obj)) {}
|
LineTaggerFeature<BaseT>::LineTaggerFeature(const LineTaggerFeature &obj) : BaseT(static_cast<const BaseT &>(obj)) {
|
||||||
|
}
|
||||||
|
|
||||||
template <typename BaseT>
|
template <typename BaseT>
|
||||||
template <typename ArgsT>
|
template <typename ArgsT>
|
||||||
@ -182,4 +192,8 @@ boost::log::record LineTaggerFeature<BaseT>::open_record_unlocked(const ArgsT &a
|
|||||||
return BaseT::open_record_unlocked(args);
|
return BaseT::open_record_unlocked(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
ostream &operator<<(ostream &os, const map<int, int> &m);
|
||||||
|
}
|
||||||
|
|
||||||
#endif // BOOSTLOG_INL
|
#endif // BOOSTLOG_INL
|
||||||
|
@ -124,9 +124,6 @@ typename FunctionTraits<std::decay_t<Function>>::StlFunctionType makeStlFunction
|
|||||||
return static_cast<typename FunctionTraits<std::decay_t<Function>>::StlFunctionType>(std::forward<Function>(lambda));
|
return static_cast<typename FunctionTraits<std::decay_t<Function>>::StlFunctionType>(std::forward<Function>(lambda));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using Unqualified = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \struct HasCallOperator
|
* \struct HasCallOperator
|
||||||
* http://stackoverflow.com/a/5117641
|
* http://stackoverflow.com/a/5117641
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#include "Singleton.h"
|
#include "Singleton.h"
|
||||||
#include <boost/asio/io_context.hpp>
|
#include <boost/asio/io_context.hpp>
|
||||||
|
#include <boost/asio/post.hpp>
|
||||||
|
#include <boost/asio/steady_timer.hpp>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
class IoContext {
|
class IoContext {
|
||||||
@ -12,7 +14,9 @@ public:
|
|||||||
Asynchronous,
|
Asynchronous,
|
||||||
};
|
};
|
||||||
friend class Amass::Singleton<IoContext, Amass::LocalInstance>;
|
friend class Amass::Singleton<IoContext, Amass::LocalInstance>;
|
||||||
std::shared_ptr<boost::asio::io_context> ioContext() const { return m_ioContext; }
|
std::shared_ptr<boost::asio::io_context> ioContext() const {
|
||||||
|
return m_ioContext;
|
||||||
|
}
|
||||||
template <Mode mode = Mode::Asynchronous>
|
template <Mode mode = Mode::Asynchronous>
|
||||||
void run() {
|
void run() {
|
||||||
if constexpr (mode == Mode::Asynchronous) {
|
if constexpr (mode == Mode::Asynchronous) {
|
||||||
@ -24,9 +28,28 @@ public:
|
|||||||
void stop();
|
void stop();
|
||||||
~IoContext();
|
~IoContext();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* @tparam Executor boost::asio::io_context、boost::asio::strand(通过 boost::asio::make_strand(io_context); 得到)
|
||||||
|
*/
|
||||||
|
template <class Executor, class Func, class Rep, class Period>
|
||||||
|
static void postDelayed(Executor &ioContext, Func &&task, const std::chrono::duration<Rep, Period> &duration) {
|
||||||
|
auto timer = std::make_shared<boost::asio::steady_timer>(ioContext, duration);
|
||||||
|
timer->async_wait([timer, task = std::forward<Func>(task)](const boost::system::error_code & /*e*/) mutable {
|
||||||
|
boost::asio::post(timer->get_executor(), std::move(task));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Executor, class Func>
|
||||||
|
static void postDelayed(Executor &ioContext, Func &&task, int milliseconds) {
|
||||||
|
return postDelayed(ioContext, std::forward(task), std::chrono::milliseconds(milliseconds));
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
IoContext(Args &&... args) : m_ioContext(std::make_shared<boost::asio::io_context>(std::forward<Args>(args)...)) {}
|
IoContext(Args &&...args) : m_ioContext(std::make_shared<boost::asio::io_context>(std::forward<Args>(args)...)) {
|
||||||
|
}
|
||||||
void runIoContext();
|
void runIoContext();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -180,4 +180,35 @@ protected:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U, bool IsConvertibleViaStaticCast>
|
||||||
|
struct PromiseConverterBase;
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct PromiseConverterBase<T, U, true> {
|
||||||
|
static std::function<U(const T &)> create() {
|
||||||
|
return [](const T &value) { return static_cast<U>(value); };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct PromiseConverterBase<T, U, false> {
|
||||||
|
static std::function<U(const T &)> create() {
|
||||||
|
return [](const T &value) {
|
||||||
|
throw PromiseConversionException{};
|
||||||
|
return U{};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct PromiseConverter : PromiseConverterBase<T, U,
|
||||||
|
// Fundamental types and converting constructors.
|
||||||
|
std::is_convertible<T, U>::value ||
|
||||||
|
// Conversion to void.
|
||||||
|
std::is_same<U, void>::value ||
|
||||||
|
// Conversion between enums and arithmetic types.
|
||||||
|
((std::is_enum<T>::value && std::is_arithmetic<U>::value) ||
|
||||||
|
(std::is_arithmetic<T>::value && std::is_enum<U>::value) ||
|
||||||
|
(std::is_enum<T>::value && std::is_enum<U>::value))> {};
|
||||||
|
|
||||||
#endif // __PROMISEDATA_H__
|
#endif // __PROMISEDATA_H__
|
@ -1,10 +0,0 @@
|
|||||||
#ifndef __PROMISEEXCEPTION_H__
|
|
||||||
#define __PROMISEEXCEPTION_H__
|
|
||||||
|
|
||||||
#include <exception>
|
|
||||||
|
|
||||||
class PromiseUndefinedException : public std::exception {
|
|
||||||
public:
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // __PROMISEEXCEPTION_H__
|
|
@ -2,9 +2,9 @@
|
|||||||
#define __PROMISEHANDLER_H__
|
#define __PROMISEHANDLER_H__
|
||||||
|
|
||||||
#include "../FunctionTraits.h"
|
#include "../FunctionTraits.h"
|
||||||
#include <type_traits>
|
|
||||||
#include <exception>
|
|
||||||
#include "PromiseData.h"
|
#include "PromiseData.h"
|
||||||
|
#include <exception>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class Promise;
|
class Promise;
|
||||||
@ -26,6 +26,12 @@ struct PromiseDeduce<const volatile T> : public PromiseDeduce<T> {};
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
struct PromiseDeduce<Promise<T>> : public PromiseDeduce<T> {};
|
struct PromiseDeduce<Promise<T>> : public PromiseDeduce<T> {};
|
||||||
|
|
||||||
|
template <typename Functor, typename... Args>
|
||||||
|
struct PromiseFunctor {
|
||||||
|
using ResultType = typename std::invoke_result_t<Functor, Args...>;
|
||||||
|
using PromiseType = typename PromiseDeduce<ResultType>::Type;
|
||||||
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct PromiseFulfill {
|
struct PromiseFulfill {
|
||||||
template <typename V, typename TResolve, typename TReject>
|
template <typename V, typename TResolve, typename TReject>
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
#ifndef __PROMISERESOLVER_H__
|
#ifndef __PROMISERESOLVER_H__
|
||||||
#define __PROMISERESOLVER_H__
|
#define __PROMISERESOLVER_H__
|
||||||
|
|
||||||
#include "PromiseException.h"
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class Promise;
|
class Promise;
|
||||||
|
|
||||||
|
@ -1,6 +1,16 @@
|
|||||||
#ifndef __PROMISE_H__
|
#ifndef __PROMISE_H__
|
||||||
#define __PROMISE_H__
|
#define __PROMISE_H__
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
class PromiseUndefinedException : public std::exception {
|
||||||
|
public:
|
||||||
|
};
|
||||||
|
|
||||||
|
class PromiseConversionException : public std::exception {
|
||||||
|
public:
|
||||||
|
};
|
||||||
|
|
||||||
#include "Private/PromiseHandler.h"
|
#include "Private/PromiseHandler.h"
|
||||||
#include "Private/PromiseResolver.h"
|
#include "Private/PromiseResolver.h"
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@ -42,6 +52,25 @@ public:
|
|||||||
swap(other);
|
swap(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual ~PromiseBase() {
|
||||||
|
}
|
||||||
|
|
||||||
|
PromiseBase<T> &operator=(const PromiseBase<T> &other) {
|
||||||
|
m_d = other.m_d;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
PromiseBase<T> &operator=(PromiseBase<T> &&other) {
|
||||||
|
PromiseBase<T>(std::move(other)).swap(*this);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const PromiseBase<T> &other) const {
|
||||||
|
return (m_d == other.m_d);
|
||||||
|
}
|
||||||
|
bool operator!=(const PromiseBase<T> &other) const {
|
||||||
|
return (m_d != other.m_d);
|
||||||
|
}
|
||||||
|
|
||||||
void swap(PromiseBase<T> &other) {
|
void swap(PromiseBase<T> &other) {
|
||||||
std::swap(m_d, other.m_d);
|
std::swap(m_d, other.m_d);
|
||||||
}
|
}
|
||||||
@ -78,6 +107,12 @@ public:
|
|||||||
return then(std::forward<TFulfilled>(fulfilled), nullptr);
|
return then(std::forward<TFulfilled>(fulfilled), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename THandler>
|
||||||
|
Promise<T> finally(THandler handler) const {
|
||||||
|
Promise<T> p = *this;
|
||||||
|
return p.then(handler, handler).then([=]() { return p; });
|
||||||
|
}
|
||||||
|
|
||||||
template <typename TRejected>
|
template <typename TRejected>
|
||||||
typename PromiseHandler<T, std::nullptr_t>::PromiseType fail(TRejected &&rejected) const {
|
typename PromiseHandler<T, std::nullptr_t>::PromiseType fail(TRejected &&rejected) const {
|
||||||
return then(nullptr, std::forward<TRejected>(rejected));
|
return then(nullptr, std::forward<TRejected>(rejected));
|
||||||
@ -95,10 +130,112 @@ public:
|
|||||||
return Promise<T>{[&](const PromiseResolve<T> &, const PromiseReject<T> &reject) { reject(std::forward<E>(error)); }};
|
return Promise<T>{[&](const PromiseResolve<T> &, const PromiseReject<T> &reject) { reject(std::forward<E>(error)); }};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename THandler>
|
||||||
|
Promise<T> tapFail(THandler handler) const {
|
||||||
|
Promise<T> p = *this;
|
||||||
|
return p.then([]() {}, handler).then([=]() { return p; });
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename THandler>
|
||||||
|
Promise<T> tap(THandler handler) const {
|
||||||
|
Promise<T> p = *this;
|
||||||
|
return p.then(handler).then([=]() { return p; });
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef BOOST_ASIO_EXECUTION_EXECUTOR
|
||||||
|
template <class Executor, class Rep, class Period>
|
||||||
|
Promise<T> delay(Executor &ioContext, const std::chrono::duration<Rep, Period> &duration) const {
|
||||||
|
return tap([&]() {
|
||||||
|
return Promise<void>{[&](const PromiseResolve<void> &resolve) {
|
||||||
|
auto timer = std::make_shared<boost::asio::steady_timer>(ioContext, duration);
|
||||||
|
timer->async_wait([timer, resolve](const boost::system::error_code & /*e*/) mutable {
|
||||||
|
boost::asio::post(timer->get_executor(), resolve);
|
||||||
|
});
|
||||||
|
}};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Executor>
|
||||||
|
Promise<T> delay(Executor &ioContext, int milliseconds) const {
|
||||||
|
return delay(ioContext, std::chrono::milliseconds(milliseconds));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::shared_ptr<PromiseData<T>> m_d;
|
std::shared_ptr<PromiseData<T>> m_d;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace PromiseHelper {
|
||||||
|
template <typename T>
|
||||||
|
typename PromiseDeduce<T>::Type resolve(T &&value) {
|
||||||
|
using PromiseType = typename PromiseDeduce<T>::Type;
|
||||||
|
using ValueType = typename PromiseType::Type;
|
||||||
|
using ResolveType = PromiseResolve<ValueType>;
|
||||||
|
using RejectType = PromiseReject<ValueType>;
|
||||||
|
|
||||||
|
return PromiseType{[&](ResolveType &&resolve, RejectType &&reject) {
|
||||||
|
PromiseFulfill<std::decay_t<T>>::call(std::forward<T>(value), std::forward<ResolveType>(resolve), std::forward<RejectType>(reject));
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
Promise<void> resolve();
|
||||||
|
|
||||||
|
template <typename Functor, typename... Args>
|
||||||
|
typename PromiseFunctor<Functor, Args...>::PromiseType attempt(Functor &&fn, Args &&...args) {
|
||||||
|
using FunctorType = PromiseFunctor<Functor, Args...>;
|
||||||
|
using PromiseType = typename FunctorType::PromiseType;
|
||||||
|
using ValueType = typename PromiseType::Type;
|
||||||
|
|
||||||
|
// NOTE: std::forward<T<U>>: MSVC 2013 fails when forwarding
|
||||||
|
// template type (error: "expects 4 arguments - 0 provided").
|
||||||
|
// However it succeeds with type alias.
|
||||||
|
// TODO: should we expose QPromise::ResolveType & RejectType?
|
||||||
|
using ResolveType = PromiseResolve<ValueType>;
|
||||||
|
using RejectType = PromiseReject<ValueType>;
|
||||||
|
|
||||||
|
return PromiseType{[&](ResolveType &&resolve, RejectType &&reject) {
|
||||||
|
PromiseDispatch<typename FunctorType::ResultType>::call(std::forward<ResolveType>(resolve), std::forward<RejectType>(reject),
|
||||||
|
std::forward<Functor>(fn), std::forward<Args>(args)...);
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, template <typename, typename...> class Sequence = std::vector, typename... Args>
|
||||||
|
Promise<std::vector<T>> all(const Sequence<Promise<T>, Args...> &promises) {
|
||||||
|
const int count = static_cast<int>(promises.size());
|
||||||
|
if (count == 0) {
|
||||||
|
return PromiseHelper::resolve(std::vector<T>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise<std::vector<T>>{[=](const PromiseResolve<std::vector<T>> &resolve, const PromiseReject<std::vector<T>> &reject) {
|
||||||
|
auto remaining = std::make_shared<int>(count);
|
||||||
|
auto results = std::make_shared<std::vector<T>>(count);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (const auto &promise : promises) {
|
||||||
|
promise.then(
|
||||||
|
[=](const T &res) mutable {
|
||||||
|
(*results)[i] = res;
|
||||||
|
if (--(*remaining) == 0) {
|
||||||
|
resolve(*results);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[=]() mutable {
|
||||||
|
if (*remaining != -1) {
|
||||||
|
*remaining = -1;
|
||||||
|
reject(std::current_exception());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <template <typename, typename...> class Sequence = std::vector, typename... Args>
|
||||||
|
Promise<void> all(const Sequence<Promise<void>, Args...> &promises);
|
||||||
|
|
||||||
|
}; // namespace PromiseHelper
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class Promise : public PromiseBase<T> {
|
class Promise : public PromiseBase<T> {
|
||||||
friend class PromiseResolver<T>;
|
friend class PromiseResolver<T>;
|
||||||
@ -108,6 +245,30 @@ public:
|
|||||||
Promise(F &&resolver) : PromiseBase<T>(std::forward<F>(resolver)) {
|
Promise(F &&resolver) : PromiseBase<T>(std::forward<F>(resolver)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
Promise<U> convert() const {
|
||||||
|
return PromiseBase<T>::then(PromiseConverter<T, U>::create());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
Promise<T> each(Functor fn) {
|
||||||
|
return this->tap([=](const T &values) {
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
std::vector<Promise<void>> promises;
|
||||||
|
for (const auto &v : values) {
|
||||||
|
promises.push_back(PromiseHelper::attempt(fn, v, i).then([]() {
|
||||||
|
// Cast to void in case fn returns a non promise value.
|
||||||
|
// TODO remove when implicit cast is implemented.
|
||||||
|
}));
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PromiseHelper::all(promises);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static Promise<T> resolve(const T &value) {
|
static Promise<T> resolve(const T &value) {
|
||||||
return Promise<T>{[&](const PromiseResolve<T> &resolve) { resolve(value); }};
|
return Promise<T>{[&](const PromiseResolve<T> &resolve) { resolve(value); }};
|
||||||
}
|
}
|
||||||
@ -131,4 +292,36 @@ private:
|
|||||||
friend class PromiseBase<void>;
|
friend class PromiseBase<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace PromiseHelper {
|
||||||
|
Promise<void> resolve() {
|
||||||
|
return Promise<void>{[](const PromiseResolve<void> &resolve) { resolve(); }};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <template <typename, typename...> class Sequence = std::vector, typename... Args>
|
||||||
|
Promise<void> all(const Sequence<Promise<void>, Args...> &promises) {
|
||||||
|
const int count = static_cast<int>(promises.size());
|
||||||
|
if (count == 0) {
|
||||||
|
return PromiseHelper::resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise<void>{[=](const PromiseResolve<void> &resolve, const PromiseReject<void> &reject) {
|
||||||
|
auto remaining = std::make_shared<int>(count);
|
||||||
|
for (const auto &promise : promises) {
|
||||||
|
promise.then(
|
||||||
|
[=]() {
|
||||||
|
if (--(*remaining) == 0) {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[=]() {
|
||||||
|
if (*remaining != -1) {
|
||||||
|
*remaining = -1;
|
||||||
|
reject(std::current_exception());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
} // namespace PromiseHelper
|
||||||
|
|
||||||
#endif // __PROMISE_H__
|
#endif // __PROMISE_H__
|
@ -67,6 +67,7 @@ function build() {
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
$build_path/UnitTest/UnitTest
|
$build_path/UnitTest/UnitTest
|
||||||
|
# valgrind --leak-check=full $build_path/UnitTest/UnitTest
|
||||||
}
|
}
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
|
Loading…
Reference in New Issue
Block a user