Open1

引数で値を受け渡しする関数を戻り値で返すようにする

yaito3014yaito3014

初期実装

#include <functional>
#include <print>
#include <tuple>
#include <type_traits>
#include <utility>

namespace yk {

template <class T, template <class...> class TT>
struct is_specialization_of : std::false_type {};

template <template <class...> class TT, class... Ts>
struct is_specialization_of<TT<Ts...>, TT> : std::true_type {};

template <class T, template <class...> class TT>
inline constexpr bool is_specialization_of_v = is_specialization_of<T, TT>::value;

template <template <class...> class TT, class... Ts>
struct count_specializations : std::integral_constant<std::size_t, 0> {};

template <template <class...> class TT, class T, class... Ts>
struct count_specializations<TT, T, Ts...> : std::integral_constant<std::size_t, count_specializations<TT, Ts...>::value + is_specialization_of_v<T, TT>> {};

namespace detail {

template <std::size_t N, template <class...> class TT, class Seq, class... Ts>
struct count_specializations_until_impl {};

template <std::size_t N, template <class...> class TT, std::size_t... Is, class... Ts>
struct count_specializations_until_impl<N, TT, std::index_sequence<Is...>, Ts...> : count_specializations<TT, std::conditional_t<(Is < N), Ts, void>...> {};

}  // namespace detail

template <std::size_t N, template <class...> class TT, class... Ts>
struct count_specializations_until : detail::count_specializations_until_impl<N, TT, std::index_sequence_for<Ts...>, Ts...> {};

template <class T, class Tuple>
struct prepend_tuple {};

template <class T, class... Ts>
struct prepend_tuple<T, std::tuple<Ts...>> {
  using type = std::tuple<T, Ts...>;
};

template <class T, class Tuple>
using prepend_tuple_t = typename prepend_tuple<T, Tuple>::type;

template <class T>
struct ref_placeholder_t {};

template <class T>
inline constexpr ref_placeholder_t<T> ref;

template <class T, std::size_t I, class... Ts, class Tuple>
constexpr decltype(auto) get_or_forward(T&& x, Tuple&& tuple) {
  if constexpr (is_specialization_of_v<std::remove_cvref_t<T>, ref_placeholder_t>) {
    return std::get<count_specializations_until<I, ref_placeholder_t, std::remove_cvref_t<Ts>...>::value>(tuple);
  } else {
    return std::forward<T>(x);
  }
}

template <class... Ts>
struct ref_invoke_tuple_helper {};

template <>
struct ref_invoke_tuple_helper<> {
  using type = std::tuple<>;
};

template <class T, class... Ts>
struct ref_invoke_tuple_helper<T, Ts...> : ref_invoke_tuple_helper<Ts...> {};

template <class T, class... Ts>
struct ref_invoke_tuple_helper<yk::ref_placeholder_t<T>, Ts...> {
  using type = prepend_tuple_t<T, typename ref_invoke_tuple_helper<Ts...>::type>;
};

template <class... Ts>
using ref_invoke_tuple_helper_t = typename ref_invoke_tuple_helper<Ts...>::type;

template <class F, class... Args>
constexpr auto ref_invoke(F&& f, Args&&... args) {
  ref_invoke_tuple_helper_t<std::remove_cvref_t<Args>...> val;

  [&]<std::size_t... Is>(std::index_sequence<Is...>) {  //
    std::invoke(std::forward<F>(f), get_or_forward<Args, Is, Args...>(std::forward<Args>(args), val)...);
  }(std::index_sequence_for<Args...>{});

  return val;
}

}  // namespace yk

int main() {  //
  auto f = [](auto& x, auto val, auto& y) {
    x = val + 33;
    y = val + 4;
  };

  auto res = yk::ref_invoke(f, yk::ref<int>, 42, yk::ref<int>);

  std::println("{}", res);
}