180 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			180 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
 | |
| #pragma once
 | |
| 
 | |
| #if !defined(MIJIN_ASYNC_FUTURE_HPP_INCLUDED)
 | |
| #define MIJIN_ASYNC_FUTURE_HPP_INCLUDED 1
 | |
| 
 | |
| #include <memory>
 | |
| #include <optional>
 | |
| #include <tuple>
 | |
| #include <type_traits>
 | |
| #include "./signal.hpp"
 | |
| #include "../container/optional.hpp"
 | |
| #include "../debug/assert.hpp"
 | |
| #include "../internal/common.hpp"
 | |
| 
 | |
| namespace mijin
 | |
| {
 | |
| 
 | |
| //
 | |
| // public defines
 | |
| //
 | |
| 
 | |
| //
 | |
| // public constants
 | |
| //
 | |
| 
 | |
| //
 | |
| // public types
 | |
| //
 | |
| template<typename TValue, template<typename> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR>
 | |
| class Future;
 | |
| 
 | |
| // TODO: add support for mutexes and waiting for futures
 | |
| namespace impl
 | |
| {
 | |
| template<typename TValue>
 | |
| struct FutureStorage
 | |
| {
 | |
|     Optional<TValue> value;
 | |
| 
 | |
|     void setValue(TValue value_) MIJIN_NOEXCEPT { value = std::move(value_); }
 | |
|     [[nodiscard]] TValue& getValue() MIJIN_NOEXCEPT { return value.get(); }
 | |
| 
 | |
| };
 | |
| 
 | |
| // template<typename TValue>
 | |
| // struct FutureStorage<TValue&>
 | |
| // {
 | |
| //     Optional<TValue*> value;
 | |
| //
 | |
| //     void setValue(TValue& value_) MIJIN_NOEXCEPT { value = &value_; }
 | |
| //     [[nodiscard]] TValue& getValue() const MIJIN_NOEXCEPT { return *value.get(); }
 | |
| // };
 | |
| 
 | |
| template<>
 | |
| struct FutureStorage<void>
 | |
| {
 | |
| };
 | |
| } // namespace impl
 | |
| 
 | |
| template<typename TValue, template<typename> typename TAllocator>
 | |
| class Future
 | |
| {
 | |
| private:
 | |
|     [[no_unique_address]] impl::FutureStorage<TValue> value_;
 | |
|     bool isSet_ = false;
 | |
| public:
 | |
|     Future() = default;
 | |
|     Future(const Future&) = delete;
 | |
|     Future(Future&&) MIJIN_NOEXCEPT = default;
 | |
|     explicit Future(TAllocator<void> allocator) : sigSet(std::move(allocator)) {}
 | |
| public:
 | |
|     Future& operator=(const Future&) = delete;
 | |
|     Future& operator=(Future&&) MIJIN_NOEXCEPT = default;
 | |
| 
 | |
|     [[nodiscard]]
 | |
|     constexpr explicit operator bool() const MIJIN_NOEXCEPT { return ready(); }
 | |
| 
 | |
|     [[nodiscard]]
 | |
|     constexpr bool operator!() const MIJIN_NOEXCEPT { return !ready(); }
 | |
| public: // access
 | |
|     [[nodiscard]]
 | |
|     constexpr decltype(auto) get() MIJIN_NOEXCEPT
 | |
|     {
 | |
|         MIJIN_ASSERT(isSet_, "Attempting to get from future that is not ready.");
 | |
|         if constexpr(std::is_same_v<TValue, void>) {
 | |
|             return;
 | |
|         }
 | |
|         else {
 | |
|             return value_.getValue();
 | |
|         }
 | |
|     }
 | |
|     [[nodiscard]]
 | |
|     constexpr decltype(auto) get() const MIJIN_NOEXCEPT
 | |
|     {
 | |
|         MIJIN_ASSERT(isSet_, "Attempting to get from future that is not ready.");
 | |
|         if constexpr(std::is_same_v<TValue, void>) {
 | |
|             return;
 | |
|         }
 | |
|         else {
 | |
|             return value_.getValue();
 | |
|         }
 | |
|     }
 | |
|     [[nodiscard]]
 | |
|     constexpr bool ready() const MIJIN_NOEXCEPT
 | |
|     {
 | |
|         return isSet_;
 | |
|     }
 | |
| public: // modification
 | |
|     template<typename TArg> requires (!std::is_same_v<TValue, void>)
 | |
|     constexpr void set(TArg&& value) MIJIN_NOEXCEPT
 | |
|     {
 | |
|         MIJIN_ASSERT(!isSet_, "Trying to set a future twice!");
 | |
|         value_.setValue(std::move(value));
 | |
|         isSet_ = true;
 | |
|         sigSet.emit();
 | |
|     }
 | |
|     constexpr void set() MIJIN_NOEXCEPT requires (std::is_same_v<TValue, void>)
 | |
|     {
 | |
|         MIJIN_ASSERT(!isSet_, "Trying to set a future twice!");
 | |
|         isSet_ = true;
 | |
|         if constexpr (std::is_same_v<TValue, void>) {
 | |
|             sigSet.emit();
 | |
|         }
 | |
|         else {
 | |
|             // would love to make this a compile-time error :/
 | |
|             MIJIN_ERROR("Attempting to call set(void) on future with value.");
 | |
|         }
 | |
|     }
 | |
| public: // signals
 | |
|     BaseSignal<TAllocator> sigSet;
 | |
| };
 | |
| 
 | |
| template<typename TValue = void, template<typename> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR>
 | |
| using FuturePtr = std::shared_ptr<Future<TValue, TAllocator>>;
 | |
| 
 | |
| //
 | |
| // public functions
 | |
| //
 | |
| 
 | |
| namespace impl
 | |
| {
 | |
| template<typename... TResult>
 | |
| struct MultiFutureHelper
 | |
| {
 | |
|     template<std::size_t... indices>
 | |
|     static bool allReady(const std::tuple<FuturePtr<TResult>...>& futures, std::index_sequence<indices...>) MIJIN_NOEXCEPT
 | |
|     {
 | |
|         return (std::get<indices>(futures)->ready() && ...);
 | |
|     }
 | |
| 
 | |
|     template<std::size_t... indices>
 | |
|     static std::tuple<std::remove_reference_t<TResult>...> getAll(const std::tuple<FuturePtr<TResult>...>& futures, std::index_sequence<indices...>) MIJIN_NOEXCEPT
 | |
|     {
 | |
|         return std::make_tuple(std::move(std::get<indices>(futures)->get())...);
 | |
|     }
 | |
| };
 | |
| }
 | |
| 
 | |
| template<typename T, template<typename> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR>
 | |
| constexpr FuturePtr<T> makeSharedFuture(TAllocator<Future<T>> allocator = {}) MIJIN_NOEXCEPT
 | |
| {
 | |
|     return std::allocate_shared<Future<T>>(std::move(allocator));
 | |
| }
 | |
| 
 | |
| template<typename... TResult>
 | |
| constexpr bool allReady(const std::tuple<FuturePtr<TResult>...>& futures) MIJIN_NOEXCEPT
 | |
| {
 | |
|     return impl::MultiFutureHelper<TResult...>::allReady(futures, std::index_sequence_for<TResult...>());
 | |
| }
 | |
| 
 | |
| template<typename... TResult>
 | |
| constexpr std::tuple<std::remove_reference_t<TResult>...> getAll(const std::tuple<FuturePtr<TResult>...>& futures) MIJIN_NOEXCEPT
 | |
| {
 | |
|     return impl::MultiFutureHelper<TResult...>::getAll(futures, std::index_sequence_for<TResult...>());
 | |
| }
 | |
| } // namespace mijin
 | |
| 
 | |
| #endif // !defined(MIJIN_ASYNC_FUTURE_HPP_INCLUDED)
 |