#ifndef __PROMISEDATA_H__ #define __PROMISEDATA_H__ #include #include #include #include template class PromiseValue { public: PromiseValue() { } PromiseValue(const T &data) : m_data(std::make_shared(data)) { } PromiseValue(T &&data) : m_data(std::make_shared(std::forward(data))) { } bool isNull() const { return m_data == nullptr; } const T &data() const { return *m_data; } private: std::shared_ptr m_data; }; class PromiseError { public: template PromiseError(const T &value) { try { throw value; } catch (...) { m_data = std::current_exception(); } } PromiseError() { } PromiseError(const std::exception_ptr &exception) : m_data{exception} { } void rethrow() const { std::rethrow_exception(m_data); } bool isNull() const { return m_data == nullptr; } private: std::exception_ptr m_data; // std::exception_ptr is already a shared pointer }; template class PromiseDataBase { public: using Handler = std::function; using Catcher = std::function; bool isFulfilled() const { return !isPending() && m_error.isNull(); } bool isRejected() const { return !isPending() && !m_error.isNull(); } bool isPending() const { std::lock_guard locker{m_mutex}; return !m_settled; } void addHandler(std::function handler) { std::lock_guard locker{m_mutex}; m_handlers.push_back(std::move(handler)); } void addCatcher(std::function catcher) { std::lock_guard locker{m_mutex}; m_catchers.push_back(std::move(catcher)); } template void reject(E &&error) { assert(isPending()); assert(m_error.isNull()); m_error = PromiseError{std::forward(error)}; setSettled(); } const PromiseError &error() const { assert(isRejected()); return m_error; } void dispatch() { if (isPending()) { return; } m_mutex.lock(); auto handlers = std::move(m_handlers); auto catchers = std::move(m_catchers); m_mutex.unlock(); if (m_error.isNull()) { notify(handlers); return; } PromiseError error = m_error; assert(!error.isNull()); for (auto &catcher : catchers) { catcher(error); } } protected: virtual void notify(const std::list &handlers) = 0; void setSettled() { std::lock_guard locker{m_mutex}; assert(!m_settled); m_settled = true; } mutable std::mutex m_mutex; private: bool m_settled = false; std::list m_handlers; std::list m_catchers; PromiseError m_error; }; template class PromiseData : public PromiseDataBase { using Handler = typename PromiseDataBase::Handler; public: template void resolve(V &&value) { assert(this->isPending()); assert(m_value.isNull()); m_value = PromiseValue{std::forward(value)}; this->setSettled(); } const PromiseValue &value() const { assert(this->isFulfilled()); return m_value; } protected: void notify(const std::list &handlers) final { PromiseValue value = m_value; assert(!value.isNull()); for (auto &handler : handlers) { handler(value.data()); } } private: PromiseValue m_value; }; template <> class PromiseData : public PromiseDataBase { using Handler = PromiseDataBase::Handler; public: void resolve() { setSettled(); } protected: void notify(const std::list &handlers) { for (const auto &handler : handlers) { handler(); } } }; template struct PromiseConverterBase; template struct PromiseConverterBase { static std::function create() { return [](const T &value) { return static_cast(value); }; } }; template struct PromiseConverterBase { static std::function create() { return [](const T &value) { throw PromiseConversionException{}; return U{}; }; } }; template struct PromiseConverter : PromiseConverterBase::value || // Conversion to void. std::is_same::value || // Conversion between enums and arithmetic types. ((std::is_enum::value && std::is_arithmetic::value) || (std::is_arithmetic::value && std::is_enum::value) || (std::is_enum::value && std::is_enum::value))> {}; #endif // __PROMISEDATA_H__