Created
November 18, 2022 20:23
-
-
Save ericniebler/8e07f57a50d4de989d26bf15159a4730 to your computer and use it in GitHub Desktop.
An example of how arbitrarily-named customization points can be generically forwarded
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
#if defined(__has_feature) | |
#if __has_feature(__cpp_static_call_operator) | |
#define STATIC_CALL_OPERATOR(...) static __VA_ARGS__ operator() STATIC_CALL_OPERATOR_ | |
#define STATIC_CALL_OPERATOR_(...) (__VA_ARGS__) | |
#endif | |
#endif | |
#ifndef STATIC_CALL_OPERATOR | |
#define STATIC_CALL_OPERATOR(...) __VA_ARGS__ operator() STATIC_CALL_OPERATOR_ | |
#define STATIC_CALL_OPERATOR_(...) (__VA_ARGS__) const | |
#endif | |
template <class A, class B> | |
inline constexpr bool __same_as = false; | |
template <class A> | |
inline constexpr bool __same_as<A, A> = true; | |
template <class _T> | |
inline constexpr auto __v = _T::value; | |
template <class _From, class _To> | |
struct __copy_cvref { | |
using type = _To; | |
}; | |
template <class _From, class _To> | |
struct __copy_cvref<_From&, _To> { | |
using type = _To&; | |
}; | |
template <class _From, class _To> | |
struct __copy_cvref<_From const&, _To> { | |
using type = _To const&; | |
}; | |
template <class _From, class _To> | |
struct __copy_cvref<_From&&, _To> { | |
using type = _To&&; | |
}; | |
template <class _From, class _To> | |
struct __copy_cvref<_From const&&, _To> { | |
using type = _To const&&; | |
}; | |
template <class _From, class _To> | |
using __copy_cvref_t = typename __copy_cvref<_From, _To>::type; | |
template <class _Ty> | |
_Ty&& __declval() noexcept; | |
template <class _Ty> | |
_Ty __decay_fn(const _Ty&) noexcept; | |
template <class _Ty> | |
using __decay_t = decltype(__decay_fn(__declval<_Ty>())); | |
template <class _Fn, class... _As> | |
using __call_result_t = decltype(__declval<_Fn>()(__declval<_As>()...)); | |
template <class _Ty> | |
using __t = typename _Ty::type; | |
template <class _Ty> | |
struct _X { | |
using type = struct _ { | |
using type = _Ty; | |
}; | |
}; | |
template <class _Ty> | |
using __x = __t<_X<_Ty>>; | |
template <class...> | |
concept __always_true = true; | |
template <bool> | |
struct __i { | |
template <template <class...> class _Fn, class... _Args> | |
using __g = _Fn<_Args...>; | |
}; | |
template <template <class...> class _Fn, class... _Args> | |
using __meval = | |
typename __i<__always_true<_Args...>>:: | |
template __g<_Fn, _Args...>; | |
template <class _Fn, class... _Args> | |
using __minvoke = | |
__meval<_Fn::template __f, _Args...>; | |
template <class _Fn, class... _Args> | |
concept __minvocable = | |
requires { | |
typename __minvoke<_Fn, _Args...>; | |
}; | |
template <template <class...> class _Fn> | |
struct __q { | |
template <class... _Args> | |
using __f = __meval<_Fn, _Args...>; | |
}; | |
template <bool> | |
struct __if_ { | |
template <class _True, class...> | |
using __f = _True; | |
}; | |
template <> | |
struct __if_<false> { | |
template <class, class _False> | |
using __f = _False; | |
}; | |
template <class _Pred, class _True, class... _False> | |
requires (sizeof...(_False) <= 1) | |
using __if = __minvoke<__if_<__v<_Pred>>, _True, _False...>; | |
template <bool _Pred, class _True, class... _False> | |
requires (sizeof...(_False) <= 1) | |
using __if_c = __minvoke<__if_<_Pred>, _True, _False...>; | |
template <class _Derived> | |
struct __to { | |
template <class _Ty> | |
STATIC_CALL_OPERATOR(auto)(_Ty&& __self) noexcept | |
-> decltype(static_cast<__copy_cvref_t<_Ty&&, _Derived>>((_Ty&&) __self)) { | |
return static_cast<__copy_cvref_t<_Ty&&, _Derived>>((_Ty&&) __self); | |
} | |
}; | |
struct __ident { | |
template <class _Ty> | |
STATIC_CALL_OPERATOR(_Ty&&)(_Ty&& __t) noexcept { | |
return (_Ty&&) __t; | |
} | |
}; | |
template <class _SecondFn, class _FirstFn> | |
struct __compose { | |
template <class _Ty> | |
STATIC_CALL_OPERATOR(auto)(_Ty&& __t) noexcept | |
-> decltype(_SecondFn{}(_FirstFn{}((_Ty&&) __t))) { | |
return _SecondFn{}(_FirstFn{}((_Ty&&) __t)); | |
} | |
}; | |
template <class _Derived, class _GetBaseFn> | |
using __fwd_base_t = __decay_t<decltype(_GetBaseFn{}(__declval<_Derived>()))>; | |
template <class _Ty> | |
struct __mconst { | |
template <class...> | |
using __f = _Ty; | |
}; | |
struct __forwarding_base | |
: __mconst<__forwarding_base> | |
{}; | |
struct __forwarding_rebase { | |
template <class _Derived, class _GetBaseFn, class _GetSelfFn> | |
using __f = | |
typename __fwd_base_t<_Derived, _GetBaseFn>::__forwarding | |
::template __rebind_t<__compose<_GetBaseFn, _GetSelfFn>{}>; | |
}; | |
template <class _Derived, class _GetBaseFn, class _GetSelfFn> | |
using __forwarding_base_t = | |
__minvoke< | |
__if_c< | |
__minvocable<__forwarding_rebase, _Derived, _GetBaseFn, _GetSelfFn>, | |
__forwarding_rebase, | |
__forwarding_base>, | |
_Derived, | |
_GetBaseFn, | |
_GetSelfFn>; | |
template <class... _Ts> | |
requires (sizeof...(_Ts) >= 3) && (sizeof...(_Ts) <= 5) | |
struct __forwarding; | |
template <class _Env> | |
struct __forward { | |
template <class... Ts> | |
using __f = __forwarding<_Env, Ts...>; | |
}; | |
template <class _DerivedId, class _Query, class _QueryFn, class _GetBaseFn, class _GetSelfFn> | |
struct __forwarding<_DerivedId, _Query, _QueryFn, _GetBaseFn, _GetSelfFn> | |
: __forwarding_base_t<__t<_DerivedId>, _GetBaseFn, _GetSelfFn> | |
, private _Query::template __forward< | |
__t<_DerivedId>, | |
__compose<_QueryFn, _GetSelfFn>{}, | |
__forward<_DerivedId>> { | |
template <auto _RebaseFn> | |
using __rebind_t = | |
__forwarding<_DerivedId, _Query, _QueryFn, _GetBaseFn, decltype(_RebaseFn)>; | |
}; | |
template <class _Derived, | |
class _Query, | |
auto _QueryFn, | |
auto _GetBaseFn = __ident{}, // _Derived -> _Base | |
auto _GetSelfFn = __to<_Derived>{}> // __forwarding<Derived....> -> _Derived | |
using __with_query_forwarding = | |
__forwarding< | |
__x<_Derived>, | |
_Query, | |
decltype(_QueryFn), | |
decltype(_GetBaseFn), | |
decltype(_GetSelfFn)>; | |
template <class _Fn, class... _Ts> | |
using __apply = | |
typename _Fn::template __f<_Ts...>; | |
namespace __detail { | |
void get_scheduler(); | |
struct get_scheduler_t { | |
template <class _Derived, auto _GetSchedulerFn, class _SelfT = __mconst<_Derived>> | |
struct __forward { | |
template <class... _Ts> | |
friend auto get_scheduler(get_scheduler_t, const __apply<_SelfT, _Ts...>& __self) | |
-> decltype(_GetSchedulerFn(__self)) { | |
return _GetSchedulerFn(__self); | |
} | |
}; | |
template <class _Ty> | |
STATIC_CALL_OPERATOR(auto)(_Ty&& __t) | |
-> decltype(get_scheduler(__declval<get_scheduler_t>(), (_Ty&&) __t)) { | |
return get_scheduler(get_scheduler_t{}, (_Ty&&) __t); | |
} | |
}; | |
void get_stop_token(); | |
struct get_stop_token_t { | |
template <class _Derived, auto _GetStopTokenFn, class _SelfT = __mconst<_Derived>> | |
struct __forward { | |
template <class... _Ts> | |
friend auto get_stop_token(get_stop_token_t, const __apply<_SelfT, _Ts...>& __self) | |
-> decltype(_GetStopTokenFn(__self)) { | |
return _GetStopTokenFn(__self); | |
} | |
}; | |
template <class _Ty> | |
STATIC_CALL_OPERATOR(auto)(_Ty&& __t) | |
-> decltype(get_stop_token(__declval<get_stop_token_t>(), (_Ty&&) __t)) { | |
return get_stop_token(get_stop_token_t{}, (_Ty&&) __t); | |
} | |
}; | |
} | |
using __detail::get_scheduler_t; | |
inline constexpr get_scheduler_t get_scheduler{}; | |
using __detail::get_stop_token_t; | |
inline constexpr get_stop_token_t get_stop_token{}; | |
struct in_place_stop_token {}; | |
namespace __env { | |
struct __empty {}; | |
template <class _Derived, auto _Query, class _Value, class _Base> | |
struct __with_query | |
: __with_query_forwarding< | |
_Derived, | |
decltype(_Query), | |
[](const auto& __self) -> _Value { return __self.__value_; }, | |
[]<class _Ty>(_Ty&& __self) noexcept -> __copy_cvref_t<_Ty&&, _Base> { | |
return ((_Ty&&) __self).__base_; | |
}> { | |
explicit __with_query(_Value __value) | |
: __value_((_Value&&) __value) | |
, __base_{} | |
{} | |
__with_query(_Value __value, _Base __base) | |
: __value_((_Value&&) __value) | |
, __base_((_Base&&) __base) | |
{} | |
_Value __value_; | |
[[no_unique_address]] _Base __base_; | |
}; | |
template <class _Scheduler, class _Base = __empty> | |
struct __with_scheduler | |
: __with_query<__with_scheduler<_Scheduler, _Base>, get_scheduler, _Scheduler, _Base> { | |
using __with_scheduler::__with_query::__with_query; | |
}; | |
template <class _StopToken, class _Base = __empty> | |
struct __with_stop_token | |
: __with_query<__with_stop_token<_StopToken, _Base>, get_stop_token, _StopToken, _Base> { | |
using __with_stop_token::__with_query::__with_query; | |
}; | |
} | |
using __env::__with_scheduler; | |
using __env::__with_stop_token; | |
extern "C" int printf(char const*, ...); | |
int main() { | |
__with_scheduler<int> e{42}; | |
int i = get_scheduler(e); | |
__with_scheduler<char const*, decltype((e))> e2{"42", e}; | |
char const* sz = get_scheduler(e2); | |
__with_stop_token<in_place_stop_token, decltype((e))> e3{{}, e}; | |
in_place_stop_token stok = get_stop_token(e3); | |
int i2 = get_scheduler(e3); | |
printf("i2 = %d\n", i2); | |
e.__value_++; | |
i2 = get_scheduler(e3); | |
printf("i2 = %d\n", i2); | |
__with_scheduler<char const*, decltype((e3))> e4{"hello", e3}; | |
in_place_stop_token stok2 = get_stop_token(e4); | |
char const* sz2 = get_scheduler(e4); | |
printf("i2 = %s\n", sz2); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment