#include <tuple>
#include <functional>
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <list>
#include <array>
#include <iterator>

// for_each_multi
template<typename Tuple, typename Callable>
Callable for_each_multi(Tuple begins, Tuple ends, Callable fn);

// visit_tuple
template<typename Callable, typename Head, typename... Tail>
Callable visit_tuple(Callable fn, Head&& aTuple, Tail&& ...aTail);

template<size_t size>
struct visit_tuple_ws;

// apply_tuple
template<typename Applicator, typename Tuple, typename Callable>
void apply_tuple(Tuple&& tuple, Callable&& fn);

// tuple unpack utils
template<size_t ...>
struct sequence { };

template<size_t N, size_t ...S>
struct generate : generate<N-1u, N-1u, S...> { };

template<size_t ...S>
struct generate<0u, S...> {
  typedef sequence<S...> type;
};

struct Dereference 
{
    template< typename F, typename T, size_t ...S >
	static void apply(T&& tuple, F& fn, sequence<S...> )
	{
	    fn( *std::get<S>(tuple) ...);
	}
};	

struct Increment 
{
	template <typename T>
	void operator()(T&& t) 
	{
		++t;
	}
};

struct CheckBeginEnd
{
	CheckBeginEnd() : BeginIsNotEnd(true) {}
	
	template<typename T>
	void operator()(T&& begin, T&& end)
	{
		BeginIsNotEnd &= (begin != end);
	}		
		
	bool BeginIsNotEnd;
};

// for_each_multi
template<typename Tuple, typename Callable>
Callable for_each_multi(Tuple begins, Tuple ends, Callable fn)
{
     for ( ; visit_tuple(CheckBeginEnd(), begins, ends).BeginIsNotEnd; visit_tuple(Increment(), begins) )
          apply_tuple<Dereference>(begins, fn);
     return fn;
}

template<typename Callable, typename Head, typename... Tail>
Callable for_each_multi(Callable fn, Head&& head, Tail&& ...tail)
{
	return for_each_multi( std::make_tuple(begin(head), begin(tail)...), std::make_tuple(end(head), end(tail)...), fn);
}

// visit_tuple
template<typename Callable, typename Head, typename... Tail>
Callable visit_tuple(Callable f, Head&& aTuple, Tail&& ...aTail)
{
	const size_t size = std::tuple_size<typename std::remove_reference<Head>::type>::value-1;
    visit_tuple_ws<size>::visit(f, aTuple, aTail...);
    return f;
}

template<size_t size>
struct visit_tuple_ws
{
	template<typename Callable, typename Head, typename... Tail>
    static void visit(Callable& f, Head& aTuple, Tail& ...aTail)
    {
        visit_tuple_ws<size-1>::visit(f, aTuple, aTail...);    
        f(std::get<size>(aTuple), std::get<size>(aTail)...);
    }
};

template<>
struct visit_tuple_ws<0u>
{
	template<typename Callable, typename Head, typename... Tail>
    static void visit(Callable& f, Head& aTuple, Tail& ...aTail)
    {	
         f(std::get<0>(aTuple), std::get<0>(aTail)...);
    }
};

// apply_tuple
template<typename Unpacker, typename Tuple, typename Callable>
void apply_tuple(Tuple&& tuple, Callable&& fn)
{
	const int size = std::tuple_size<typename std::remove_reference<Tuple>::type>::value;
	Unpacker::apply( std::forward<Tuple>(tuple), std::forward<Callable>(fn), typename generate<size>::type() );
}


using namespace std;

// support
template<typename Head, typename ...Tail>
auto begins(Head&& head, Tail&& ...tail)
    -> decltype(std::make_tuple(head.begin(), tail.begin()...))
{
	return std::make_tuple(head.begin(), tail.begin()...);
}

template<typename Head, typename ...Tail>
auto ends(Head&& head, Tail&& ...tail)
	-> decltype(std::make_tuple(head.end(), tail.end()...))
{
	return std::make_tuple(head.end(), tail.end()...);
}

#define ON(...) begins(__VA_ARGS__), ends(__VA_ARGS__)

int main()
{
    vector<int> vec1 {1,2,3};
    list<int> list1 {10, 20 ,30};
	array<string, 3> arr {"one", "two", "three"};
	
    for_each_multi(ON(vec1, list1, arr), [](int i, int l, const string& s)
    {
        cout << i << " (" << s << ")" << " * 10 is " << l << endl;
    });
	
    return 0;
}