Example
constexpr auto tuple = [][[nodiscard]](auto... args) {
return [=][[nodiscard]](auto fn) { return fn(args...); };
};
constexpr auto apply(auto fn, auto t) { return t(fn); };
static_assert(0 == apply([](auto... args) { return sizeof...(args); }, tuple()));
static_assert(1 == apply([](auto... args) { return sizeof...(args); }, tuple(1)));
static_assert(2 == apply([](auto... args) { return sizeof...(args); }, tuple(1, 2)));
Puzzle
-
Can you implement get by number/type for lambda based tuple?
- Double points for not using std::tuple!
constexpr auto tuple = [][[nodiscard]](auto... args) {
return [=][[nodiscard]](auto fn) { return fn(args...); };
};
template<auto N> [[nodiscard]] constexpr auto get(auto t); // TODO
template<class T> [[nodiscard]] constexpr auto get(auto t); // TODO
static_assert(1 == get<0>(tuple(1, 2, 3)));
static_assert(2 == get<1>(tuple(1, 2, 3)));
static_assert(3 == get<2>(tuple(1, 2, 3)));
static_assert('a' == get<0>(tuple('a', 42, 77.)));
static_assert(42 == get<1>(tuple('a', 42, 77.)));
static_assert(77. == get<2>(tuple('a', 42, 77.)));
static_assert(42 == get<int>(tuple('a', 42, 77.)));
static_assert(77. == get<double>(tuple('a', 42, 77.)));
static_assert('a' == get<char>(tuple('a', 42, 77.)));
Solutions
#include <type_traits>
#include <utility>
template<::std::size_t N, class ... Args>
struct nth_type ;
template<class T, class ... Args>
struct nth_type<0, T, Args...> : ::std::type_identity<T> {};
template<::std::size_t N, class T, class ... Args>
struct nth_type<N, T, Args...> : nth_type<N-1, Args...> {};
template<auto N> [[nodiscard]] constexpr auto get(auto t)
{
auto func = []<class ... Args> (Args ... args) {
typename nth_type<N, Args...>::type result;
auto impl = [Count = 0] (auto input, auto & result) mutable {
if (Count == N) {
result = input;
}
++Count;
};
(impl(args, result), ...);
return result;
};
return t(func);
}
template<class T> [[nodiscard]] constexpr auto get(auto t)
{
auto func = []<class ... Args> (Args ... args) {
T result;
auto impl = [has_val = false]<class Arg> (Arg input, auto & result) mutable {
if (::std::is_same_v<T, Arg> && not has_val) {
result = input;
has_val = true;
}
};
(impl(args, result), ...);
return result;
};
return t(func);
}
namespace detail {
template <std::size_t N, typename T> struct elem_by_index { T &ref; };
template <typename T> struct elem_by_type { T &ref; };
} // namespace detail
template <auto N> [[nodiscard]] constexpr auto get(auto t) {
return t([]<typename... Ts>(Ts... elems) {
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
struct all_elems : detail::elem_by_index<Is, Ts>... {};
return []<typename U>(const detail::elem_by_index<N, U> &elem) {
return elem.ref;
}(all_elems{elems...});
}(std::index_sequence_for<Ts...>{});
});
}
template <class T> [[nodiscard]] constexpr auto get(auto t) {
return t([]<typename... Ts>(Ts... elems) {
struct all_elems : detail::elem_by_type<Ts>... {};
return [](const detail::elem_by_type<T> &elem) {
return elem.ref;
}(all_elems{elems...});
});
};
namespace detail {
template <class T, std::size_t>
struct tag {
T value;
};
constexpr auto get(auto visitor) {
return [=]<class... Ts>(Ts... args) {
return [&]<auto... Ns>(std::index_sequence<Ns...>) {
struct : tag<Ts, Ns>... {
} values{{.value = args}...};
return visitor(values);
}(std::index_sequence_for<Ts...>{});
};
}
} // namespace detail
template <std::size_t N>
[[nodiscard]] constexpr auto get(auto t) {
return t(detail::get(
[]<class T>(detail::tag<T, N> &element) { return element.value; }));
}
template <class T>
[[nodiscard]] constexpr auto get(auto t) {
return t(detail::get(
[]<auto N>(detail::tag<T, N> &element) { return element.value; }));
}
template <auto N, auto I>
requires (I <= N)
constexpr auto get_by_index(auto first, auto... rest) {
if constexpr (I == N) {
return first;
} else {
return get_by_index<N, I + 1>(rest...);
}
};
template<auto N> [[nodiscard]] constexpr auto get(auto t) {
return t([](auto... args) {
return get_by_index<N, 0>(args...);
});
}
template <class T>
constexpr auto get_by_type(auto first, auto... rest) {
if constexpr (std::is_same<T, decltype(first)>()) {
return first;
} else {
return get_by_type<T>(rest...);
}
};
template<class T> [[nodiscard]] constexpr auto get(auto t) {
return t([](auto... args) {
return get_by_type<T>(args...);
});
constexpr auto ignore_first = [][[nodiscard]](auto first, auto... args) {
return [=][[nodiscard]](auto fn) { return fn(args...); };
};
constexpr auto get_first = [][[nodiscard]](auto first, auto... args) {
return first;
};
template<auto N> [[nodiscard]] constexpr auto get(auto t) {
if constexpr (N == 0){
return t(get_first);
}else{
return get<N-1>(t(ignore_first));
}
};
template<class T> [[nodiscard]] constexpr auto get(auto t){
if constexpr(std::is_same_v<T,decltype(t(get_first))>){
return t(get_first);
}else{
return get<T>(t(ignore_first));
}
}
template <class T, std::size_t N>
struct any {
T value{};
};
constexpr auto indicies = [](auto fn) {
return [fn]<class... Ts>(Ts... args) {
return [&]<std::size_t... Ns>(std::index_sequence<Ns...>) {
struct : any<Ts, Ns>... {
} _{args...};
return fn(_);
}(std::make_index_sequence<sizeof...(args)>{});
};
};
template <auto N>
[[nodiscard]] constexpr auto get(auto t) {
return t(indicies([]<class T>(any<T, N>& t) { return t.value; }));
}
template <class T>
[[nodiscard]] constexpr auto get(auto t) {
return t(indicies([]<auto N>(any<T, N>& t) { return t.value; }));
template<auto N>
constexpr auto nth_index = [](auto arg1, auto... args) {
if constexpr(N == 0) {
return arg1;
}
else {
return nth_index<N-1>(args...);
}
};
template<auto N> [[nodiscard]] constexpr auto get(auto t) {
return t(nth_index<N>);
}
template<typename T>
constexpr auto Tth_type = [](auto arg1, auto... args) {
if constexpr(std::is_same_v<decltype(arg1),T>) {
return arg1;
}
else {
return Tth_type<T>(args...);
}
};
template<class T> [[nodiscard]] constexpr auto get(auto t) {
return t(Tth_type<T>);
template<auto N>
auto getNth = [](auto t, auto...ts) {
if constexpr(N==0)
return t;
else
return getNth<N-1>(ts...);
};
template<typename T>
auto getT = [](auto t, auto...ts) {
if constexpr( std::is_same_v<T, decltype(t)> )
return t;
else
return getT<T>(ts...);
};
template<auto N> [[nodiscard]] constexpr auto get(auto t) { return t(getNth<N>); }
template<class T> [[nodiscard]] constexpr auto get(auto t) { return t(getT<T>);
constexpr auto apply(auto fn, auto t) { return t(fn); };
template <typename T, std::size_t N>
struct enumerated {
T value{};
};
constexpr auto get_by(auto fn) {
return [=]<typename... Ts>(Ts... args) {
return [&]<std::size_t... Ns>(std::index_sequence<Ns...>) {
struct _ : enumerated<decltype(args), Ns>... {
} args_and_ns{args...};
return fn(args_and_ns);
}(std::make_index_sequence<sizeof...(args)>{});
};
};
template <std::size_t N>
[[nodiscard]] constexpr auto get(auto t) {
return apply(get_by([]<typename T>(enumerated<T, N>& t) { return t.value; }), t);
}
template <typename T>
[[nodiscard]] constexpr auto get(auto t) {
return apply(get_by([]<std::size_t N>(enumerated<T, N>& t) { return t.value; }), t);
}
template<class T, std::size_t N> struct any { T value{}; };
template<auto N> [[nodiscard]] constexpr auto get(auto t){
return t([](auto... args) {
return [&]<std::size_t... Ns>(std::index_sequence<Ns...>) {
struct : any<decltype(args), Ns>... { } _{args...};
return []<class K>(any<K, N>& x) { return x.value; }(_);
}
(std::make_index_sequence<sizeof...(args)>{});
});
}
template<class T> [[nodiscard]] constexpr auto get(auto t){
return t([](auto... args) {
return (... + [](auto x){
if constexpr(std::is_same_v<T, decltype(x)>) return x;
else return 0;
}(args));
});
}
template <auto N> [[nodiscard]] constexpr auto get(auto t) {
return t([](auto... args) {
return [&]<auto... Ns>(std::index_sequence<Ns...>) {
return [](decltype((void*)Ns)..., auto* nth, auto*...) {
return *nth;
}(&args...);
}(std::make_index_sequence<N>());
});
}