Skip to content

Instantly share code, notes, and snippets.

@garyp
Last active August 8, 2019 18:49
Show Gist options
  • Save garyp/c190094d57a3c36bb894 to your computer and use it in GitHub Desktop.
Save garyp/c190094d57a3c36bb894 to your computer and use it in GitHub Desktop.
Invoke a function, using a tuple that contains the arguments to be passed to the function
#include <cstddef>
#include <iostream>
#include <future>
#include <thread>
#include <tuple>
#include <utility>
////
//// implementations of index_sequence and related types, based on C++14
////
template <std::size_t... Indices>
struct index_sequence {
using value_type = size_t;
static constexpr std::size_t size() noexcept { return sizeof...(Indices); }
};
namespace detail {
template <std::size_t N, std::size_t... Indices>
struct make_index_sequence : make_index_sequence<N - 1, N - 1, Indices...> {};
template <std::size_t... Indices>
struct make_index_sequence<0, Indices...> {
using type = index_sequence<Indices...>;
};
}
template <std::size_t N>
using make_index_sequence = typename detail::make_index_sequence<N>::type;
template <typename... Types>
using index_sequence_for = make_index_sequence<sizeof...(Types)>;
////
//// callable traits, to simplify type definitions related to callables
////
// mimic the decay done by std::make_tuple
namespace detail {
template <typename T>
struct special_decay {
using type = typename std::decay<T>::type;
};
template <typename T>
struct special_decay<std::reference_wrapper<T>> {
using type = T&;
};
}
template <typename T>
struct callable_traits : callable_traits<decltype(&T::operator())> {};
template <typename T>
struct callable_traits<T&> : callable_traits<T> {};
template <typename R, typename... Args>
struct callable_traits<R(Args...)> {
using return_type = R;
using args_tuple = std::tuple<typename detail::special_decay<Args>::type...>;
using function = std::function<R(Args...)>;
using packaged_task = std::packaged_task<R(Args...)>;
static constexpr index_sequence_for<Args...> kArgsIndices{};
};
template <typename R, typename T, typename... Args>
struct callable_traits<R(T::*)(Args...)> : callable_traits<R(Args...)> {};
////
//// convert a tuple of arguments into a function call
////
// helper to do the actuall function call
template <typename Callable,
typename ArgsTuple,
std::size_t... index,
typename R = typename callable_traits<Callable>::return_type>
R invoke_helper(Callable&& f, ArgsTuple&& t, index_sequence<index...>) {
return f(std::get<index>(std::forward<ArgsTuple>(t))...);
}
// example function that uses the helper to call a function in another thread and
// get a future for the return value
template <typename Callable,
typename... Args,
typename R = typename callable_traits<Callable>::return_type,
typename ArgsTuple = typename callable_traits<Callable>::args_tuple>
std::future<R> async_invoke(Callable&& callable, Args&&... args) {
const ArgsTuple args_tuple = std::make_tuple(std::forward<Args>(args)...);
// in another function that's using saved copies of func and args_tuple:
std::promise<R> p{};
auto f = p.get_future();
std::thread([&callable, args_tuple](std::promise<R> p) {
p.set_value(
invoke_helper(callable, args_tuple, callable_traits<decltype(callable)>::args_indices));
}, std::move(p)).detach();
return f;
}
////
//// sample code using the above
////
int foo(int a, const char* b, double c) {
std::cout << a << " , " << b << " , " << c << '\n';
return a;
}
int main() {
// test of make_index_sequence
std::cout << make_index_sequence<5>::size() << '\n';
auto f = async_invoke(foo, 3, "Bye", 4.7);
std::cout << f.get() << '\n';
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment