Skip to content

Instantly share code, notes, and snippets.

@hikarin522
Created October 16, 2020 11:32
Show Gist options
  • Save hikarin522/e60bccbd424df5ed6f1298e85c519ea4 to your computer and use it in GitHub Desktop.
Save hikarin522/e60bccbd424df5ed6f1298e85c519ea4 to your computer and use it in GitHub Desktop.
c++ task
#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