feat add just,then,traits.h
This commit is contained in:
parent
069aa09d6d
commit
7fbf260c43
@ -120,7 +120,7 @@ if(SLED_BUILD_TESTS)
|
|||||||
src/exec/just_test.cc
|
src/exec/just_test.cc
|
||||||
src/any_test.cc
|
src/any_test.cc
|
||||||
src/filesystem/path_test.cc
|
src/filesystem/path_test.cc
|
||||||
src/profiling/profiling_test.cc
|
# src/profiling/profiling_test.cc
|
||||||
src/strings/base64_test.cc
|
src/strings/base64_test.cc
|
||||||
src/cleanup_test.cc
|
src/cleanup_test.cc
|
||||||
src/status_or_test.cc
|
src/status_or_test.cc
|
||||||
|
391
include/sled/exec/detail/invoke_result.h
Normal file
391
include/sled/exec/detail/invoke_result.h
Normal file
@ -0,0 +1,391 @@
|
|||||||
|
//! \file eggs/invoke.hpp
|
||||||
|
// Eggs.Invoke
|
||||||
|
//
|
||||||
|
// Copyright Agustin K-ballo Berge, Fusion Fenix 2017-2020
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
#ifndef EGGS_INVOKE_HPP
|
||||||
|
#define EGGS_INVOKE_HPP
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace eggs {
|
||||||
|
namespace detail {
|
||||||
|
#define EGGS_FWD(...) static_cast<decltype(__VA_ARGS__) &&>(__VA_ARGS__)
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
template<typename C, typename T, bool Ref, bool RefWrapper, bool IsFunction = std::is_function<T>::value>
|
||||||
|
struct invoke_mem_ptr;
|
||||||
|
|
||||||
|
// when `pm` is a pointer to member of a class `C` and
|
||||||
|
// `is_base_of_v<C, remove_reference_t<T>>` is `true`;
|
||||||
|
template<typename C, typename T>
|
||||||
|
struct invoke_mem_ptr<C, T, /*Ref=*/true, /*RefWrapper=*/false, /*IsFunction=*/false> {
|
||||||
|
T C::*pm;
|
||||||
|
|
||||||
|
#if !__cpp_aggregate_paren_init
|
||||||
|
constexpr invoke_mem_ptr(T C::*pm) noexcept : pm(pm) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template<typename T1>
|
||||||
|
constexpr auto operator()(T1 &&t1) const noexcept(noexcept(EGGS_FWD(t1).*pm)) -> decltype(EGGS_FWD(t1).*pm)
|
||||||
|
{
|
||||||
|
return EGGS_FWD(t1).*pm;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename C, typename T>
|
||||||
|
struct invoke_mem_ptr<C, T, /*Ref=*/true, /*RefWrapper=*/false, /*IsFunction=*/true> {
|
||||||
|
T C::*pm;
|
||||||
|
|
||||||
|
#if !__cpp_aggregate_paren_init
|
||||||
|
constexpr invoke_mem_ptr(T C::*pm) noexcept : pm(pm) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template<typename T1, typename... Tn>
|
||||||
|
constexpr auto operator()(T1 &&t1, Tn &&...tn) const noexcept(noexcept((EGGS_FWD(t1).*pm)(EGGS_FWD(tn)...)))
|
||||||
|
-> decltype((EGGS_FWD(t1).*pm)(EGGS_FWD(tn)...))
|
||||||
|
{
|
||||||
|
return (EGGS_FWD(t1).*pm)(EGGS_FWD(tn)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// when `pm` is a pointer to member of a class `C` and
|
||||||
|
// `remove_cvref_t<T>` is a specialization of `reference_wrapper`;
|
||||||
|
template<typename C, typename T>
|
||||||
|
struct invoke_mem_ptr<C, T, /*Ref=*/false, /*RefWrapper=*/true, /*IsFunction=*/false> {
|
||||||
|
T C::*pm;
|
||||||
|
|
||||||
|
#if !__cpp_aggregate_paren_init
|
||||||
|
constexpr invoke_mem_ptr(T C::*pm) noexcept : pm(pm) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template<typename T1>
|
||||||
|
constexpr auto operator()(T1 &&t1) const noexcept(noexcept(t1.get().*pm)) -> decltype(t1.get().*pm)
|
||||||
|
{
|
||||||
|
return t1.get().*pm;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename C, typename T>
|
||||||
|
struct invoke_mem_ptr<C, T, /*Ref=*/false, /*RefWrapper=*/true, /*IsFunction=*/true> {
|
||||||
|
T C::*pm;
|
||||||
|
|
||||||
|
#if !__cpp_aggregate_paren_init
|
||||||
|
constexpr invoke_mem_ptr(T C::*pm) noexcept : pm(pm) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template<typename T1, typename... Tn>
|
||||||
|
constexpr auto operator()(T1 &&t1, Tn &&...tn) const noexcept(noexcept((t1.get().*pm)(EGGS_FWD(tn)...)))
|
||||||
|
-> decltype((t1.get().*pm)(EGGS_FWD(tn)...))
|
||||||
|
{
|
||||||
|
return (t1.get().*pm)(EGGS_FWD(tn)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// when `pm` is a pointer to member of a class `C` and `T` does not
|
||||||
|
// satisfy the previous two items;
|
||||||
|
template<typename C, typename T>
|
||||||
|
struct invoke_mem_ptr<C, T, /*Ref=*/false, /*RefWrapper=*/false, /*IsFunction=*/false> {
|
||||||
|
T C::*pm;
|
||||||
|
|
||||||
|
#if !__cpp_aggregate_paren_init
|
||||||
|
constexpr invoke_mem_ptr(T C::*pm) noexcept : pm(pm) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template<typename T1>
|
||||||
|
constexpr auto operator()(T1 &&t1) const noexcept(noexcept((*EGGS_FWD(t1)).*pm)) -> decltype((*EGGS_FWD(t1)).*pm)
|
||||||
|
{
|
||||||
|
return (*EGGS_FWD(t1)).*pm;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename C, typename T>
|
||||||
|
struct invoke_mem_ptr<C, T, /*Ref=*/false, /*RefWrapper=*/false, /*IsFunction=*/true> {
|
||||||
|
T C::*pm;
|
||||||
|
|
||||||
|
#if !__cpp_aggregate_paren_init
|
||||||
|
constexpr invoke_mem_ptr(T C::*pm) noexcept : pm(pm) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template<typename T1, typename... Tn>
|
||||||
|
constexpr auto operator()(T1 &&t1, Tn &&...tn) const noexcept(noexcept(((*EGGS_FWD(t1)).*pm)(EGGS_FWD(tn)...)))
|
||||||
|
-> decltype(((*EGGS_FWD(t1)).*pm)(EGGS_FWD(tn)...))
|
||||||
|
{
|
||||||
|
return ((*EGGS_FWD(t1)).*pm)(EGGS_FWD(tn)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
template<typename F>
|
||||||
|
auto invoke(F &&, ...) -> F &&;
|
||||||
|
|
||||||
|
template<typename T, typename C, typename T1>
|
||||||
|
auto invoke(T C::*, T1 const &, ...) -> invoke_mem_ptr<C,
|
||||||
|
T,
|
||||||
|
/*Ref=*/std::is_base_of<C, T1>::value,
|
||||||
|
/*RefWrapper=*/false>;
|
||||||
|
|
||||||
|
template<typename T, typename C, typename X>
|
||||||
|
auto invoke(T C::*, std::reference_wrapper<X>, ...) -> invoke_mem_ptr<C,
|
||||||
|
T,
|
||||||
|
/*Ref=*/false,
|
||||||
|
/*RefWrapper=*/true>;
|
||||||
|
|
||||||
|
//! EGGS_INVOKE(F, ...)
|
||||||
|
//!
|
||||||
|
//! - _Returns_: `INVOKE(F __VA_OPT__(,) __VA_ARGS__)`.
|
||||||
|
#if __cplusplus > 201703L// C++20: P0306
|
||||||
|
#define EGGS_INVOKE(F, ...) \
|
||||||
|
(static_cast<decltype(::eggs::detail::invoke(F __VA_OPT__(, ) __VA_ARGS__))>(F)(__VA_ARGS__))
|
||||||
|
#elif _MSVC_TRADITIONAL
|
||||||
|
#define EGGS_INVOKE(F, ...) (static_cast<decltype(::eggs::detail::invoke(F, __VA_ARGS__))>(F)(__VA_ARGS__))
|
||||||
|
#else
|
||||||
|
#define EGGS_INVOKE(F, ...) (static_cast<decltype(::eggs::detail::invoke(F, ##__VA_ARGS__))>(F)(__VA_ARGS__))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// `INVOKE(f, t1, t2, ..., tN)` implicitly converted to `R`.
|
||||||
|
template<typename R, typename RD = typename std::remove_cv<R>::type>
|
||||||
|
struct invoke_r {
|
||||||
|
private:
|
||||||
|
static R conversion(R) noexcept;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<typename F, typename... Args>
|
||||||
|
static constexpr auto
|
||||||
|
call(F &&f, Args &&...args) noexcept(noexcept(conversion(EGGS_INVOKE(EGGS_FWD(f), EGGS_FWD(args)...))))
|
||||||
|
-> decltype(conversion(EGGS_INVOKE(EGGS_FWD(f), EGGS_FWD(args)...)))
|
||||||
|
{
|
||||||
|
return EGGS_INVOKE(EGGS_FWD(f), EGGS_FWD(args)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// `static_cast<void>(INVOKE(f, t1, t2, ..., tN))` if `R` is _cv_ `void`.
|
||||||
|
template<typename R>
|
||||||
|
struct invoke_r<R, void> {
|
||||||
|
template<typename F, typename... Args>
|
||||||
|
static constexpr auto call(F &&f, Args &&...args) noexcept(noexcept(EGGS_INVOKE(EGGS_FWD(f), EGGS_FWD(args)...)))
|
||||||
|
-> decltype(static_cast<void>(EGGS_INVOKE(EGGS_FWD(f), EGGS_FWD(args)...)))
|
||||||
|
{
|
||||||
|
return static_cast<void>(EGGS_INVOKE(EGGS_FWD(f), EGGS_FWD(args)...));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! EGGS_INVOKE(R, F, ...)
|
||||||
|
//!
|
||||||
|
//! - _Returns_: `INVOKE<R>(F __VA_OPT__(,) __VA_ARGS__)`.
|
||||||
|
#define EGGS_INVOKE_R(R, ...) (::eggs::detail::invoke_r<R>::call(__VA_ARGS__))
|
||||||
|
|
||||||
|
}// namespace detail
|
||||||
|
}// namespace eggs
|
||||||
|
|
||||||
|
namespace eggs {
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
namespace detail {
|
||||||
|
template<typename T, typename Enable = void>
|
||||||
|
struct invoke_result_impl {};
|
||||||
|
|
||||||
|
template<typename F, typename... Ts>
|
||||||
|
struct invoke_result_impl<F(Ts...), decltype((void) EGGS_INVOKE(std::declval<F>(), std::declval<Ts>()...))> {
|
||||||
|
using type = decltype(EGGS_INVOKE(std::declval<F>(), std::declval<Ts>()...));
|
||||||
|
};
|
||||||
|
}// namespace detail
|
||||||
|
|
||||||
|
//! template <class Fn, class... ArgTypes> struct invoke_result;
|
||||||
|
//!
|
||||||
|
//! - _Comments_: If the expression `INVOKE(std::declval<Fn>(),
|
||||||
|
//! std::declval<ArgTypes>()...)` is well-formed when treated as an
|
||||||
|
//! unevaluated operand, the member typedef `type` names the type
|
||||||
|
//! `decltype(INVOKE(std::declval<Fn>(), std::declval<ArgTypes>()...))`;
|
||||||
|
//! otherwise, there shall be no member `type`. Access checking is
|
||||||
|
//! performed as if in a context unrelated to `Fn` and `ArgTypes`. Only
|
||||||
|
//! the validity of the immediate context of the expression is considered.
|
||||||
|
//!
|
||||||
|
//! - _Preconditions_: `Fn` and all types in the template parameter pack
|
||||||
|
//! `ArgTypes` are complete types, _cv_ `void`, or arrays of unknown
|
||||||
|
//! bound.
|
||||||
|
template<typename Fn, typename... ArgTypes>
|
||||||
|
struct invoke_result : detail::invoke_result_impl<Fn && (ArgTypes && ...)> {};
|
||||||
|
|
||||||
|
//! template <class Fn, class... ArgTypes>
|
||||||
|
//! using invoke_result_t = typename invoke_result<Fn, ArgTypes...>::type;
|
||||||
|
template<typename Fn, typename... ArgTypes>
|
||||||
|
using invoke_result_t = typename invoke_result<Fn, ArgTypes...>::type;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
namespace detail {
|
||||||
|
template<typename T, typename Enable = void>
|
||||||
|
struct is_invocable_impl : std::false_type {};
|
||||||
|
|
||||||
|
template<typename F, typename... Ts>
|
||||||
|
struct is_invocable_impl<F(Ts...), decltype((void) EGGS_INVOKE(std::declval<F>(), std::declval<Ts>()...))>
|
||||||
|
: std::true_type {};
|
||||||
|
}// namespace detail
|
||||||
|
|
||||||
|
//! template <class Fn, class... ArgTypes> struct is_invocable;
|
||||||
|
//!
|
||||||
|
//! - _Condition_: The expression `INVOKE(std::declval<Fn>(),
|
||||||
|
//! std::declval<ArgTypes>()...)` is well-formed when treated as an
|
||||||
|
//! unevaluated operand.
|
||||||
|
//!
|
||||||
|
//! - _Comments_: `Fn` and all types in the template parameter pack
|
||||||
|
//! `ArgTypes` shall be complete types, _cv_ `void`, or arrays of
|
||||||
|
//! unknown bound.
|
||||||
|
template<typename Fn, typename... ArgTypes>
|
||||||
|
struct is_invocable : detail::is_invocable_impl<Fn && (ArgTypes && ...)>::type {};
|
||||||
|
|
||||||
|
#if __cpp_variable_templates
|
||||||
|
//! template <class Fn, class... ArgTypes> // (C++14)
|
||||||
|
//! inline constexpr bool is_invocable_v =
|
||||||
|
//! eggs::is_invocable<Fn, ArgTypes...>::value;
|
||||||
|
template<typename Fn, typename... ArgTypes>
|
||||||
|
#if __cpp_inline_variables
|
||||||
|
inline
|
||||||
|
#endif
|
||||||
|
constexpr bool is_invocable_v = is_invocable<Fn, ArgTypes...>::value;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
namespace detail {
|
||||||
|
template<typename T, typename R, typename Enable = void>
|
||||||
|
struct is_invocable_r_impl : std::false_type {};
|
||||||
|
|
||||||
|
template<typename F, typename... Ts, typename R>
|
||||||
|
struct is_invocable_r_impl<F(Ts...), R, decltype((void) EGGS_INVOKE_R(R, std::declval<F>(), std::declval<Ts>()...))>
|
||||||
|
: std::true_type {};
|
||||||
|
}// namespace detail
|
||||||
|
|
||||||
|
//! template <class R, class Fn, class... ArgTypes> struct is_invocable_r;
|
||||||
|
//!
|
||||||
|
//! - _Condition_: The expression `INVOKE<R>(std::declval<Fn>(),
|
||||||
|
//! std::declval<ArgTypes>()...)` is well-formed when treated as an
|
||||||
|
//! unevaluated operand.
|
||||||
|
//!
|
||||||
|
//! - _Comments_: `Fn`, `R`, and all types in the template parameter pack
|
||||||
|
//! `ArgTypes` shall be complete types, _cv_ `void`, or arrays of
|
||||||
|
//! unknown bound.
|
||||||
|
template<typename R, typename Fn, typename... ArgTypes>
|
||||||
|
struct is_invocable_r : detail::is_invocable_r_impl<Fn && (ArgTypes && ...), R>::type {};
|
||||||
|
|
||||||
|
#if __cpp_variable_templates
|
||||||
|
//! template <class R, class Fn, class... ArgTypes> // (C++14)
|
||||||
|
//! inline constexpr bool is_invocable_r_v =
|
||||||
|
//! eggs::is_invocable_r<R, Fn, ArgTypes...>::value;
|
||||||
|
template<typename R, typename Fn, typename... ArgTypes>
|
||||||
|
#if __cpp_inline_variables
|
||||||
|
inline
|
||||||
|
#endif
|
||||||
|
constexpr bool is_invocable_r_v = is_invocable_r<R, Fn, ArgTypes...>::value;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
namespace detail {
|
||||||
|
template<typename T, typename Enable = void>
|
||||||
|
struct is_nothrow_invocable_impl : std::false_type {};
|
||||||
|
|
||||||
|
template<typename F, typename... Ts>
|
||||||
|
struct is_nothrow_invocable_impl<F(Ts...), decltype((void) EGGS_INVOKE(std::declval<F>(), std::declval<Ts>()...))>
|
||||||
|
: std::integral_constant<bool, noexcept(EGGS_INVOKE(std::declval<F>(), std::declval<Ts>()...))> {};
|
||||||
|
}// namespace detail
|
||||||
|
|
||||||
|
//! template <class Fn, class... ArgTypes> struct is_nothrow_invocable;
|
||||||
|
//!
|
||||||
|
//! - _Condition_: `eggs::is_invocable_v<Fn, ArgTypes...>` is `true` and
|
||||||
|
//! the expression `INVOKE(std::declval<Fn>(), std::declval<ArgTypes>()...)`
|
||||||
|
//! is known not to throw any exceptions.
|
||||||
|
//!
|
||||||
|
//! - _Comments_: `Fn` and all types in the template parameter pack
|
||||||
|
//! `ArgTypes` shall be complete types, _cv_ `void`, or arrays of
|
||||||
|
//! unknown bound.
|
||||||
|
template<typename Fn, typename... ArgTypes>
|
||||||
|
struct is_nothrow_invocable : detail::is_nothrow_invocable_impl<Fn && (ArgTypes && ...)>::type {};
|
||||||
|
|
||||||
|
#if __cpp_variable_templates
|
||||||
|
//! template <class Fn, class... ArgTypes> // (C++14)
|
||||||
|
//! inline constexpr bool is_nothrow_invocable_v =
|
||||||
|
//! eggs::is_nothrow_invocable<Fn, ArgTypes...>::value;
|
||||||
|
template<typename Fn, typename... ArgTypes>
|
||||||
|
#if __cpp_inline_variables
|
||||||
|
inline
|
||||||
|
#endif
|
||||||
|
constexpr bool is_nothrow_invocable_v = is_nothrow_invocable<Fn, ArgTypes...>::value;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
namespace detail {
|
||||||
|
template<typename T, typename R, typename Enable = void>
|
||||||
|
struct is_nothrow_invocable_r_impl : std::false_type {};
|
||||||
|
|
||||||
|
template<typename F, typename... Ts, typename R>
|
||||||
|
struct is_nothrow_invocable_r_impl<F(Ts...),
|
||||||
|
R,
|
||||||
|
decltype((void) EGGS_INVOKE_R(R, std::declval<F>(), std::declval<Ts>()...))>
|
||||||
|
: std::integral_constant<bool, noexcept(EGGS_INVOKE_R(R, std::declval<F>(), std::declval<Ts>()...))> {};
|
||||||
|
}// namespace detail
|
||||||
|
|
||||||
|
//! template <class R, class Fn, class... ArgTypes> struct is_nothrow_invocable_r;
|
||||||
|
//!
|
||||||
|
//! - _Condition_: `eggs::is_invocable_r_v<R, Fn, ArgTypes...>` is `true`
|
||||||
|
//! and the expression `INVOKE(std::declval<Fn>(), std::declval<ArgTypes>()...)`
|
||||||
|
//! is known not to throw any exceptions.
|
||||||
|
//!
|
||||||
|
//! - _Comments_: `Fn`, `R`, and all types in the template parameter pack
|
||||||
|
//! `ArgTypes` shall be complete types, _cv_ `void`, or arrays of
|
||||||
|
//! unknown bound.
|
||||||
|
template<typename R, typename Fn, typename... ArgTypes>
|
||||||
|
struct is_nothrow_invocable_r : detail::is_nothrow_invocable_r_impl<Fn && (ArgTypes && ...), R>::type {};
|
||||||
|
|
||||||
|
#if __cpp_variable_templates
|
||||||
|
//! template <class R, class Fn, class... ArgTypes> // (C++14)
|
||||||
|
//! inline constexpr bool is_nothrow_invocable_r_v =
|
||||||
|
//! eggs::is_nothrow_invocable_r<R, Fn, ArgTypes...>::value;
|
||||||
|
template<typename R, typename Fn, typename... ArgTypes>
|
||||||
|
#if __cpp_inline_variables
|
||||||
|
inline
|
||||||
|
#endif
|
||||||
|
constexpr bool is_nothrow_invocable_r_v = is_nothrow_invocable_r<R, Fn, ArgTypes...>::value;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
//! template <class F, class... Args>
|
||||||
|
//! constexpr eggs::invoke_result_t<F, Args...> invoke(F&& f, Args&&... args)
|
||||||
|
//! noexcept(eggs::is_nothrow_invocable_v<F, Args...>);
|
||||||
|
//!
|
||||||
|
//! - _Returns_: `INVOKE(std::forward<F>(f), std::forward<Args>(args)...)`.
|
||||||
|
//!
|
||||||
|
//! - _Remarks_: This function shall not participate in overload resolution
|
||||||
|
//! unless `eggs::is_invocable_v<F, Args...>` is `true`.
|
||||||
|
template<typename Fn, typename... ArgTypes>
|
||||||
|
constexpr auto
|
||||||
|
invoke(Fn &&f, ArgTypes &&...args) noexcept(noexcept(EGGS_INVOKE(EGGS_FWD(f), EGGS_FWD(args)...)))
|
||||||
|
-> decltype(EGGS_INVOKE(EGGS_FWD(f), EGGS_FWD(args)...))
|
||||||
|
{
|
||||||
|
return EGGS_INVOKE(EGGS_FWD(f), EGGS_FWD(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
//! template <class R, class F, class... Args> // (extension)
|
||||||
|
//! constexpr R eggs::invoke_r(F&& f, Args&&... args)
|
||||||
|
//! noexcept(eggs::is_nothrow_invocable_r_v<R, F, Args...>);
|
||||||
|
//!
|
||||||
|
//! - _Returns_: `INVOKE<R>(std::forward<F>(f), std::forward<Args>(args)...)`.
|
||||||
|
//!
|
||||||
|
//! - _Remarks_: This function shall not participate in overload resolution
|
||||||
|
//! unless `eggs::is_invocable_r_v<R, F, Args...>` is `true`.
|
||||||
|
template<typename R, typename Fn, typename... ArgTypes>
|
||||||
|
constexpr auto
|
||||||
|
invoke_r(Fn &&f, ArgTypes &&...args) noexcept(noexcept(EGGS_INVOKE_R(R, EGGS_FWD(f), EGGS_FWD(args)...)))
|
||||||
|
-> decltype(EGGS_INVOKE_R(R, EGGS_FWD(f), EGGS_FWD(args)...))
|
||||||
|
{
|
||||||
|
return EGGS_INVOKE_R(R, EGGS_FWD(f), EGGS_FWD(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef EGGS_FWD
|
||||||
|
}// namespace eggs
|
||||||
|
|
||||||
|
#endif /*EGGS_INVOKE_HPP*/
|
39
include/sled/exec/detail/just.h
Normal file
39
include/sled/exec/detail/just.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#ifndef SLED_EXEC_DETAIL_JUST_H
|
||||||
|
#define SLED_EXEC_DETAIL_JUST_H
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace sled {
|
||||||
|
|
||||||
|
template<typename TReceiver, typename T>
|
||||||
|
struct JustOperation {
|
||||||
|
TReceiver receiver;
|
||||||
|
T value;
|
||||||
|
|
||||||
|
void Start() { receiver.SetValue(std::move(value)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct JustSender {
|
||||||
|
using result_t = T;
|
||||||
|
T value;
|
||||||
|
|
||||||
|
template<typename TReceiver>
|
||||||
|
JustOperation<TReceiver, T> Connect(TReceiver &&receiver)
|
||||||
|
{
|
||||||
|
return {std::forward<TReceiver>(receiver), std::forward<T>(value)};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
JustSender<T>
|
||||||
|
Just(T &&t)
|
||||||
|
{
|
||||||
|
return {std::forward<T>(t)};
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace sled
|
||||||
|
|
||||||
|
#endif// SLED_EXEC_DETAIL_JUST_H
|
68
include/sled/exec/detail/sync_wait.h
Normal file
68
include/sled/exec/detail/sync_wait.h
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#ifndef SLED_EXEC_DETAIL_SYNC_WAIT_H
|
||||||
|
#define SLED_EXEC_DETAIL_SYNC_WAIT_H
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "sled/optional.h"
|
||||||
|
#include "sled/synchronization/mutex.h"
|
||||||
|
#include "traits.h"
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
namespace sled {
|
||||||
|
struct SyncWaitData {
|
||||||
|
sled::Mutex lock;
|
||||||
|
sled::ConditionVariable cv;
|
||||||
|
std::exception_ptr err;
|
||||||
|
bool done = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct SyncWaitReceiver {
|
||||||
|
SyncWaitData &data;
|
||||||
|
sled::optional<T> &value;
|
||||||
|
|
||||||
|
void SetValue(T &&val)
|
||||||
|
{
|
||||||
|
sled::MutexLock lock(&data.lock);
|
||||||
|
value.emplace(val);
|
||||||
|
data.done = true;
|
||||||
|
data.cv.NotifyOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetError(std::exception_ptr err)
|
||||||
|
{
|
||||||
|
sled::MutexLock lock(&data.lock);
|
||||||
|
data.err = err;
|
||||||
|
data.done = true;
|
||||||
|
data.cv.NotifyOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetStopped(std::exception_ptr err)
|
||||||
|
{
|
||||||
|
sled::MutexLock lock(&data.lock);
|
||||||
|
data.done = true;
|
||||||
|
data.cv.NotifyOne();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename TSender>
|
||||||
|
sled::optional<SenderResultT<TSender>>
|
||||||
|
SyncWait(TSender sender)
|
||||||
|
{
|
||||||
|
using T = SenderResultT<TSender>;
|
||||||
|
SyncWaitData data;
|
||||||
|
sled::optional<T> value;
|
||||||
|
|
||||||
|
auto op = sender.Connect(SyncWaitReceiver<T>{data, value});
|
||||||
|
op.Start();
|
||||||
|
|
||||||
|
sled::MutexLock lock(&data.lock);
|
||||||
|
data.cv.Wait(lock, [&data] { return data.done; });
|
||||||
|
|
||||||
|
if (data.err) { std::rethrow_exception(data.err); }
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace sled
|
||||||
|
|
||||||
|
#endif// SLED_EXEC_DETAIL_SYNC_WAIT_H
|
54
include/sled/exec/detail/then.h
Normal file
54
include/sled/exec/detail/then.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#ifndef SLED_EXEC_DETAIL_THEN_H
|
||||||
|
#define SLED_EXEC_DETAIL_THEN_H
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "traits.h"
|
||||||
|
#include <exception>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace sled {
|
||||||
|
template<typename TReceiver, typename F>
|
||||||
|
struct ThenReceiver {
|
||||||
|
TReceiver receiver;
|
||||||
|
F func;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void SetValue(T &&value)
|
||||||
|
{
|
||||||
|
receiver.SetValue(func(std::forward<T>(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetError(std::exception_ptr err) { receiver.SetError(err); }
|
||||||
|
|
||||||
|
void SetStopped() { receiver.SetStopped(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename TSender, typename TReceiver, typename F>
|
||||||
|
struct ThenOperation {
|
||||||
|
ConnectResultT<TSender, ThenReceiver<TReceiver, F>> op;
|
||||||
|
|
||||||
|
void Start() { op.Start(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename TSender, typename F>
|
||||||
|
struct ThenSender {
|
||||||
|
using result_t = typename eggs::invoke_result_t<F, SenderResultT<TSender>>;
|
||||||
|
TSender sender;
|
||||||
|
F func;
|
||||||
|
|
||||||
|
template<typename TReceiver>
|
||||||
|
ThenOperation<TSender, TReceiver, F> Connect(TReceiver &&receiver)
|
||||||
|
{
|
||||||
|
return {sender.Connect(ThenReceiver<TReceiver, F>{std::forward<TReceiver>(receiver), func})};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename TSender, typename F>
|
||||||
|
ThenSender<TSender, F>
|
||||||
|
Then(TSender sender, F &&func)
|
||||||
|
{
|
||||||
|
return {std::forward<TSender>(sender), std::forward<F>(func)};
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace sled
|
||||||
|
#endif// SLED_EXEC_DETAIL_THEN_H
|
16
include/sled/exec/detail/traits.h
Normal file
16
include/sled/exec/detail/traits.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef SLED_EXEC_DETAIL_TRAITS_H
|
||||||
|
#define SLED_EXEC_DETAIL_TRAITS_H
|
||||||
|
#pragma once
|
||||||
|
#include "invoke_result.h"
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace sled {
|
||||||
|
template<typename TSender, typename TReceiver>
|
||||||
|
using ConnectResultT = decltype(std::declval<TSender>().Connect(std::declval<TReceiver>()));
|
||||||
|
|
||||||
|
template<typename TSender>
|
||||||
|
using SenderResultT = typename TSender::result_t;
|
||||||
|
|
||||||
|
}// namespace sled
|
||||||
|
|
||||||
|
#endif// SLED_EXEC_DETAIL_TRAITS_H
|
10
include/sled/exec/exec.h
Normal file
10
include/sled/exec/exec.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#ifndef SLED_EXEC_EXEC_H
|
||||||
|
#define SLED_EXEC_EXEC_H
|
||||||
|
#pragma once
|
||||||
|
#include "detail/just.h"
|
||||||
|
#include "detail/sync_wait.h"
|
||||||
|
#include "detail/then.h"
|
||||||
|
|
||||||
|
namespace sled {}
|
||||||
|
|
||||||
|
#endif// SLED_EXEC_EXEC_H
|
@ -1,68 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#ifndef SLED_EXEC_JUST_H
|
|
||||||
#define SLED_EXEC_JUST_H
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace sled {
|
|
||||||
|
|
||||||
struct immovable {
|
|
||||||
immovable() = default;
|
|
||||||
immovable(immovable &&) = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename S, typename R>
|
|
||||||
using connect_result_t = decltype(connect(std::declval<S>(), std::declval<R>()));
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
using sender_result_t = typename T::result_t;
|
|
||||||
|
|
||||||
template<typename R, typename T>
|
|
||||||
struct just_operation : immovable {
|
|
||||||
R receiver;
|
|
||||||
T value;
|
|
||||||
|
|
||||||
friend void start(just_operation &self) { set_value(self.receiver, self.value); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct just_sender {
|
|
||||||
using result_t = T;
|
|
||||||
T value;
|
|
||||||
|
|
||||||
template<typename R>
|
|
||||||
just_operation<R, T> connect(R receiver)
|
|
||||||
{
|
|
||||||
return {{}, receiver, this->value};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
just_sender<T>
|
|
||||||
just(T t)
|
|
||||||
{
|
|
||||||
return {t};
|
|
||||||
}
|
|
||||||
|
|
||||||
struct cout_receiver {
|
|
||||||
template<typename T>
|
|
||||||
friend void set_value(cout_receiver self, T &&val)
|
|
||||||
{
|
|
||||||
std::cout << "Result: " << val << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend void set_error(cout_receiver self, std::exception_ptr e)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
std::rethrow_exception(e);
|
|
||||||
} catch (const std::exception &e) {
|
|
||||||
std::cout << "Error: " << e.what() << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
friend void set_stopped(cout_receiver self) { std::cout << "Stopped" << std::endl; }
|
|
||||||
};
|
|
||||||
|
|
||||||
}// namespace sled
|
|
||||||
#endif// SLED_EXEC_JUST_H
|
|
@ -59,9 +59,9 @@ private:
|
|||||||
bool WaitSelect(int64_t cusWait, bool process_io);
|
bool WaitSelect(int64_t cusWait, bool process_io);
|
||||||
|
|
||||||
uint64_t next_dispatcher_key_ = 0;
|
uint64_t next_dispatcher_key_ = 0;
|
||||||
std::unordered_map<uint64_t, Dispatcher *> dispatcher_by_key_;
|
std::unordered_map<uint64_t, Dispatcher *> dispatcher_by_key_ GUARDED_BY(lock_);
|
||||||
std::unordered_map<Dispatcher *, uint64_t> key_by_dispatcher_;
|
std::unordered_map<Dispatcher *, uint64_t> key_by_dispatcher_ GUARDED_BY(lock_);
|
||||||
std::vector<uint64_t> current_dispatcher_keys_;
|
std::vector<uint64_t> current_dispatcher_keys_ GUARDED_BY(lock_);
|
||||||
Signaler *signal_wakeup_;
|
Signaler *signal_wakeup_;
|
||||||
// Mutex lock_;
|
// Mutex lock_;
|
||||||
RecursiveMutex lock_;
|
RecursiveMutex lock_;
|
||||||
@ -91,10 +91,7 @@ public:
|
|||||||
int Send(const void *pv, size_t cb) override;
|
int Send(const void *pv, size_t cb) override;
|
||||||
int SendTo(const void *pv, size_t cb, const SocketAddress &addr) override;
|
int SendTo(const void *pv, size_t cb, const SocketAddress &addr) override;
|
||||||
int Recv(void *pv, size_t cb, int64_t *timestamp) override;
|
int Recv(void *pv, size_t cb, int64_t *timestamp) override;
|
||||||
int RecvFrom(void *pv,
|
int RecvFrom(void *pv, size_t cb, SocketAddress *paddr, int64_t *timestamp) override;
|
||||||
size_t cb,
|
|
||||||
SocketAddress *paddr,
|
|
||||||
int64_t *timestamp) override;
|
|
||||||
int Listen(int backlog) override;
|
int Listen(int backlog) override;
|
||||||
Socket *Accept(SocketAddress *paddr) override;
|
Socket *Accept(SocketAddress *paddr) override;
|
||||||
|
|
||||||
@ -108,16 +105,9 @@ protected:
|
|||||||
int DoConnect(const SocketAddress &addr);
|
int DoConnect(const SocketAddress &addr);
|
||||||
virtual SOCKET DoAccept(SOCKET socket, sockaddr *addr, socklen_t *addrlen);
|
virtual SOCKET DoAccept(SOCKET socket, sockaddr *addr, socklen_t *addrlen);
|
||||||
virtual int DoSend(SOCKET socket, const char *buf, int len, int flags);
|
virtual int DoSend(SOCKET socket, const char *buf, int len, int flags);
|
||||||
virtual int DoSendTo(SOCKET socket,
|
virtual int
|
||||||
const char *buf,
|
DoSendTo(SOCKET socket, const char *buf, int len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
|
||||||
int len,
|
int DoReadFromSocket(void *buffer, size_t length, SocketAddress *out_addr, int64_t *timestamp);
|
||||||
int flags,
|
|
||||||
const struct sockaddr *dest_addr,
|
|
||||||
socklen_t addrlen);
|
|
||||||
int DoReadFromSocket(void *buffer,
|
|
||||||
size_t length,
|
|
||||||
SocketAddress *out_addr,
|
|
||||||
int64_t *timestamp);
|
|
||||||
|
|
||||||
void OnResolveResult(AsyncResolverInterface *resolver);
|
void OnResolveResult(AsyncResolverInterface *resolver);
|
||||||
void UpdateLastError();
|
void UpdateLastError();
|
||||||
@ -134,7 +124,7 @@ protected:
|
|||||||
bool udp_;
|
bool udp_;
|
||||||
int family_ = 0;
|
int family_ = 0;
|
||||||
mutable Mutex mutex_;
|
mutable Mutex mutex_;
|
||||||
int error_;
|
int error_ GUARDED_BY(mutex_);
|
||||||
ConnState state_;
|
ConnState state_;
|
||||||
AsyncResolver *resolver_;
|
AsyncResolver *resolver_;
|
||||||
|
|
||||||
|
@ -78,10 +78,7 @@ public:
|
|||||||
LockGuard(const LockGuard &) = delete;
|
LockGuard(const LockGuard &) = delete;
|
||||||
LockGuard &operator=(const LockGuard &) = delete;
|
LockGuard &operator=(const LockGuard &) = delete;
|
||||||
|
|
||||||
explicit LockGuard(TLock *lock) EXCLUSIVE_LOCK_FUNCTION() : mutex_(lock)
|
explicit LockGuard(TLock *lock) EXCLUSIVE_LOCK_FUNCTION() : mutex_(lock) { mutex_->Lock(); };
|
||||||
{
|
|
||||||
mutex_->Lock();
|
|
||||||
};
|
|
||||||
|
|
||||||
~LockGuard() UNLOCK_FUNCTION() { mutex_->Unlock(); };
|
~LockGuard() UNLOCK_FUNCTION() { mutex_->Unlock(); };
|
||||||
|
|
||||||
@ -150,7 +147,7 @@ public:
|
|||||||
template<typename Predicate>
|
template<typename Predicate>
|
||||||
inline void Wait(MutexLock &lock, Predicate &&pred)
|
inline void Wait(MutexLock &lock, Predicate &&pred)
|
||||||
{
|
{
|
||||||
cv_.wait(lock, std::forward<Predicate>(pred));
|
cv_.wait(lock.lock_, std::forward<Predicate>(pred));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Predicate>
|
template<typename Predicate>
|
||||||
|
@ -1,9 +1,40 @@
|
|||||||
#include "sled/ref_count.h"
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <sled/exec/just.h>
|
#include <sled/exec/exec.h>
|
||||||
|
|
||||||
TEST(just, basic)
|
struct cout_receiver {
|
||||||
|
template<typename T>
|
||||||
|
void SetValue(T &&val)
|
||||||
|
{
|
||||||
|
// 这个receiver什么都不干,只对收集到的结果输出
|
||||||
|
std::cout << "Result: " << val << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetError(std::exception_ptr err) { std::terminate(); }
|
||||||
|
|
||||||
|
void SetStopped() { std::terminate(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(Just, basic)
|
||||||
{
|
{
|
||||||
sled::just_sender<int> sender = sled::just(1);
|
sled::Just(42).Connect(cout_receiver{}).Start();
|
||||||
auto op = connect(sender, sled::cout_receiver{});
|
sled::Just(11).Connect(cout_receiver{}).Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Then, basic)
|
||||||
|
{
|
||||||
|
auto s1 = sled::Just(42);
|
||||||
|
auto s2 = sled::Then(s1, [](int x) { return x + 1; });
|
||||||
|
auto s3 = sled::Then(s2, [](int x) { return x + 1; });
|
||||||
|
auto s4 = sled::Then(s3, [](int x) { return x + 1; });
|
||||||
|
s4.Connect(cout_receiver{}).Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SyncWait, basic)
|
||||||
|
{
|
||||||
|
auto s1 = sled::Just(42);
|
||||||
|
auto s2 = sled::Then(s1, [](int x) { return x + 1; });
|
||||||
|
auto s3 = sled::Then(s2, [](int x) { return x + 1; });
|
||||||
|
auto s4 = sled::Then(s3, [](int x) { return x + 1; });
|
||||||
|
auto s5 = sled::SyncWait(s4).value();
|
||||||
|
std::cout << "Result: " << s5 << '\n';
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user