Created
October 16, 2020 11:32
-
-
Save hikarin522/e60bccbd424df5ed6f1298e85c519ea4 to your computer and use it in GitHub Desktop.
c++ task
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <condition_variable> | |
#include <functional> | |
#include <future> | |
#include <memory> | |
#include <mutex> | |
#include <boost/optional.hpp> | |
#include <boost/variant.hpp> | |
namespace task | |
{ | |
namespace impl | |
{ | |
template <class T, std::enable_if_t< | |
!std::is_reference<T>::value && std::is_assignable<T&, std::exception_ptr>::value, | |
nullptr_t | |
> = nullptr> | |
class StateBase | |
{ | |
std::mutex m_mtx; | |
std::condition_variable m_cv; | |
boost::optional<T> m_value; | |
std::function<void(T&&)> m_callback; | |
public: | |
StateBase() = default; | |
virtual ~StateBase() = default; | |
StateBase(const StateBase&) = delete; | |
StateBase& operator =(const StateBase&) = delete; | |
StateBase(StateBase&&) = default; | |
StateBase& operator =(StateBase&&) = default; | |
protected: | |
template <class _T, std::enable_if_t<std::is_assignable<T&, _T>::value, nullptr_t> = nullptr> | |
void set_value(_T &&value) | |
{ | |
{ | |
std::lock_guard<std::mutex> _lock(m_mtx); | |
if (m_callback) { | |
m_callback(std::forward<_T>(value)); | |
return; | |
} | |
m_value = std::forward<_T>(value); | |
} | |
m_cv.notify_one(); | |
} | |
T get_value() | |
{ | |
std::unique_lock<std::mutex> _lock(m_mtx); | |
m_cv.wait(_lock, [this]() { return static_cast<bool>(m_value); }); | |
return std::move(*m_value); | |
} | |
public: | |
template <class _T, std::enable_if_t<std::is_assignable<std::exception_ptr&, _T>::value, nullptr_t> = nullptr> | |
void set_exception(_T &&ep) | |
{ | |
set_value(std::forward<_T>(ep)); | |
} | |
template <class _T, std::enable_if_t<std::is_assignable<std::function<void(T&&)>&, _T>::value, nullptr_t> = nullptr> | |
void set_callback(_T &&callback) | |
{ | |
std::lock_guard<std::mutex> _lock(m_mtx); | |
if (!m_value) { | |
m_callback = std::forward<_T>(callback); | |
} else { | |
callback(std::move(*m_value)); | |
} | |
} | |
}; | |
template <class T> | |
class State final: | |
public StateBase<boost::variant<std::exception_ptr, T>>, | |
public std::enable_shared_from_this<State<T>> | |
{ | |
using base = StateBase<boost::variant<std::exception_ptr, T>>; | |
public: | |
void set_value(T &&value) | |
{ | |
base::set_value(std::move(value)); | |
} | |
T get_value() | |
{ | |
auto &&value = base::get_value(); | |
if (value.witch() == 0) { | |
std::rethrow_exception(boost::get<0>(value)); | |
} | |
return std::move(boost::get<1>(value)); | |
} | |
}; | |
template <class T> | |
class State<T&> final: | |
public StateBase<boost::variant<std::exception_ptr, std::reference_wrapper<T>>>, | |
public std::enable_shared_from_this<State<T&>> | |
{ | |
using base = StateBase<boost::variant<std::exception_ptr, std::reference_wrapper<T>>>; | |
public: | |
void set_value(T &value) | |
{ | |
base::set_value(std::ref(value)); | |
} | |
T get_value() | |
{ | |
auto &&value = base::get_value(); | |
if (value.witch() == 0) { | |
std::rethrow_exception(boost::get<0>(value)); | |
} | |
return std::move(boost::get<1>(value)); | |
} | |
}; | |
template <> | |
class State<void> final: | |
public StateBase<std::exception_ptr>, | |
public std::enable_shared_from_this<State<void>> | |
{ | |
using base = StateBase<std::exception_ptr>; | |
public: | |
void set_value() | |
{ | |
base::set_value(nullptr); | |
} | |
void get_value() | |
{ | |
auto &&value = base::get_value(); | |
if (value) { | |
std::rethrow_exception(value); | |
} | |
} | |
}; | |
template <class T> | |
class PromiseBase | |
{ | |
protected: | |
std::shared_ptr<State<T>> m_state; | |
PromiseBase() = default; | |
virtual ~PromiseBase() = default; | |
PromiseBase(const PromiseBase&) = delete; | |
PromiseBase& operator =(const PromiseBase&) = delete; | |
PromiseBase(PromiseBase&&) = default; | |
PromiseBase& operator =(PromiseBase&&) = default; | |
public: | |
Future<T> get_future() | |
{ | |
if (m_state) { | |
throw std::future_errc::future_already_retrieved; | |
} | |
auto state = std::make_shared<State<T>>(); | |
m_state = state; | |
return { std::move(state) }; | |
} | |
template <class _T, std::enable_if_t<std::is_assignable<std::exception_ptr&, _T>::value, nullptr_t> = nullptr> | |
void set_exception(_T &&ep) && | |
{ | |
auto state = std::move(m_state); | |
state->set_exception(std::forward<_T>(ep)); | |
} | |
}; | |
template <class T> | |
class Promise final: public PromiseBase<T> | |
{ | |
public: | |
using PromiseBase<T>::PromiseBase; | |
template <class _T, std::enable_if_t<std::is_assignable<T&, _T>::value, nullptr_t> = nullptr> | |
void set_value(_T &&value) && | |
{ | |
auto state = std::move(m_state); | |
state->set_value(std::forward<T>(value)); | |
} | |
}; | |
template <> | |
class Promise<void> final: public PromiseBase<void> | |
{ | |
public: | |
using PromiseBase<void>::PromiseBase; | |
void set_value(void) && | |
{ | |
auto state = std::move(m_state); | |
state->set_value(); | |
} | |
}; | |
template <class T> | |
class FutureBase | |
{ | |
protected: | |
std::shared_ptr<State<T>> m_state; | |
template <class _T, std::enable_if_t<std::is_assignable<std::shared_ptr<State<T>>&, _T>::value, nullptr_t> = nullptr> | |
FutureBase(_T &&state): | |
m_state(std::forward<_T>(state)) | |
{ } | |
FutureBase() = delete; | |
virtual ~FutureBase() = default; | |
FutureBase(const FutureBase&) = delete; | |
FutureBase& operator =(const FutureBase&) = delete; | |
FutureBase(FutureBase&&) = default; | |
FutureBase& operator =(FutureBase&&) = default; | |
public: | |
bool valid() const | |
{ | |
return m_state; | |
} | |
}; | |
template <class T> | |
class Future final: public FutureBase<T> | |
{ | |
public: | |
using FutureBase<T>::FutureBase; | |
T get() && | |
{ | |
auto state = std::move(m_state); | |
return state->get_value(); | |
} | |
template <class _T, std::enable_if_t<std::is_assignable<std::function<void(boost::variant<std::exception_ptr, T>&&)>&, _T>::value, nullptr_t> = nullptr> | |
void set_callback(_T &&callback) && | |
{ | |
auto state == std::move(m_state); | |
state->set_callback(std::forward<_T>(callback)); | |
} | |
}; | |
template <> | |
class Future<void> final: public FutureBase<void> | |
{ | |
public: | |
using FutureBase<void>::FutureBase; | |
void get() && | |
{ | |
auto state = std::move(m_state); | |
state->get_value(); | |
} | |
template <class _T, std::enable_if_t<std::is_assignable<std::function<void(std::exception_ptr&&)>&, _T>::value, nullptr_t> = nullptr> | |
void set_callback(_T &&callback) && | |
{ | |
auto state == std::move(m_state); | |
state->set_callback(std::forward<_T>(callback)); | |
} | |
}; | |
} // namespace impl | |
template <class T> | |
using promise = impl::Promise<T>; | |
template <class T> | |
using future = impl::Future<T>; | |
void post(std::function<void()>&&); | |
template <class T> | |
class Task final | |
{ | |
future<T> m_future; | |
template <class _T, std::enable_if_t<std::is_assignable<future<T>&, _T>::value, nullptr_t> = nullptr> | |
Task(_T &&future): | |
m_future(std::forward<_T>(future)) | |
{ } | |
public: | |
template <class _T, std::enable_if_t<std::is_assignable<std::function<T()>&, _T>::value, nullptr_t> = nullptr> | |
Task(_T &&func) | |
{ | |
auto p = promise<T>(); | |
m_future = p.get_future(); | |
post([p = std::move(p), func = std::move(func)]() mutable { | |
try { | |
std::move(p).set_value(std::move(func)()); | |
} catch (...) { | |
std::move(p).set_exception(std::current_exception()); | |
} | |
}); | |
} | |
T GetResult() && | |
{ | |
return std::move(m_future).get_value(); | |
} | |
template <class _T, class _U, std::enable_if_t<std::is_assignable<std::function<_T(T&&)>&, _U>::value, nullptr_t> = nullptr> | |
Task<_T> Then(_U &&func) && | |
{ | |
auto p = promise<_T>(); | |
auto f = p.get_future(); | |
std::move(m_future).set_callback([p = std::move(p), func = std::move(func)](boost::variant<std::exception_ptr, T> &&value) mutable { | |
if (value.which() == 0) { | |
std::move(p).set_exception(std::move(boost::get<0>(value))); | |
return; | |
} | |
post([p = std::move(p), func = std::move(func), value = std::move(boost::get<1>(value))]() mutable { | |
try { | |
std::move(p).set_value(std::move(func)(std::move(value))); | |
} catch (...) { | |
std::move(p).set_exception(std::current_exception()); | |
} | |
}); | |
}); | |
return { std::move(f) }; | |
} | |
}; | |
template <class T, class U, std::enable_if_t<std::is_assignable<std::function<T()>&, U>::value, nullptr_t> = nullptr> | |
Task<T> create_task(U &&func) | |
{ | |
{ std::forward<U>(func) }; | |
} | |
} // namespace task |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment