Skip to content

Instantly share code, notes, and snippets.

@cbeck88
Forked from lichray/visit_at.cc
Last active August 26, 2015 23:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cbeck88/2af8bf52c6633185753d to your computer and use it in GitHub Desktop.
Save cbeck88/2af8bf52c6633185753d to your computer and use it in GitHub Desktop.
Access tuple by runtime index
#include <tuple>
#include <type_traits>
template <int Low, int High, int Mid = (Low + High) / 2, typename = void>
struct _visit_at;
template <int Low, int High, int Mid>
struct _visit_at<Low, High, Mid, std::enable_if_t<(Low > High)>>
{
template <typename... T>
static
decltype(auto) apply(int, T&&...)
{
throw std::out_of_range("visit_at");
}
};
template <int Mid>
struct _visit_at<Mid, Mid, Mid>
{
template <typename Tuple, typename F>
static
decltype(auto) apply(int n, F&& f, Tuple&& tp)
{
if (n != Mid)
throw std::out_of_range("visit_at");
// use std::invoke
return std::forward<F>(f)(
std::get<Mid>(std::forward<Tuple>(tp)));
}
};
template <int Low, int High, int Mid>
struct _visit_at<Low, High, Mid, std::enable_if_t<(Low < High)>>
{
template <typename... T>
static
decltype(auto) apply(int n, T&&... t)
{
if (n < Mid)
return _visit_at<Low, Mid - 1>::apply(n,
std::forward<T>(t)...);
else if (n == Mid)
return _visit_at<Mid, Mid>::apply(n,
std::forward<T>(t)...);
else
return _visit_at<Mid + 1, High>::apply(n,
std::forward<T>(t)...);
}
};
template <typename Tuple, typename F>
inline
decltype(auto) visit_at(int n, F&& f, Tuple&& tp)
{
return _visit_at<0, int(std::tuple_size<std::decay_t<Tuple>>{}) - 1>
::apply(n, std::forward<F>(f), std::forward<Tuple>(tp));
}
#include <iostream>
#include <memory>
#include <cassert>
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