#include <iostream>
#include <tuple>
#include <cassert>

template<std::size_t ...Indices>
struct index_tuple{
};
template<typename Left,typename Right>
struct concat_index_tuple;
template<std::size_t ...Lefts,std::size_t ...Rights>
struct concat_index_tuple<index_tuple<Lefts...>,index_tuple<Rights...>>{
	using type = index_tuple<Lefts...,Rights...>;
};
template<std::size_t Beg,std::size_t End>
struct make_index_tuple{
	using type = typename concat_index_tuple<
					index_tuple<Beg>,
					typename make_index_tuple<Beg + 1,End>::type
	>::type;
};
template<std::size_t End>
struct make_index_tuple<End,End>{
	using type = index_tuple<End>;
};
template<typename Tuple>
struct tuple_index;
template<typename ...Ts>
struct tuple_index<std::tuple<Ts...>>{
	using type = typename make_index_tuple<0,sizeof...(Ts) - 1>::type;
};

struct outputter{
	std::ostream &os;
	outputter(std::ostream &s) : os(s){
	}
	
	template<typename T>
	void operator ()(T const& v){
		os << v << std::endl;
	}
};
struct add_x{
	int i;
	add_x(int v) : i(v){
	}
	
	template<typename T>
	void operator ()(T &v){
		v += i;
	}
};

template<typename F,typename T>
void work_impl(F func,std::size_t s,T &&x){
	assert(s == 0);
	func(std::forward<T>(x));
}
template<typename F,typename T,typename ...Us>
void work_impl(F func,std::size_t s,T &&x,Us &&...xs){
	if(s){
		work_impl(func,s - 1,std::forward<Us>(xs)...);
	}
	else{
		func(std::forward<T>(x));
	}
}
template<typename F,typename Tuple,std::size_t ...Indices>
void work_unpack(F func,std::size_t s,Tuple &&t,index_tuple<Indices...>){
	work_impl(func,s,std::get<Indices>(std::forward<Tuple>(t))...);
}
template<typename F,typename Tuple>
void work(F func,std::size_t s,Tuple &&t){
	work_unpack(func,s,std::forward<Tuple>(t),typename tuple_index<typename std::remove_reference<Tuple>::type>::type{});
}

int main() {
	int i;
	std::cout << "Enter index" << std::endl;
	std::cin >> i;
	
	auto t = std::make_tuple(1,2.0,3ull,4.f);
	work(outputter{std::cout},i,t);
	work(add_x{2},i,t);
	work(outputter{std::cout},i,t);
	work(outputter{std::cout},i,std::make_tuple("a",42,3.14f,&i));
	return 0;
}