Skip to content

Instantly share code, notes, and snippets.

@lichray
Last active January 15, 2023 10:45
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save lichray/dd803a8bb3461fc842e5 to your computer and use it in GitHub Desktop.
Save lichray/dd803a8bb3461fc842e5 to your computer and use it in GitHub Desktop.
Access tuple by runtime index (C++20)
#include <functional>
#include <stdexcept>
#include <tuple>
#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 <int Mid>
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>(f),
std::get<Mid>(std::forward<Tuple>(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>(t)...);
else if (n == Mid)
return _visit_at<Mid, Mid>(n, std::forward<T>(t)...);
else
return _visit_at<Mid + 1, High>(n, std::forward<T>(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<Tuple>>) - 1>(
n, std::forward<F>(f), std::forward<Tuple>(tp));
}
#include <cassert>
#include <iostream>
#include <memory>
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&) {
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment