diff --git a/.gitmodules b/.gitmodules index c345e54a..9942b400 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,3 +25,9 @@ [submodule "toolset/model_converter/modules/assimp"] path = toolset/model_converter/modules/assimp url = https://github.com/assimp/assimp +[submodule "modules/invoke.hpp"] + path = modules/invoke.hpp + url = https://github.com/BlackMATov/invoke.hpp +[submodule "modules/promise.hpp"] + path = modules/promise.hpp + url = https://github.com/BlackMATov/promise.hpp diff --git a/headers/3rdparty/invoke.hpp/invoke.hpp b/headers/3rdparty/invoke.hpp/invoke.hpp new file mode 100644 index 00000000..4df38aa4 --- /dev/null +++ b/headers/3rdparty/invoke.hpp/invoke.hpp @@ -0,0 +1,248 @@ +/******************************************************************************* + * This file is part of the "invoke.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018 Matvey Cherevko + ******************************************************************************/ + +#pragma once + +#include +#include +#include +#include + +#define INVOKE_HPP_NOEXCEPT_RETURN(...) \ + noexcept(noexcept(__VA_ARGS__)) { return __VA_ARGS__; } + +#define INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN(...) \ + noexcept(noexcept(__VA_ARGS__)) -> decltype (__VA_ARGS__) { return __VA_ARGS__; } + +// +// void_t +// + +namespace invoke_hpp +{ + namespace impl + { + template < typename... Args > + struct make_void { + using type = void; + }; + } + + template < typename... Args > + using void_t = typename impl::make_void::type; +} + +// +// is_reference_wrapper +// + +namespace invoke_hpp +{ + namespace impl + { + template < typename T > + struct is_reference_wrapper_impl + : std::false_type {}; + + template < typename U > + struct is_reference_wrapper_impl> + : std::true_type {}; + } + + template < typename T > + struct is_reference_wrapper + : impl::is_reference_wrapper_impl> {}; +} + +// +// invoke +// + +namespace invoke_hpp +{ + namespace impl + { + // + // invoke_member_object_impl + // + + template + < + typename Base, typename F, typename Derived, + typename std::enable_if_t>::value, int> = 0 + > + constexpr auto invoke_member_object_impl(F Base::* f, Derived&& ref) + INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN( + std::forward(ref).*f) + + template + < + typename Base, typename F, typename RefWrap, + typename std::enable_if_t>::value, int> = 0 + > + constexpr auto invoke_member_object_impl(F Base::* f, RefWrap&& ref) + INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN( + ref.get().*f) + + template + < + typename Base, typename F, typename Pointer, + typename std::enable_if_t< + !std::is_base_of>::value && + !is_reference_wrapper>::value + , int> = 0 + > + constexpr auto invoke_member_object_impl(F Base::* f, Pointer&& ptr) + INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN( + (*std::forward(ptr)).*f) + + // + // invoke_member_function_impl + // + + template + < + typename Base, typename F, typename Derived, typename... Args, + typename std::enable_if_t>::value, int> = 0 + > + constexpr auto invoke_member_function_impl(F Base::* f, Derived&& ref, Args&&... args) + INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN( + (std::forward(ref).*f)(std::forward(args)...)) + + template + < + typename Base, typename F, typename RefWrap, typename... Args, + typename std::enable_if_t>::value, int> = 0 + > + constexpr auto invoke_member_function_impl(F Base::* f, RefWrap&& ref, Args&&... args) + INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN( + (ref.get().*f)(std::forward(args)...)) + + template + < + typename Base, typename F, typename Pointer, typename... Args, + typename std::enable_if_t< + !std::is_base_of>::value && + !is_reference_wrapper>::value + , int> = 0 + > + constexpr auto invoke_member_function_impl(F Base::* f, Pointer&& ptr, Args&&... args) + INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN( + ((*std::forward(ptr)).*f)(std::forward(args)...)) + } + + template + < + typename F, typename... Args, + typename std::enable_if_t>::value, int> = 0 + > + constexpr auto invoke(F&& f, Args&&... args) + INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN( + std::forward(f)(std::forward(args)...)) + + template + < + typename F, typename T, + typename std::enable_if_t>::value, int> = 0 + > + constexpr auto invoke(F&& f, T&& t) + INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN( + impl::invoke_member_object_impl(std::forward(f), std::forward(t))) + + template + < + typename F, typename... Args, + typename std::enable_if_t>::value, int> = 0 + > + constexpr auto invoke(F&& f, Args&&... args) + INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN( + impl::invoke_member_function_impl(std::forward(f), std::forward(args)...)) +} + +// +// invoke_result +// + +namespace invoke_hpp +{ + namespace impl + { + struct invoke_result_impl_tag {}; + + template < typename Void, typename F, typename... Args > + struct invoke_result_impl {}; + + template < typename F, typename... Args > + struct invoke_result_impl(), std::declval()...))>, F, Args...> { + using type = decltype(invoke_hpp::invoke(std::declval(), std::declval()...)); + }; + } + + template < typename F, typename... Args > + struct invoke_result + : impl::invoke_result_impl {}; + + template < typename F, typename... Args > + using invoke_result_t = typename invoke_result::type; +} + +// +// is_invocable +// + +namespace invoke_hpp +{ + namespace impl + { + struct is_invocable_r_impl_tag {}; + + template < typename Void, typename R, typename F, typename... Args > + struct is_invocable_r_impl + : std::false_type {}; + + template < typename R, typename F, typename... Args > + struct is_invocable_r_impl>, R, F, Args...> + : std::conditional_t< + std::is_void::value, + std::true_type, + std::is_convertible, R>> {}; + } + + template < typename R, typename F, typename... Args > + struct is_invocable_r + : impl::is_invocable_r_impl {}; + + template < typename F, typename... Args > + using is_invocable = is_invocable_r; +} + +// +// apply +// + +namespace invoke_hpp +{ + namespace impl + { + template < typename F, typename Tuple, std::size_t... I > + constexpr decltype(auto) apply_impl(F&& f, Tuple&& args, std::index_sequence) + INVOKE_HPP_NOEXCEPT_RETURN( + invoke_hpp::invoke( + std::forward(f), + std::get(std::forward(args))...)) + } + + template < typename F, typename Tuple > + constexpr decltype(auto) apply(F&& f, Tuple&& args) + INVOKE_HPP_NOEXCEPT_RETURN( + impl::apply_impl( + std::forward(f), + std::forward(args), + std::make_index_sequence>::value>())) +} + +#undef INVOKE_HPP_NOEXCEPT_RETURN +#undef INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN diff --git a/headers/3rdparty/promise.hpp/promise.hpp b/headers/3rdparty/promise.hpp/promise.hpp new file mode 100644 index 00000000..febbf579 --- /dev/null +++ b/headers/3rdparty/promise.hpp/promise.hpp @@ -0,0 +1,1182 @@ +/******************************************************************************* + * This file is part of the "promise.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018 Matvey Cherevko + ******************************************************************************/ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// +// invoke.hpp +// https://github.com/BlackMATov/invoke.hpp +// + +namespace promise_hpp +{ + #define INVOKE_HPP_NOEXCEPT_RETURN(...) \ + noexcept(noexcept(__VA_ARGS__)) { return __VA_ARGS__; } + + #define INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN(...) \ + noexcept(noexcept(__VA_ARGS__)) -> decltype (__VA_ARGS__) { return __VA_ARGS__; } + + // + // void_t + // + + namespace invoke_hpp + { + namespace impl + { + template < typename... Args > + struct make_void { + using type = void; + }; + } + + template < typename... Args > + using void_t = typename impl::make_void::type; + } + + // + // is_reference_wrapper + // + + namespace invoke_hpp + { + namespace impl + { + template < typename T > + struct is_reference_wrapper_impl + : std::false_type {}; + + template < typename U > + struct is_reference_wrapper_impl> + : std::true_type {}; + } + + template < typename T > + struct is_reference_wrapper + : impl::is_reference_wrapper_impl> {}; + } + + // + // invoke + // + + namespace invoke_hpp + { + namespace impl + { + // + // invoke_member_object_impl + // + + template + < + typename Base, typename F, typename Derived, + typename std::enable_if_t>::value, int> = 0 + > + constexpr auto invoke_member_object_impl(F Base::* f, Derived&& ref) + INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN( + std::forward(ref).*f) + + template + < + typename Base, typename F, typename RefWrap, + typename std::enable_if_t>::value, int> = 0 + > + constexpr auto invoke_member_object_impl(F Base::* f, RefWrap&& ref) + INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN( + ref.get().*f) + + template + < + typename Base, typename F, typename Pointer, + typename std::enable_if_t< + !std::is_base_of>::value && + !is_reference_wrapper>::value + , int> = 0 + > + constexpr auto invoke_member_object_impl(F Base::* f, Pointer&& ptr) + INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN( + (*std::forward(ptr)).*f) + + // + // invoke_member_function_impl + // + + template + < + typename Base, typename F, typename Derived, typename... Args, + typename std::enable_if_t>::value, int> = 0 + > + constexpr auto invoke_member_function_impl(F Base::* f, Derived&& ref, Args&&... args) + INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN( + (std::forward(ref).*f)(std::forward(args)...)) + + template + < + typename Base, typename F, typename RefWrap, typename... Args, + typename std::enable_if_t>::value, int> = 0 + > + constexpr auto invoke_member_function_impl(F Base::* f, RefWrap&& ref, Args&&... args) + INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN( + (ref.get().*f)(std::forward(args)...)) + + template + < + typename Base, typename F, typename Pointer, typename... Args, + typename std::enable_if_t< + !std::is_base_of>::value && + !is_reference_wrapper>::value + , int> = 0 + > + constexpr auto invoke_member_function_impl(F Base::* f, Pointer&& ptr, Args&&... args) + INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN( + ((*std::forward(ptr)).*f)(std::forward(args)...)) + } + + template + < + typename F, typename... Args, + typename std::enable_if_t>::value, int> = 0 + > + constexpr auto invoke(F&& f, Args&&... args) + INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN( + std::forward(f)(std::forward(args)...)) + + template + < + typename F, typename T, + typename std::enable_if_t>::value, int> = 0 + > + constexpr auto invoke(F&& f, T&& t) + INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN( + impl::invoke_member_object_impl(std::forward(f), std::forward(t))) + + template + < + typename F, typename... Args, + typename std::enable_if_t>::value, int> = 0 + > + constexpr auto invoke(F&& f, Args&&... args) + INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN( + impl::invoke_member_function_impl(std::forward(f), std::forward(args)...)) + } + + // + // invoke_result + // + + namespace invoke_hpp + { + namespace impl + { + struct invoke_result_impl_tag {}; + + template < typename Void, typename F, typename... Args > + struct invoke_result_impl {}; + + template < typename F, typename... Args > + struct invoke_result_impl(), std::declval()...))>, F, Args...> { + using type = decltype(invoke_hpp::invoke(std::declval(), std::declval()...)); + }; + } + + template < typename F, typename... Args > + struct invoke_result + : impl::invoke_result_impl {}; + + template < typename F, typename... Args > + using invoke_result_t = typename invoke_result::type; + } + + // + // is_invocable + // + + namespace invoke_hpp + { + namespace impl + { + struct is_invocable_r_impl_tag {}; + + template < typename Void, typename R, typename F, typename... Args > + struct is_invocable_r_impl + : std::false_type {}; + + template < typename R, typename F, typename... Args > + struct is_invocable_r_impl>, R, F, Args...> + : std::conditional_t< + std::is_void::value, + std::true_type, + std::is_convertible, R>> {}; + } + + template < typename R, typename F, typename... Args > + struct is_invocable_r + : impl::is_invocable_r_impl {}; + + template < typename F, typename... Args > + using is_invocable = is_invocable_r; + } + + // + // apply + // + + namespace invoke_hpp + { + namespace impl + { + template < typename F, typename Tuple, std::size_t... I > + constexpr decltype(auto) apply_impl(F&& f, Tuple&& args, std::index_sequence) + INVOKE_HPP_NOEXCEPT_RETURN( + invoke_hpp::invoke( + std::forward(f), + std::get(std::forward(args))...)) + } + + template < typename F, typename Tuple > + constexpr decltype(auto) apply(F&& f, Tuple&& args) + INVOKE_HPP_NOEXCEPT_RETURN( + impl::apply_impl( + std::forward(f), + std::forward(args), + std::make_index_sequence>::value>())) + } + + #undef INVOKE_HPP_NOEXCEPT_RETURN + #undef INVOKE_HPP_NOEXCEPT_DECLTYPE_RETURN +} + +// +// promise.hpp +// https://github.com/BlackMATov/promise.hpp +// + +namespace promise_hpp +{ + // + // forward declaration + // + + template < typename T = void > + class promise; + + // + // is_promise + // + + namespace impl + { + template < typename T > + struct is_promise_impl + : std::false_type {}; + + template < typename R > + struct is_promise_impl> + : std::true_type {}; + } + + template < typename T > + struct is_promise + : impl::is_promise_impl> {}; + + // + // is_promise_r + // + + namespace impl + { + template < typename R, typename T > + struct is_promise_r_impl + : std::false_type {}; + + template < typename R, typename PR > + struct is_promise_r_impl> + : std::is_convertible {}; + } + + template < typename R, typename T > + struct is_promise_r + : impl::is_promise_r_impl> {}; + + // + // detail + // + + namespace detail + { + class noncopyable { + public: + noncopyable(const noncopyable&) = delete; + noncopyable& operator=(const noncopyable&) = delete; + protected: + noncopyable() = default; + ~noncopyable() = default; + }; + + template < typename T > + class storage final : private noncopyable { + public: + storage() = default; + + ~storage() noexcept(std::is_nothrow_destructible::value) { + if ( initialized_ ) { + ptr_()->~T(); + } + } + + template < typename U > + void set(U&& value) noexcept(std::is_nothrow_constructible::value) { + assert(!initialized_); + ::new(ptr_()) T(std::forward(value)); + initialized_ = true; + } + + const T& value() const noexcept { + assert(initialized_); + return *ptr_(); + } + private: + T* ptr_() noexcept { + return reinterpret_cast(&data_); + } + + const T* ptr_() const noexcept { + return reinterpret_cast(&data_); + } + private: + std::aligned_storage_t data_; + bool initialized_ = false; + }; + } + + // + // promise + // + + template < typename T > + class promise final { + public: + using value_type = T; + + enum class status : std::uint8_t { + pending, + resolved, + rejected + }; + public: + promise() + : state_(std::make_shared()) {} + + promise(const promise&) noexcept = default; + promise& operator=(const promise&) noexcept = default; + + void swap(promise& other) noexcept { + state_.swap(other.state_); + } + + std::size_t hash() const noexcept { + return std::hash()(state_.get()); + } + + friend bool operator<(const promise& l, const promise& r) noexcept { + return l.state_ < r.state_; + } + + friend bool operator==(const promise& l, const promise& r) noexcept { + return l.state_ == r.state_; + } + + friend bool operator!=(const promise& l, const promise& r) noexcept { + return l.state_ != r.state_; + } + + template < typename ResolveF + , typename ResolveFR = invoke_hpp::invoke_result_t > + std::enable_if_t< + is_promise::value && std::is_void::value, + promise> + then(ResolveF&& on_resolve) { + promise next; + + then([ + n = next, + f = std::forward(on_resolve) + ](const T& v) mutable { + auto np = invoke_hpp::invoke( + std::forward(f), + v); + np.then([n]() mutable { + n.resolve(); + }, [n](std::exception_ptr e) mutable { + n.reject(e); + }); + }, [n = next](std::exception_ptr e) mutable { + n.reject(e); + }); + + return next; + } + + template < typename ResolveF + , typename ResolveFR = invoke_hpp::invoke_result_t > + std::enable_if_t< + is_promise::value && !std::is_void::value, + promise> + then(ResolveF&& on_resolve) { + promise next; + + then([ + n = next, + f = std::forward(on_resolve) + ](const T& v) mutable { + auto np = invoke_hpp::invoke( + std::forward(f), + v); + np.then([n](const typename ResolveFR::value_type& nv) mutable { + n.resolve(nv); + }, [n](std::exception_ptr e) mutable { + n.reject(e); + }); + }, [n = next](std::exception_ptr e) mutable { + n.reject(e); + }); + + return next; + } + + template < typename ResolveF > + auto then_all(ResolveF&& on_resolve) { + return then([ + f = std::forward(on_resolve) + ](const T& v) mutable { + auto r = invoke_hpp::invoke( + std::forward(f), + v); + return make_all_promise(std::move(r)); + }); + } + + template < typename ResolveF > + auto then_any(ResolveF&& on_resolve) { + return then([ + f = std::forward(on_resolve) + ](const T& v) mutable { + auto r = invoke_hpp::invoke( + std::forward(f), + v); + return make_any_promise(std::move(r)); + }); + } + + template < typename ResolveF + , typename RejectF + , typename ResolveFR = invoke_hpp::invoke_result_t > + std::enable_if_t< + !is_promise::value, + promise> + then(ResolveF&& on_resolve, RejectF&& on_reject) { + promise next; + state_->attach( + next, + std::forward(on_resolve), + std::forward(on_reject)); + return next; + } + + template < typename ResolveF + , typename ResolveFR = invoke_hpp::invoke_result_t > + std::enable_if_t< + !is_promise::value, + promise> + then(ResolveF&& on_resolve) { + return then( + std::forward(on_resolve), + [](std::exception_ptr){}); + } + + template < typename RejectF > + promise except(RejectF&& on_reject) { + return then( + [](const T& value) { return value; }, + std::forward(on_reject)); + } + + template < typename U > + bool resolve(U&& value) { + return state_->resolve( + std::forward(value)); + } + + bool reject(std::exception_ptr e) noexcept { + return state_->reject(e); + } + + template < typename E > + bool reject(E&& e) { + return state_->reject( + std::make_exception_ptr(std::forward(e))); + } + private: + class state; + std::shared_ptr state_; + private: + class state final : private detail::noncopyable { + public: + state() = default; + + template < typename U > + bool resolve(U&& value) { + std::lock_guard guard(mutex_); + if ( status_ != status::pending ) { + return false; + } + storage_.set(std::forward(value)); + status_ = status::resolved; + invoke_resolve_handlers_(); + return true; + } + + bool reject(std::exception_ptr e) noexcept { + std::lock_guard guard(mutex_); + if ( status_ != status::pending ) { + return false; + } + exception_ = e; + status_ = status::rejected; + invoke_reject_handlers_(); + return true; + } + + template < typename U, typename ResolveF, typename RejectF > + std::enable_if_t::value, void> + attach(promise& next, ResolveF&& resolve, RejectF&& reject) { + auto reject_h = [ + n = next, + f = std::forward(reject) + ](std::exception_ptr e) mutable { + try { + invoke_hpp::invoke( + std::forward(f), + e); + n.reject(e); + } catch (...) { + n.reject(std::current_exception()); + } + }; + + auto resolve_h = [ + n = next, + f = std::forward(resolve), + j = reject_h + ](const T& v) mutable { + try { + invoke_hpp::invoke( + std::forward(f), + v); + n.resolve(); + } catch (...) { + invoke_hpp::invoke( + std::move(j), + std::current_exception()); + } + }; + + std::lock_guard guard(mutex_); + add_handlers_(std::move(resolve_h), std::move(reject_h)); + } + + template < typename U, typename ResolveF, typename RejectF > + std::enable_if_t::value, void> + attach(promise& next, ResolveF&& resolve, RejectF&& reject) { + auto reject_h = [ + n = next, + f = std::forward(reject) + ](std::exception_ptr e) mutable { + try { + invoke_hpp::invoke( + std::forward(f), + e); + n.reject(e); + } catch (...) { + n.reject(std::current_exception()); + } + }; + + auto resolve_h = [ + n = next, + f = std::forward(resolve), + j = reject_h + ](const T& v) mutable { + try { + auto r = invoke_hpp::invoke( + std::forward(f), + v); + n.resolve(std::move(r)); + } catch (...) { + invoke_hpp::invoke( + std::move(j), + std::current_exception()); + } + }; + + std::lock_guard guard(mutex_); + add_handlers_(std::move(resolve_h), std::move(reject_h)); + } + private: + template < typename ResolveF, typename RejectF > + void add_handlers_(ResolveF&& resolve, RejectF&& reject) { + if ( status_ == status::resolved ) { + invoke_hpp::invoke( + std::forward(resolve), + storage_.value()); + } else if ( status_ == status::rejected ) { + invoke_hpp::invoke( + std::forward(reject), + exception_); + } else { + handlers_.emplace_back( + std::forward(resolve), + std::forward(reject)); + } + } + + void invoke_resolve_handlers_() noexcept { + const T& value = storage_.value(); + for ( const auto& h : handlers_ ) { + h.resolve_(value); + } + handlers_.clear(); + } + + void invoke_reject_handlers_() noexcept { + for ( const auto& h : handlers_ ) { + h.reject_(exception_); + } + handlers_.clear(); + } + private: + detail::storage storage_; + status status_ = status::pending; + std::exception_ptr exception_ = nullptr; + + std::mutex mutex_; + + struct handler { + using resolve_t = std::function; + using reject_t = std::function; + + resolve_t resolve_; + reject_t reject_; + + template < typename ResolveF, typename RejectF > + handler(ResolveF&& resolve, RejectF&& reject) + : resolve_(std::forward(resolve)) + , reject_(std::forward(reject)) {} + }; + + std::vector handlers_; + }; + }; + + // + // promise + // + + template <> + class promise final { + public: + using value_type = void; + + enum class status : std::uint8_t { + pending, + resolved, + rejected + }; + public: + promise() + : state_(std::make_shared()) {} + + promise(const promise&) noexcept = default; + promise& operator=(const promise&) noexcept = default; + + void swap(promise& other) noexcept { + state_.swap(other.state_); + } + + std::size_t hash() const noexcept { + return std::hash()(state_.get()); + } + + friend bool operator<(const promise& l, const promise& r) noexcept { + return l.state_ < r.state_; + } + + friend bool operator==(const promise& l, const promise& r) noexcept { + return l.state_ == r.state_; + } + + friend bool operator!=(const promise& l, const promise& r) noexcept { + return l.state_ != r.state_; + } + + template < typename ResolveF + , typename ResolveFR = invoke_hpp::invoke_result_t > + std::enable_if_t< + is_promise::value && std::is_void::value, + promise> + then(ResolveF&& on_resolve) { + promise next; + + then([ + n = next, + f = std::forward(on_resolve) + ]() mutable { + auto np = invoke_hpp::invoke( + std::forward(f)); + np.then([n]() mutable { + n.resolve(); + }, [n](std::exception_ptr e) mutable { + n.reject(e); + }); + }, [n = next](std::exception_ptr e) mutable { + n.reject(e); + }); + + return next; + } + + template < typename ResolveF + , typename ResolveFR = invoke_hpp::invoke_result_t > + std::enable_if_t< + is_promise::value && !std::is_void::value, + promise> + then(ResolveF&& on_resolve) { + promise next; + + then([ + n = next, + f = std::forward(on_resolve) + ]() mutable { + auto np = invoke_hpp::invoke( + std::forward(f)); + np.then([n](const typename ResolveFR::value_type& nv) mutable { + n.resolve(nv); + }, [n](std::exception_ptr e) mutable { + n.reject(e); + }); + }, [n = next](std::exception_ptr e) mutable { + n.reject(e); + }); + + return next; + } + + template < typename ResolveF > + auto then_all(ResolveF&& on_resolve) { + return then([ + f = std::forward(on_resolve) + ]() mutable { + auto r = invoke_hpp::invoke( + std::forward(f)); + return make_all_promise(std::move(r)); + }); + } + + template < typename ResolveF > + auto then_any(ResolveF&& on_resolve) { + return then([ + f = std::forward(on_resolve) + ]() mutable { + auto r = invoke_hpp::invoke( + std::forward(f)); + return make_any_promise(std::move(r)); + }); + } + + template < typename ResolveF + , typename RejectF + , typename ResolveFR = invoke_hpp::invoke_result_t > + std::enable_if_t< + !is_promise::value, + promise> + then(ResolveF&& on_resolve, RejectF&& on_reject) { + promise next; + state_->attach( + next, + std::forward(on_resolve), + std::forward(on_reject)); + return next; + } + + template < typename ResolveF + , typename ResolveFR = invoke_hpp::invoke_result_t > + std::enable_if_t< + !is_promise::value, + promise> + then(ResolveF&& on_resolve) { + return then( + std::forward(on_resolve), + [](std::exception_ptr){}); + } + + template < typename RejectF > + promise except(RejectF&& on_reject) { + return then( + []{}, + std::forward(on_reject)); + } + + bool resolve() { + return state_->resolve(); + } + + bool reject(std::exception_ptr e) noexcept { + return state_->reject(e); + } + + template < typename E > + bool reject(E&& e) { + return state_->reject( + std::make_exception_ptr(std::forward(e))); + } + private: + class state; + std::shared_ptr state_; + private: + class state final : private detail::noncopyable { + public: + state() = default; + + bool resolve() { + std::lock_guard guard(mutex_); + if ( status_ != status::pending ) { + return false; + } + status_ = status::resolved; + invoke_resolve_handlers_(); + return true; + } + + bool reject(std::exception_ptr e) noexcept { + std::lock_guard guard(mutex_); + if ( status_ != status::pending ) { + return false; + } + exception_ = e; + status_ = status::rejected; + invoke_reject_handlers_(); + return true; + } + + template < typename U, typename ResolveF, typename RejectF > + std::enable_if_t::value, void> + attach(promise& next, ResolveF&& resolve, RejectF&& reject) { + auto reject_h = [ + n = next, + f = std::forward(reject) + ](std::exception_ptr e) mutable { + try { + invoke_hpp::invoke( + std::forward(f), + e); + n.reject(e); + } catch (...) { + n.reject(std::current_exception()); + } + }; + + auto resolve_h = [ + n = next, + f = std::forward(resolve), + j = reject_h + ]() mutable { + try { + invoke_hpp::invoke( + std::forward(f)); + n.resolve(); + } catch (...) { + invoke_hpp::invoke( + std::move(j), + std::current_exception()); + } + }; + + std::lock_guard guard(mutex_); + add_handlers_(std::move(resolve_h), std::move(reject_h)); + } + + template < typename U, typename ResolveF, typename RejectF > + std::enable_if_t::value, void> + attach(promise& next, ResolveF&& resolve, RejectF&& reject) { + auto reject_h = [ + n = next, + f = std::forward(reject) + ](std::exception_ptr e) mutable { + try { + invoke_hpp::invoke( + std::forward(f), + e); + n.reject(e); + } catch (...) { + n.reject(std::current_exception()); + } + }; + + auto resolve_h = [ + n = next, + f = std::forward(resolve), + j = reject_h + ]() mutable { + try { + auto r = invoke_hpp::invoke( + std::forward(f)); + n.resolve(std::move(r)); + } catch (...) { + invoke_hpp::invoke( + std::move(j), + std::current_exception()); + } + }; + + std::lock_guard guard(mutex_); + add_handlers_(std::move(resolve_h), std::move(reject_h)); + } + private: + template < typename ResolveF, typename RejectF > + void add_handlers_(ResolveF&& resolve, RejectF&& reject) { + if ( status_ == status::resolved ) { + invoke_hpp::invoke( + std::forward(resolve)); + } else if ( status_ == status::rejected ) { + invoke_hpp::invoke( + std::forward(reject), + exception_); + } else { + handlers_.emplace_back( + std::forward(resolve), + std::forward(reject)); + } + } + + void invoke_resolve_handlers_() noexcept { + for ( const auto& h : handlers_ ) { + h.resolve_(); + } + handlers_.clear(); + } + + void invoke_reject_handlers_() noexcept { + for ( const auto& h : handlers_ ) { + h.reject_(exception_); + } + handlers_.clear(); + } + private: + status status_ = status::pending; + std::exception_ptr exception_ = nullptr; + + std::mutex mutex_; + + struct handler { + using resolve_t = std::function; + using reject_t = std::function; + + resolve_t resolve_; + reject_t reject_; + + template < typename ResolveF, typename RejectF > + handler(ResolveF&& resolve, RejectF&& reject) + : resolve_(std::forward(resolve)) + , reject_(std::forward(reject)) {} + }; + + std::vector handlers_; + }; + }; + + // + // swap + // + + template < typename T > + void swap(promise& l, promise& r) noexcept { + l.swap(r); + } + + // + // make_promise + // + + template < typename R > + promise make_promise() { + return promise(); + } + + template < typename R, typename F > + promise make_promise(F&& f) { + promise result; + + auto resolver = [result](auto&& v) mutable { + return result.resolve(std::forward(v)); + }; + + auto rejector = [result](auto&& e) mutable { + return result.reject(std::forward(e)); + }; + + try { + invoke_hpp::invoke( + std::forward(f), + std::move(resolver), + std::move(rejector)); + } catch (...) { + result.reject(std::current_exception()); + } + + return result; + } + + // + // make_resolved_promise + // + + inline promise make_resolved_promise() { + promise result; + result.resolve(); + return result; + } + + template < typename R > + promise> make_resolved_promise(R&& v) { + promise> result; + result.resolve(std::forward(v)); + return result; + } + + // + // make_rejected_promise + // + + template < typename E > + promise make_rejected_promise(E&& e) { + promise result; + result.reject(std::forward(e)); + return result; + } + + template < typename R, typename E > + promise make_rejected_promise(E&& e) { + promise result; + result.reject(std::forward(e)); + return result; + } + + // + // make_all_promise + // + + template < typename Iter > + auto make_all_promise(Iter begin, Iter end) { + using child_promise_t = typename Iter::value_type; + using child_promise_value_t = typename child_promise_t::value_type; + using promise_out_container_t = std::vector; + + struct context_t { + promise_out_container_t results; + std::atomic_size_t counter = ATOMIC_VAR_INIT(0); + + context_t(std::size_t count) + : results(count) {} + + bool apply_result(std::size_t index, const child_promise_value_t& value) { + results[index] = value; + return ++counter == results.size(); + } + }; + + if ( begin == end ) { + return make_resolved_promise(promise_out_container_t()); + } + + return make_promise([begin, end](auto&& resolver, auto&& rejector){ + std::size_t result_index = 0; + auto context = std::make_shared(std::distance(begin, end)); + for ( auto iter = begin; iter != end; ++iter, ++result_index ) { + (*iter).then([ + context, + resolver, + result_index + ](const child_promise_value_t& v) mutable { + if ( context->apply_result(result_index, v) ) { + resolver(std::move(context->results)); + } + }, rejector); + } + }); + } + + template < typename Container > + auto make_all_promise(Container&& container) { + return make_all_promise( + std::begin(container), + std::end(container)); + } + + // + // make_any_promise + // + + template < typename Iter > + auto make_any_promise(Iter begin, Iter end) { + using child_promise_t = typename Iter::value_type; + using child_promise_value_t = typename child_promise_t::value_type; + + if ( begin == end ) { + throw std::logic_error("at least one input promise must be provided for make_any_promise"); + } + + return make_promise([begin, end](auto&& resolver, auto&& rejector){ + for ( auto iter = begin; iter != end; ++iter ) { + (*iter).then(resolver, rejector); + } + }); + } + + template < typename Container > + auto make_any_promise(Container&& container) { + return make_any_promise( + std::begin(container), + std::end(container)); + } +} + +namespace std +{ + template < typename T > + struct hash> + : std::unary_function, std::size_t> + { + std::size_t operator()(const promise_hpp::promise& p) const noexcept { + return p.hash(); + } + }; +} diff --git a/headers/enduro2d/base/_base.hpp b/headers/enduro2d/base/_base.hpp index 0bf10c3c..d26276aa 100644 --- a/headers/enduro2d/base/_base.hpp +++ b/headers/enduro2d/base/_base.hpp @@ -43,4 +43,6 @@ #include #include +#include <3rdparty/invoke.hpp/invoke.hpp> +#include <3rdparty/promise.hpp/promise.hpp> #include <3rdparty/variant/variant.hpp> diff --git a/headers/enduro2d/base/stdex.hpp b/headers/enduro2d/base/stdex.hpp index 9bbd6b54..0b2809b0 100644 --- a/headers/enduro2d/base/stdex.hpp +++ b/headers/enduro2d/base/stdex.hpp @@ -8,50 +8,13 @@ #include "macros.hpp" -#define E2D_STDEX_NOEXCEPT_RETURN(...) \ - noexcept(noexcept(__VA_ARGS__)) { return __VA_ARGS__; } - -#define E2D_STDEX_NOEXCEPT_DECLTYPE_RETURN(...) \ - noexcept(noexcept(__VA_ARGS__)) -> decltype (__VA_ARGS__) { return __VA_ARGS__; } - // -// void_t +// variant // namespace e2d { namespace stdex { - namespace impl - { - template < typename... Args > - struct make_void { - using type = void; - }; - } - - template < typename... Args > - using void_t = typename impl::make_void::type; -}} - -// -// is_reference_wrapper -// - -namespace e2d { namespace stdex -{ - namespace impl - { - template < typename T > - struct is_reference_wrapper_impl - : std::false_type {}; - - template < typename U > - struct is_reference_wrapper_impl> - : std::true_type {}; - } - - template < typename T > - struct is_reference_wrapper - : impl::is_reference_wrapper_impl> {}; + using namespace mpark; }} // @@ -60,164 +23,16 @@ namespace e2d { namespace stdex namespace e2d { namespace stdex { - namespace impl - { - // - // invoke_member_object_impl - // - - template - < - typename Base, typename F, typename Derived, - typename std::enable_if_t>::value, int> = 0 - > - constexpr auto invoke_member_object_impl(F Base::* f, Derived&& ref) - E2D_STDEX_NOEXCEPT_DECLTYPE_RETURN( - std::forward(ref).*f) - - template - < - typename Base, typename F, typename RefWrap, - typename std::enable_if_t>::value, int> = 0 - > - constexpr auto invoke_member_object_impl(F Base::* f, RefWrap&& ref) - E2D_STDEX_NOEXCEPT_DECLTYPE_RETURN( - ref.get().*f) - - template - < - typename Base, typename F, typename Pointer, - typename std::enable_if_t< - !std::is_base_of>::value && - !is_reference_wrapper>::value - , int> = 0 - > - constexpr auto invoke_member_object_impl(F Base::* f, Pointer&& ptr) - E2D_STDEX_NOEXCEPT_DECLTYPE_RETURN( - (*std::forward(ptr)).*f) - - // - // invoke_member_function_impl - // - - template - < - typename Base, typename F, typename Derived, typename... Args, - typename std::enable_if_t>::value, int> = 0 - > - constexpr auto invoke_member_function_impl(F Base::* f, Derived&& ref, Args&&... args) - E2D_STDEX_NOEXCEPT_DECLTYPE_RETURN( - (std::forward(ref).*f)(std::forward(args)...)) - - template - < - typename Base, typename F, typename RefWrap, typename... Args, - typename std::enable_if_t>::value, int> = 0 - > - constexpr auto invoke_member_function_impl(F Base::* f, RefWrap&& ref, Args&&... args) - E2D_STDEX_NOEXCEPT_DECLTYPE_RETURN( - (ref.get().*f)(std::forward(args)...)) - - template - < - typename Base, typename F, typename Pointer, typename... Args, - typename std::enable_if_t< - !std::is_base_of>::value && - !is_reference_wrapper>::value - , int> = 0 - > - constexpr auto invoke_member_function_impl(F Base::* f, Pointer&& ptr, Args&&... args) - E2D_STDEX_NOEXCEPT_DECLTYPE_RETURN( - ((*std::forward(ptr)).*f)(std::forward(args)...)) - } - - template - < - typename F, typename... Args, - typename std::enable_if_t>::value, int> = 0 - > - constexpr auto invoke(F&& f, Args&&... args) - E2D_STDEX_NOEXCEPT_DECLTYPE_RETURN( - std::forward(f)(std::forward(args)...)) - - template - < - typename F, typename T, - typename std::enable_if_t>::value, int> = 0 - > - constexpr auto invoke(F&& f, T&& t) - E2D_STDEX_NOEXCEPT_DECLTYPE_RETURN( - impl::invoke_member_object_impl(std::forward(f), std::forward(t))) - - template - < - typename F, typename... Args, - typename std::enable_if_t>::value, int> = 0 - > - constexpr auto invoke(F&& f, Args&&... args) - E2D_STDEX_NOEXCEPT_DECLTYPE_RETURN( - impl::invoke_member_function_impl(std::forward(f), std::forward(args)...)) + using namespace invoke_hpp; }} // -// invoke_result +// promise // namespace e2d { namespace stdex { - namespace impl - { - struct invoke_result_impl_tag {}; - - template < typename Void, typename F, typename... Args > - struct invoke_result_impl {}; - - template < typename F, typename... Args > - struct invoke_result_impl(), std::declval()...))>, F, Args...> { - using type = decltype(stdex::invoke(std::declval(), std::declval()...)); - }; - } - - template < typename F, typename... Args > - struct invoke_result - : impl::invoke_result_impl {}; - - template < typename F, typename... Args > - using invoke_result_t = typename invoke_result::type; -}} - -// -// apply -// - -namespace e2d { namespace stdex -{ - namespace impl - { - template < typename F, typename Tuple, std::size_t... I > - constexpr decltype(auto) apply_impl(F&& f, Tuple&& args, std::index_sequence) - E2D_STDEX_NOEXCEPT_RETURN( - stdex::invoke( - std::forward(f), - std::get(std::forward(args))...)) - } - - template < typename F, typename Tuple > - constexpr decltype(auto) apply(F&& f, Tuple&& args) - E2D_STDEX_NOEXCEPT_RETURN( - impl::apply_impl( - std::forward(f), - std::forward(args), - std::make_index_sequence>::value>())) -}} - -// -// variant -// - -namespace e2d { namespace stdex -{ - using namespace mpark; + using namespace promise_hpp; }} // diff --git a/modules/invoke.hpp b/modules/invoke.hpp new file mode 160000 index 00000000..de0183fe --- /dev/null +++ b/modules/invoke.hpp @@ -0,0 +1 @@ +Subproject commit de0183febef4910d6d550a1a86562705cc779552 diff --git a/modules/promise.hpp b/modules/promise.hpp new file mode 160000 index 00000000..8491a7a2 --- /dev/null +++ b/modules/promise.hpp @@ -0,0 +1 @@ +Subproject commit 8491a7a2efc5496b065a9f5029afae3db58911c0 diff --git a/scripts/update_modules.sh b/scripts/update_modules.sh index 447d556b..3c173695 100755 --- a/scripts/update_modules.sh +++ b/scripts/update_modules.sh @@ -22,6 +22,12 @@ git submodule update --remote --recursive mkdir -p $UNTESTS_DIR/catch cp -fv $MODULES_DIR/catch2/single_include/catch2/catch.hpp $UNTESTS_DIR/catch/catch.hpp +mkdir -p $HEADERS_RDPARTY_DIR/invoke.hpp +cp -rfv $MODULES_DIR/invoke.hpp/invoke.hpp $HEADERS_RDPARTY_DIR/invoke.hpp/invoke.hpp + +mkdir -p $HEADERS_RDPARTY_DIR/promise.hpp +cp -rfv $MODULES_DIR/promise.hpp/promise.hpp $HEADERS_RDPARTY_DIR/promise.hpp/promise.hpp + mkdir -p $SOURCES_RDPARTY_DIR/pugixml cp -rfv $MODULES_DIR/pugixml/src/. $SOURCES_RDPARTY_DIR/pugixml/