Most samples of c++ books leverage recursively mechanism to print std::tuple.
Is it possible to print std::tuples iteratively by leverage sizeof...(Typename)?
For example, the function signature is like below:
template<typename... Ts>
constexpr void PrintTuple(std::tuple<Ts...>& tuple)
Then I could use sizeof...(Ts) to know how many elements in the tuple and then
I could use std::get< i >(tuple) to retrieve the individual element?
Here's one of the possible solutions:
#include <cstddef>
#include <iostream>
#include <tuple>
#include <type_traits>
#include <utility>
template <typename T, std::size_t ...I, typename F>
void tuple_foreach_impl(T &&tuple, std::index_sequence<I...>, F &&func)
{
// In C++17 we would use a fold expression here, but in C++14 we have to resort to this.
using dummy_array = int[];
dummy_array{(void(func(std::get<I>(tuple))), 0)..., 0};
}
template <typename T, typename F> void tuple_foreach(T &&tuple, F &&func)
{
constexpr int size = std::tuple_size<std::remove_reference_t<T>>::value;
tuple_foreach_impl(std::forward<T>(tuple), std::make_index_sequence<size>{},
std::forward<F>(func));
}
int main()
{
auto x = std::make_tuple("Meow", 1, 2.3);
tuple_foreach(x, [](auto &&value)
{
std::cout << value << ' ';
});
// Prints:
// Meow
// 1
// 2.3
}
With tuple_foreach making a proper printer should be simple.
template <typename T> void print_tuple(const T &tuple)
{
std::cout << '{';
tuple_foreach(tuple, [first = true](auto &value) mutable
{
if (!first)
std::cout << "; ";
else
first = 0;
std::cout << value;
});
std::cout << '}';
}
// ...
print_tuple(std::make_tuple("Meow", 1, 2.3)); // Prints `{Meow; 1; 2.3}`
c++20 makes everything very easy:
void print_tuple(auto&& t) noexcept
{
[&]<auto ...I>(std::index_sequence<I...>) noexcept
{
(
[&]() noexcept
{
if constexpr(I)
{
std::cout << ", ";
}
std::cout << std::get<I>(t);
}(),
...
);
std::cout << '\n';
}
(
std::make_index_sequence<
std::tuple_size_v<std::remove_cvref_t<decltype(t)>>
>()
);
}
Related
I don't know if there is a good and clean way to index variadic arguments when unpacking tuple-like objects into callable handlers, i.e. when using std::apply.
Here is a not perfect, but rather clean solution:
const auto animals = std::make_tuple("cow", "dog", "sheep");
// handwritten, stateful, bad...
std::apply([](const auto& ... str){
const auto print = [](const auto& str, size_t index){
std::cout << index << ": " << str << '\n';
};
// this should not be done by the user!!!
size_t i = 0;
(print(str, i++), ...);
}, animals);
This solution is cleaner than using overloads with std::index_sequence since you don't have to write any code outside lambda's scope. Templates are not allowed within a block scope, so one would need to create some helper class outside.
It is bad, since there is a mutable state that is created by user. There should be no such thing, index should be available implicitly and on demand.
Here is what I think is desired and what I managed to implement so far:
const auto animals = std::make_tuple("cow", "dog", "sheep");
// desired
// JavaScript-style destructuring.
// C++ structured bindings are not allowed as arguments
// apply([](auto ... {value, index}){ ... }, animals);
// still bad, but better - index is implicit and constant
std::apply(indexed([](auto ... indexedValue){
const auto print = [](const auto& indexedValue){
const auto &[index, value] = indexedValue;
std::cout << index << ": " << value << '\n';
};
(print(indexedValue), ...);
}), animals);
C++ does not allow to have structured bindings as function arguments, and this is very unfortunate. Anyways, I consider this api better, than incrementing a counter by hand or writing some boilerplate helper.
All you have to do is to follow the damn train wrap your callable into indexed() function.
And it does not require any modifications on STL's part.
However, my implementation is very far from optimal. It results in far more instructions than the first example: https://godbolt.org/z/3G4doao39
Here is my implementation for indexed() function which I would like to be corrected.
#include <cstddef>
#include <type_traits>
#include <tuple>
namespace detail {
template <size_t I, typename T>
struct _indexed
{
constexpr static size_t index = I;
T value;
constexpr _indexed(std::integral_constant<size_t, I>, T t)
: value(t)
{}
template <size_t Elem>
friend constexpr auto get(const detail::_indexed<I, T>& v) noexcept ->
std::tuple_element_t<Elem, detail::_indexed<I, T>>{
if constexpr (Elem == 0)
return I;
if constexpr (Elem == 1)
return v.value;
}
};
template <size_t I, typename T>
_indexed(std::integral_constant<size_t, I>, T) -> _indexed<I, T>;
template <typename CRTP>
class _add_indices
{
public:
template <typename ... Args>
constexpr decltype(auto) operator()(Args &&... args) const noexcept {
return (*this)(std::make_index_sequence<sizeof...(Args)>(), std::forward<Args>(args)...);
}
private:
template <typename ... Args, size_t ... I>
constexpr decltype(auto) operator()(std::index_sequence<I...>, Args ... args) const noexcept {
// does not compile
// return std::invoke(&CRTP::callable, static_cast<CRTP const&>(*this),
// _indexed(std::integral_constant<size_t, I>{}, std::forward<Args>(args))...);
return static_cast<const CRTP&>(*this).callable(_indexed(std::integral_constant<size_t, I>{}, std::forward<Args>(args))...);
}
};
}
template <size_t I, typename T>
struct std::tuple_size<detail::_indexed<I, T>> : std::integral_constant<size_t, 2> {};
template <size_t I, typename T>
struct std::tuple_element<0, detail::_indexed<I, T>>
{
using type = size_t;
};
template <size_t I, typename T>
struct std::tuple_element<1, detail::_indexed<I, T>>
{
using type = T;
};
template <typename Callable>
constexpr auto indexed(Callable c) noexcept{
struct _c : detail::_add_indices<_c> {
Callable callable;
};
return _c{.callable = c};
}
// api:
// apply(indexed([](auto ... indexedValue){}), tuple);
If I correctly understand what do you want... it seems to me that you only need a class/struct as follows
template <typename Callable>
struct indexed_call
{
Callable c;
template <std::size_t ... Is, typename ... As>
constexpr auto call (std::index_sequence<Is...>, As && ... as) const {
return c(std::pair{Is, std::forward<As>(as)}...);
}
template <typename ... As>
constexpr auto operator() (As && ... as) const {
return call(std::index_sequence_for<As...>{}, std::forward<As>(as)...);
}
};
and an explicit, trivial, deduction guide
template <typename C>
indexed_call(C) -> indexed_call<C>;
and the call change a little as follows
std::apply(indexed_call{[](auto ... indexedValue){
const auto print = [](const auto& iV){
const auto &[index, value] = iV;
std::cout << index << ": " << value << '\n';
};
(print(indexedValue), ...);
}}, animals);
or, if you prefer, also the call can be simplified
std::apply(indexed_call{[](auto ... iV){
((std::cout << iV.first << ": " << iV.second << '\n'), ...);
}}, animals);
The following is a full compiling example
#include <type_traits>
#include <iostream>
#include <tuple>
template <typename Callable>
struct indexed_call
{
Callable c;
template <std::size_t ... Is, typename ... As>
constexpr auto call (std::index_sequence<Is...>, As && ... as) const {
return c(std::pair{Is, std::forward<As>(as)}...);
}
template <typename ... As>
constexpr auto operator() (As && ... as) const {
return call(std::index_sequence_for<As...>{}, std::forward<As>(as)...);
}
};
template <typename C>
indexed_call(C) -> indexed_call<C>;
int main(){
const auto animals = std::make_tuple("cow", "dog", "sheep");
std::apply(indexed_call{[](auto ... indexedValue){
const auto print = [](const auto& iV){
const auto &[index, value] = iV;
std::cout << index << ": " << value << '\n';
};
(print(indexedValue), ...);
}}, animals);
std::apply(indexed_call{[](auto ... iV){
((std::cout << iV.first << ": " << iV.second << '\n'), ...);
}}, animals);
}
----- EDIT -----
The OP writes
I don't think that utilizing std::pair is semantically correct. Would be better to have .value, .index
I usually prefer to use standard components, when functional equivalents, but if you want something with a value and a index components, you can add a simple struct (name it as you prefer)
template <typename V>
struct val_with_index
{
std::size_t index;
V value;
};
and another trivial explicit deduction guide
template <typename V>
val_with_index(std::size_t, V) -> val_with_index<V>;
then you have to modify the call() method to use it instead std::pair
template <std::size_t ... Is, typename ... As>
constexpr auto call (std::index_sequence<Is...>, As && ... as) const {
return c(val_with_index{Is, std::forward<As>(as)}...);
} // ......^^^^^^^^^^^^^^
and now works for the double lambda case
For the simplified case, obviously you have to change first and second with index and value
std::apply(indexed_call{[](auto ... iV){
((std::cout << iV.index << ": " << iV.value << '\n'), ...);
}}, animals); // ....^^^^^...............^^^^^
Again the full compiling example
#include <type_traits>
#include <iostream>
#include <tuple>
template <typename V>
struct val_with_index
{
std::size_t index;
V value;
};
template <typename V>
val_with_index(std::size_t, V) -> val_with_index<V>;
template <typename Callable>
struct indexed_call
{
Callable c;
template <std::size_t ... Is, typename ... As>
constexpr auto call (std::index_sequence<Is...>, As && ... as) const {
return c(val_with_index{Is, std::forward<As>(as)}...);
}
template <typename ... As>
constexpr auto operator() (As && ... as) const {
return call(std::index_sequence_for<As...>{}, std::forward<As>(as)...);
}
};
template <typename C>
indexed_call(C) -> indexed_call<C>;
int main(){
const auto animals = std::make_tuple("cow", "dog", "sheep");
std::apply(indexed_call{[](auto ... indexedValue){
const auto print = [](const auto& iV){
const auto &[index, value] = iV;
std::cout << index << ": " << value << '\n';
};
(print(indexedValue), ...);
}}, animals);
std::apply(indexed_call{[](auto ... iV){
((std::cout << iV.index << ": " << iV.value << '\n'), ...);
}}, animals);
}
My goal is given an unknown tuple I want to just print the outputs of a) everything that is numeric added together and b everthing that can be cast to a string to be concatenated. So for example if I was given a random tuple t(1,true,"hello",2.1f,"world") the output would be 3.1, "hello world"
I'm not that proficient in c++ this is the code I have so far
#include <tuple>
#include <string>
using namespace std;
template <size_t I = 0, typename... Ts>
typename enable_if<I == sizeof...(Ts), void>::type
printTuple(tuple<Ts...> t)
{
return;
}
template <size_t I = 0, typename... Ts>
typename enable_if<(I < sizeof...(Ts)), void>::type
printTuple(tuple<Ts...> t)
{
cout << get<I>(t) << " ";
printTuple<I + 1>(t);
}
int main()
{
tuple <int32_t, bool, std::string, float, const char*> t(1,"Hello",true,"World",19.1f);
printTuple(t);
return 0;
}
the code iterates through it but I cant add the unknown objects together or concatenate them, I appreciate the help thank you.
Use std::apply to get all elements of tuple.
Use fold expression (action,...) to perform some operartion on each tuple elements.
Use if constexpr to check if tuple element is numeric or string convertible value:
template<class ... Args>
auto sum(std::tuple<Args...> const& t) {
double res = 0.0;
auto addToRes = [&](auto arg){
if constexpr (std::is_arithmetic_v<decltype(arg)>)
res += arg;
};
std::apply([&](auto&&... args){ (addToRes(args),...); }, t);
return res;
}
template<class ... Args>
auto concat(std::tuple<Args...> const& t) {
std::string res;
auto addToRes = [&](auto arg){
if constexpr (std::is_convertible_v<decltype(arg), std::string>)
res += arg;
};
std::apply([&](auto&&... args){ (addToRes(args),...); }, t);
return res;
}
template<class ... Args>
void foo(std::tuple<Args...> t) {
std::cout << sum(t) << std::endl; // 41
std::cout << concat(t) << std::endl; // sstrxxx
}
int main(){
const char* p = "s";
foo(std::make_tuple(p,1,10u,10L,10.0f,std::string("str"),10.0,"xxx"));
}
I have a std::tuple, and I want to unwrap the contents using std::index_sequence in order to call a variadic function template
Consider the following example code:
#include <iostream>
#include <tuple>
template<typename... Ts>
void foo(const std::string& s, Ts... ts)
{
std::cout << "foo called with " << s << " and " << sizeof...(Ts) << " ts\n";
}
template<typename Tuple, std::size_t... Ixs>
void call_foo(const std::string& s, Tuple& t, std::index_sequence<Ixs...>)
{
foo(s, std::get<Ixs>(t)...);
}
template<typename... Ts>
struct Bar
{
Bar(Ts... ts) : t(ts...)
{ }
void do_it()
{
call_foo("hi", t, std::make_index_sequence<std::tuple_size<decltype(t)>::value>{});
}
std::tuple<Ts...> t;
};
template<typename... Ts> Bar<Ts...> make_bar(Ts... ts) { return Bar<Ts...>(ts...); }
int main ()
{
auto bar = make_bar(1, 'a', 2.3);
bar.do_it();
}
Note that I have to call through call_foo with my index_sequence in order to "unwrap" the index_sequence for calling std::get...
Is it possible to forego the intermediate call_foo function, and call foo directly?
That is, unwrap the tuple directly at the call-site?
If you don't want to or can't use std::apply, I can suggest a few alternatives.
The snippets below are based on the previous revision of your question that didn't have the class Bar in it. The same solutions work for the new revision as well.
(1) You could replace call_foo with a C++20 lambda with an explicit template parameter list:
#include <cstddef>
#include <iostream>
#include <tuple>
#include <utility>
template<typename... Ts>
void foo(const std::string& s, Ts... ts)
{
std::cout << "foo called with " << s << " and " << sizeof...(Ts) << " ts\n";
}
template<typename... Ts>
void bar(Ts... ts)
{
const std::string s = "hello world";
const auto t = std::make_tuple(ts...);
[&]<std::size_t ...I>(std::index_sequence<I...>)
{
foo(s, std::get<I>(t)...);
}
(std::make_index_sequence<std::tuple_size_v<decltype(t)>>{});
}
int main()
{
bar(1, 'a', 2.3);
}
Try it live
Unfortunately, GCC 8 currently seems to be the only major compiler that supports those.
(2) If your compiler doesn't have the new fancy lambdas, or you don't want to write the index_sequence boilerplate every time you need to expand the tuple, I suggest following:
#include <cstddef>
#include <iostream>
#include <tuple>
#include <utility>
template <std::size_t ...I, typename F> void with_sequence_impl(F &&func, std::index_sequence<I...>)
{
func(std::integral_constant<std::size_t, I>{}...);
}
template <std::size_t N, typename F> void with_sequence(F &&func)
{
with_sequence_impl(std::forward<F>(func), std::make_index_sequence<N>{});
}
template<typename... Ts>
void foo(const std::string& s, Ts... ts)
{
std::cout << "foo called with " << s << " and " << sizeof...(Ts) << " ts\n";
}
template<typename... Ts>
void bar(Ts... ts)
{
const std::string s = "hello world";
const auto t = std::make_tuple(ts...);
with_sequence<std::tuple_size_v<decltype(t)>>([&](auto ... i)
{
foo(s, std::get<i.value>(t)...);
});
}
int main()
{
bar(1, 'a', 2.3);
}
Try it live
I wanna implement a Print function which works this:
Print<1, 3>("Hello", "World");
and I hope that it will print "Hello" one time and "World" 3 times.I wonder how to implement it.
Below is my stupid code, of course it failed when compiling:
template <unsigned int n, unsigned int ...n_next,
typename T, typename ...Ts>
void Print(T & t, Ts & ... ts)
{
for(int i = 0; i < n; i++)
{
std::cout << t << " ";
}
std::cout << std::endl;
Print<n_next..., ts...>(ts...);
}
template <unsigned int n, typename T>
void Print(T & t)
{
for(int i = 0; i < n; i++)
{
std::cout << t << " ";
}
std::cout << std::endl;
}
This will make it:
template <unsigned int n, typename T>
void Print(T&& t)
{
for(int i = 0; i < n; i++)
{
std::cout << std::forward<T>(t) << " ";
}
std::cout << std::endl;
}
template <std::size_t Idx1, std::size_t... Idx, class T, class... Ts>
void Print(T&& t, Ts&& ... ts) {
Print<Idx1>(std::forward<T>(t));
using expand = int[];
(void)expand{(Print<Idx>(std::forward<Ts>(ts)), 0) ...};
}
I also propose a completely different solution that avoid at all recursion and for() loops.
It simulate template folding in C++14 in initialization of an unused C-style array.
First the main Print(), that expand the variadic lists calling a Print_h() helper function, passing to it the values and list (index sequence) correspinding to numbers of iteration for every value
template <std::size_t ... Ns, typename ... Ts>
void Print (Ts ... ts)
{
using unused=int[];
(void)unused { 0, (Print_h(std::make_index_sequence<Ns>{}, ts), 0)... };
}
Next the helper function that uses the same trick for multiple printing
template <std::size_t ... Is, typename T>
void Print_h (std::index_sequence<Is...>, T const & t)
{
using unused=std::size_t[];
(void)unused { 0, (std::cout << t << " ", Is)... };
std::cout << std::endl;
}
The following is the full compiling C++14 example
#include <utility>
#include <iostream>
template <std::size_t ... Is, typename T>
void Print_h (std::index_sequence<Is...>, T const & t)
{
using unused=std::size_t[];
(void)unused { 0, (std::cout << t << " ", Is)... };
std::cout << std::endl;
}
template <std::size_t ... Ns, typename ... Ts>
void Print (Ts ... ts)
{
using unused=int[];
(void)unused { 0, (Print_h(std::make_index_sequence<Ns>{}, ts), 0)... };
}
int main ()
{
Print<1u, 3u>("hello", "world");
}
If you can't use C++14 but only C++11, isn't difficult to develop substitutes for std::make_index_sequence and std::index_sequence (both available only from C++14).
Obviously in C++17 you can use template folding simplifying the functions as follows
template <std::size_t ... Is, typename T>
void Print_h (std::index_sequence<Is...>, T const & t)
{
((std::cout << t << " ", (void)Is), ...);
std::cout << std::endl;
}
template <std::size_t ... Ns, typename ... Ts>
void Print (Ts ... ts)
{ (Print_h(std::make_index_sequence<Ns>{}, ts), ...); }
I see four problems in your code
(1) the recursive call
Print<n_next..., ts...>(ts...);
is wrong because you have to use Ts... types in template argument list, not ts... values
Print<n_next..., Ts...>(ts...);
or, better (because permit a trick I explain next) without explicating the types
Print<n_next...>(ts...);
(2) is better if you receive as const references the values
template <unsigned int n, unsigned int ...n_next,
typename T, typename ...Ts>
void Print(T const & t, Ts ... ts)
// ..........^^^^^
otherwise you can't call Print() with constant values as follows
Print<1u, 3u>(1, "world");
(3) better use unsigned value for indexes in for loops because you have to test they with unsigned values (minor problem)
// ..VVVVVVVV
for (unsigned int i = 0; i < n; i++)
(4) you have to place the ground case for Print() (the one that receive only a value) before the recursive case.
I suggest to substitute they with
template <typename = void>
void Print ()
{ }
because, this way, all prints are done in recursive version an you don't need to repeat equal code in two different function (but you have to call Print<n_next...>(ts...); the recursion.
So I propose to modify your code as follows
#include <iostream>
template <typename = void>
void Print ()
{ }
template <unsigned int n, unsigned int ...n_next,
typename T, typename ...Ts>
void Print(T const & t, Ts ... ts)
{
for(auto i = 0u; i < n; i++)
{
std::cout << t << " ";
}
std::cout << std::endl;
Print<n_next...>(ts...);
}
int main ()
{
Print<1u, 3u>("hello", "world");
}
You can make your code work by just swapping the declarations of the two overloads and removing the ts... template argument in the recursive call:
template <unsigned int n, typename T>
void Print(T & t)
{
for(unsigned int i = 0; i < n; i++)
{
std::cout << t << " ";
}
std::cout << std::endl;
}
template <unsigned int n, unsigned int ...n_next,
typename T, typename ...Ts>
void Print(T & t, Ts & ... ts)
{
for(unsigned int i = 0; i < n; i++)
{
std::cout << t << " ";
}
std::cout << std::endl;
Print<n_next...>(ts...);
}
(also, be consistant with signedness)
Demo
Furthermore, you don't need to duplicate the printing part, just call the other overload:
template <unsigned int n, typename T>
void Print(T & t)
{
for(unsigned int i = 0; i < n; i++)
{
std::cout << t << " ";
}
std::cout << std::endl;
}
template <unsigned int n, unsigned int ...n_next,
typename T, typename ...Ts>
void Print(T & t, Ts & ... ts)
{
Print<n>(t);
Print<n_next...>(ts...);
}
Alternatively, if you can use C++17 for fold expressions, you can do the following (use forwarding references and std::forward if you need to):
template<typename T>
void Print(unsigned int n, T& t)
{
for(unsigned int i = 0; i < n; i++)
{
std::cout << t << " ";
}
std::cout << std::endl;
}
template<unsigned int... Ns, typename... Ts>
void Print(Ts&... ts)
{
(Print(Ns, ts), ...);
}
Demo
I have the following pseudo code:
template <typename... Ts>
void f(int index) {
std::vector<std::function<void(void)>> funcs;
funcs.push_back([](){ std::cout << typeid(type_1).name() << std::endl; });
funcs.push_back([](){ std::cout << typeid(type_2).name() << std::endl; });
funcs.push_back([](){ std::cout << typeid(type_3).name() << std::endl; });
funcs.push_back([](){ std::cout << typeid(type_4).name() << std::endl; });
funcs[index]();
}
Imagine that the Ts... parameter pack holds type_1, type_2, type_3 and type_4.
how can I expand the parameter pack in order to achieve something like this? I mean - how can I get 4 push_back() calls if there are 4 parameters in the template pack, and also have the different types in the different lambdas? I don't know the syntax..
And can I actually get some sort of an array of such functions at compile time, so there are no push_backs at runtime?
C++17 solution is ok, but C++14 is best.
For C++17, something like this, I suppose
(funcs.push_back([](){ std::cout << typeid(Ts).name() << std::endl; }), ...);
or, better (IMHO), using emplace_back()
(funcs.emplace_back([](){ std::cout << typeid(Ts).name() << std::endl; }), ...);
But remeber that is
std::vector<std::function<void(void)>>
not
std::vector<std::function<void>>
In C++14 (and C++11) you can obtain something similar with the trick of intialization of the unused array; the function can be written as
template <typename ... Ts>
void f (int index)
{
using unused = int[];
std::vector<std::function<void(void)>> funcs;
(void)unused { 0, (funcs.emplace_back([]()
{ std::cout << typeid(Ts).name() << std::endl; }), 0)... };
funcs[index]();
}
Update.
From re-reading the question I think you just want to call the function once for the I'th type.
I which case it's trivial at compile time:
#include <array>
#include <type_traits>
#include <iostream>
#include <string>
template <class T>
void show_type()
{
std::cout << typeid(T).name() << std::endl;
}
template <typename... Ts>
void f(int index) {
using function_type = void(*)();
constexpr auto size = sizeof...(Ts);
constexpr std::array<function_type, size> funcs =
{
&show_type<Ts>...
};
funcs[index]();
}
int main()
{
for(int i = 0 ; i < 3 ; ++i)
f<int, double, std::string>(i);
}
example output:
i
d
NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
Something along these lines, perhaps:
template <typename... Ts>
void f(int index) {
int i = 0;
auto _ = {
(index == i++ ? ((std::cout << typeid(Ts).name() << std::endl) , 0) : 0) ...
};
}
Demo
If all you want to do is do something for the nth type in a template parameter pack, where n is a runtime variable, then the vector + function approach isn't really great. Better to add an index sequence in there and fold:
template <typename T> struct tag_t { using type = T; };
template <typename T> constexpr inline tag_t<T> tag{};
template <class F, size_t... Is, typename... Tags>
void match(F f, size_t i, std::index_sequence<Is...>, Tags... tags) {
auto inner = [&](auto tag) { f(tag); return true; };
bool matched = ((i == Is && inner(tags)) || ...);
if (!matched) {
// failure case?
}
}
template <typename... Ts, class F>
void match(F f, size_t i) {
return match(f, i, std::index_sequence_for<Ts...>(), tag<Ts>... );
}
template <typename... Ts>
void foo(int index) {
match<Ts...>([](auto tag){
std::cout << typeid(typename decltype(tag)::type).name() << std::endl;
}, index);
}
This construction allows you to add a failure case, where you might call the passed-in function with some special type:
struct failure { };
template <class F, size_t... Is, typename... Tags>
void match(F f, size_t i, std::index_sequence<Is...>, Tags... tags) {
auto inner = [&](auto tag) { f(tag); return true; };
bool matched = ((i == Is && inner(tags)) || ...);
if (!matched) {
f(failure{});
}
}
template <typename... Ts>
void foo(int index) {
match<Ts...>(overload(
[](auto tag){
std::cout << typeid(typename decltype(tag)::type).name() << std::endl;
},
[](failure ) { /* ... */ }
), index);
}