Skip to content

Instantly share code, notes, and snippets.

@fredemmott
Created August 10, 2024 03:00
Show Gist options
  • Select an option

  • Save fredemmott/d33f6db63da0309ac72a933b0a524294 to your computer and use it in GitHub Desktop.

Select an option

Save fredemmott/d33f6db63da0309ac72a933b0a524294 to your computer and use it in GitHub Desktop.
template <size_t... I>
struct drop_last_impl_t {
drop_last_impl_t() = delete;
constexpr drop_last_impl_t(std::index_sequence<I...>) {
}
template <template <class...> class T, class... TArgs>
using next_t = T<std::tuple_element_t<I, std::tuple<TArgs...>>...>;
template <class TIn>
static constexpr decltype(auto) make_tuple(TIn&& in) {
return std::forward_as_tuple(
std::forward<std::tuple_element_t<I, TIn>>(get<I>(in))...);
}
};
template <class... TArgs>
struct drop_last_t {
using seq_t = std::make_index_sequence<sizeof...(TArgs) - 1>;
using impl_t = decltype(drop_last_impl_t(seq_t {}));
template <template <class...> class T>
using next_t = impl_t::template next_t<T, TArgs...>;
template <class... TInitArgs>
static constexpr decltype(auto) make_tuple(TInitArgs&&... args) {
return impl_t::make_tuple(
std::forward_as_tuple(std::forward<TInitArgs>(args)...));
}
};
template <class TFn, class... TArgs>
struct arg_dropping_invocable_t {
static consteval bool value_fn() {
if constexpr (std::invocable<TFn, TArgs...>) {
return true;
} else if constexpr (sizeof...(TArgs) >= 1) {
using next_t
= drop_last_t<TFn, TArgs...>::template next_t<arg_dropping_invocable_t>;
return next_t::value_fn();
}
return false;
}
static constexpr bool value = value_fn();
};
template <class TFn, class... TArgs>
concept arg_dropping_invocable = arg_dropping_invocable_t<TFn, TArgs...>::value;
template <class TFn, class... TArgs>
requires arg_dropping_invocable<TFn, TArgs...>
struct arg_dropping_invoker_t {
static constexpr auto invoke(TFn&& fn, TArgs&&... args) {
if constexpr (std::invocable<TFn, TArgs...>) {
return std::invoke(std::forward<TFn>(fn), std::forward<TArgs>(args)...);
} else if constexpr (sizeof...(TArgs) >= 1) {
using dropper_t = drop_last_t<TFn, TArgs...>;
using next_t
= drop_last_t<TFn, TArgs...>::template next_t<arg_dropping_invoker_t>;
auto next_args = dropper_t::make_tuple(
std::forward<TFn>(fn), std::forward<TArgs>(args)...);
return std::apply(
[]<class... NextArgs>(NextArgs&&... args) {
return next_t::invoke(std::forward<NextArgs>(args)...);
},
std::move(next_args));
} else {
static_assert(false, "Should be statically unreachable due to concept");
}
}
};
template <class TFn, class... TArgs>
constexpr auto arg_dropping_invoke(TFn&& fn, TArgs&&... args) {
return arg_dropping_invoker_t<TFn, TArgs...>::invoke(
std::forward<TFn>(fn), std::forward<TArgs>(args)...);
}
constexpr auto test_fn = [](int a, int b) { return a + b; };
static_assert(arg_dropping_invocable<decltype(test_fn), int, int>);
static_assert(arg_dropping_invocable<decltype(test_fn), int, int, int>);
static_assert(arg_dropping_invocable<decltype(test_fn), int, int, void*>);
static_assert(arg_dropping_invocable<decltype(test_fn), int, int, float*>);
static_assert(!arg_dropping_invocable<decltype(test_fn), int>);
static_assert(!arg_dropping_invocable<decltype(test_fn), int, float*>);
static_assert(arg_dropping_invoke(test_fn, 1, 2) == 3);
static_assert(arg_dropping_invoke(test_fn, 1, 2, 3) == 3);
static_assert(arg_dropping_invoke(test_fn, 1, 2, "i am not an int") == 3);
static_assert(arg_dropping_invoke(test_fn, 1, 2, nullptr) == 3);
static_assert(arg_dropping_invoke(test_fn, 1, 2, nullptr, "lolwhut") == 3);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment