#include <array> // for std::array
#include <tuple> // for std::tuple, std::get
#include <type_traits> // for std::remove_cv, std::remove_reference
#include <utility> // for std::forward
template<typename T>
using Invoke = typename T::type;
template<typename T>
using RemoveCv = Invoke<std::remove_cv<T>>;
template<typename T>
using RemoveReference = Invoke<std::remove_reference<T>>;
template<typename T>
using Unqualified = RemoveCv<RemoveReference<T>>;
namespace impl {
template<typename T>
struct result_of {
using type = Invoke<T>;
};
template<typename TRet, typename... TArg>
struct result_of<TRet(*)(TArg...) > {
using type = TRet;
};
template<typename TClass, typename TRet, typename... TArg>
struct result_of<TRet(TClass::*)(TArg...)> {
using type = TRet;
};
template<typename Fn, typename... Arg>
struct result_of<Fn(Arg...)> {
using type = Invoke<std::result_of<Fn(Arg...)>>;
};
}
template<typename T>
using ResultOf = Invoke<impl::result_of<T>>;
template<int...>
struct indices { };
namespace impl {
template<int N, int... Is>
struct build_indices : build_indices<(N - 1), (N - 1), Is...> { };
template<int... Is>
struct build_indices < 0, Is...> : indices<Is...> { };
}
template<int N>
struct build_indices : impl::build_indices<N> { };
namespace detail {
struct tuplelike_tag { };
struct arraylike_tag { };
template<typename>
struct call_with_traits;
template<typename... Ts>
struct call_with_traits<std::tuple<Ts...>> {
using tag = tuplelike_tag;
enum { size = sizeof...(Ts) };
};
template<typename T, std::size_t Sz>
struct call_with_traits<std::array<T, Sz>> {
using tag = arraylike_tag;
enum { size = Sz };
};
template<typename T, std::size_t Sz>
struct call_with_traits<T[Sz]> {
using tag = arraylike_tag;
enum { size = Sz };
};
template<typename F, typename T, int... Is>
auto call_with(F && f, T && tup, indices<Is...>, tuplelike_tag) -> ResultOf<Unqualified<F>> {
return (std::forward<F>(f))(std::get<Is>(std::forward<T>(tup))...);
}
template<typename F, typename A, int... Is>
auto call_with(F && f, A && arr, indices<Is...>, arraylike_tag) -> ResultOf<Unqualified<F>> {
return (std::forward<F>(f))(std::forward<A>(arr)[Is]...);
}
}
template<typename F, typename Cont>
inline auto call_with(F && f, Cont && cont) -> ResultOf<Unqualified<F>> {
using unqualified = Unqualified<Cont>;
using traits = typename detail::call_with_traits<unqualified>;
using tag = typename detail::call_with_traits<unqualified>::tag;
using no_tag = typename traits::tag;
return detail::call_with(std::forward<F>(f), std::forward<Cont>(cont), build_indices<traits::size>(), tag());
}
int add(int a, int b, int c) {
return (a + b + c);
}
int main(int argc, char ** argv) {
std::tuple<int, int, int> tup { 1, 2, 3 };
int res = call_with(&add, tup);
return 0;
}