Access tuple by runtime index (C++20)

January 15, 2023 ยท View on GitHub

#include #include #include #include <type_traits>

template <int Low, int High, int Mid = (Low + High) / 2> inline constexpr auto _visit_at = nullptr;

template <int Low, int High, int Mid> requires(Low > High) inline constexpr auto _visit_at<Low, High, Mid> = [](int, auto&&...) { throw std::out_of_range("visit_at"); };

template inline constexpr auto _visit_at<Mid, Mid, Mid> = []<class F, class Tuple>(int, F&& f, Tuple&& tp) -> decltype(auto) { { return std::invoke(std::forward(f), std::get(std::forward(tp))); } };

template <int Low, int High, int Mid> requires(Low < High) inline constexpr auto _visit_at<Low, High, Mid> = []<class... T>(int n, T&&... t) -> decltype(auto) { { if (n < Mid) return _visit_at<Low, Mid - 1>(n, std::forward(t)...); else if (n == Mid) return _visit_at<Mid, Mid>(n, std::forward(t)...); else return _visit_at<Mid + 1, High>(n, std::forward(t)...); } };

template <typename Tuple, typename F> constexpr decltype(auto) visit_at(int n, F&& f, Tuple&& tp) { return _visit_at<0, int(std::tuple_size_v<std::decay_t>) - 1>( n, std::forward(f), std::forward(tp)); }

#include #include #include

int main() { auto tp = std::make_tuple("moew", 'a', 42); auto f = [](auto&& v) { std::cout << v << ", "; }; auto f2 = [](auto&& v) -> decltype(auto) { return std::cout << v; };

visit_at(1, f, tp);
visit_at(0, f, tp);
visit_at(2, f2, tp) << std::endl;
// visit_at(4, f, tp);  // throws out_of_range

auto tp2 = std::make_tuple(std::make_unique<int>(3));
std::unique_ptr<int> p;
visit_at(
    0, [&](auto&& v) { p = std::move(v); }, std::move(tp2));
assert(p);

try {
    visit_at(
        0, [p(std::move(p))](auto&& v) {}, std::make_tuple());
    assert(0);
} catch (std::out_of_range&) {
}

}