template <typename F, typename ... args_t>
constexpr bool is_invocable_v = std::is_invocable_v<F, args_t...>;
template <typename F, typename ... args_t>
constexpr bool is_invocable_v<F, ttps_pack<>, args_t...> =
std::is_invocable_v<F, args_t...>;
;
template <typename F, typename ... ttps_args_t, typename ... args_t>
constexpr bool is_invocable_v<F, ttps_pack<ttps_args_t...>, args_t...> =
requires{ std::declval<F>().template operator()<ttps_args_t...>(std::declval<args_t>()...); }
;
template <typename F, typename ttps, typename args>
struct invocable {
static_assert([](){ return false; }(), "parameters must be wrapped in `pack`");
};
template <typename F, typename ... ttps, typename ... args>
struct invocable<F, pack<ttps...>, pack<args...>> {
constexpr static bool value = requires{
std::declval<F>().template operator()<ttps...>(std::declval<args>()...);
};
};
template <typename F, typename ... args>
struct invocable<F, pack<>, pack<args...>> {
constexpr static bool value = std::is_invocable_v<F, args...>;;
};
template <typename F, typename ttps, typename args>
constexpr bool is_invocable_v = invocable<F, ttps, args>::value;
consteval void test() {
const auto f_witht_ttps = []<typename T>(bool, int){};
static_assert(is_invocable_v<decltype(f_witht_ttps), pack<int>, pack<bool, int>>);
const auto f_void = []<typename T>(){};
static_assert( is_invocable_v<decltype(f_void), pack<int>, pack<>>);
static_assert(not is_invocable_v<decltype(f_void), pack<int>, pack<void>>);
static_assert( is_invocable_v<decltype(f_void), pack<int>, details::skip_void_t<pack<void>>>);
}