Skip to content

Instantly share code, notes, and snippets.

@vmrob
Last active March 3, 2016 10:32
Show Gist options
  • Save vmrob/c177309d66a3989f4d85 to your computer and use it in GitHub Desktop.
Save vmrob/c177309d66a3989f4d85 to your computer and use it in GitHub Desktop.
Tuple forwarding for function types
#include <utility>
#include <tuple>
#include <type_traits>
namespace meta {
// detects callable objects, not functions
// http://stackoverflow.com/questions/15393938/find-out-if-a-c-object-is-callable
template<typename T>
class is_callable_object {
private:
using yes = char(&)[1];
using no = char(&)[2];
struct Fallback { void operator()(); };
struct Derived : T, Fallback { };
template<typename U, U> struct Check;
template<typename>
static yes test(...);
template<typename C>
static no test(Check<void (Fallback::*)(), &C::operator()>*);
public:
static const bool value = sizeof(test<Derived>(0)) == sizeof(yes);
};
template <typename T>
static constexpr auto is_callable_object_v = is_callable_object<T>::value;
// function traits
inline namespace detail {
template <typename ReturnType, typename ClassType, typename ... Args>
struct mem_function_traits {
static constexpr auto arity = sizeof...(Args);
using result_type = ReturnType;
using class_type = ClassType;
static constexpr auto valid = true;
template <size_t i>
struct arg {
using type = std::tuple_element_t<i, std::tuple<Args...>>;
};
template <size_t i>
using arg_t = typename arg<i>::type;
};
template <typename ReturnType, typename ... Args>
struct reg_function_traits {
static constexpr auto arity = sizeof...(Args);
using result_type = ReturnType;
static constexpr auto valid = true;
template <size_t i>
struct arg {
using type = decltype(std::get<i>(std::tuple<Args...>()));
};
template <size_t i>
using arg_t = typename arg<i>::type;
};
} // namespace detail
// no types defined
template <typename T, typename = void, typename = void>
struct function_traits {
static constexpr auto valid = false;
};
// generic functions
template <typename T>
struct function_traits<T,
std::enable_if_t<std::is_class<T>::value>,
std::enable_if_t<is_callable_object_v<T>>>
: public function_traits<decltype(&T::operator())>
{ };
template <typename T>
struct function_traits<T,
std::enable_if_t<std::is_reference<T>::value>>
: public function_traits<typename std::remove_reference_t<T>>
{ };
// const member functions
template <typename ReturnType, typename ClassType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const,
std::enable_if_t<std::is_member_function_pointer<ReturnType(ClassType::*)(Args...)>::value>>
: public mem_function_traits<ReturnType, ClassType, Args...>
{ };
// member functions
template <typename ReturnType, typename ClassType, typename ... Args>
struct function_traits<ReturnType(ClassType::*)(Args...),
std::enable_if_t<std::is_member_function_pointer<ReturnType(ClassType::*)(Args...)>::value>>
: public mem_function_traits<ReturnType, ClassType, Args...>
{ };
// regular functions
template <typename ReturnType, typename ... Args>
struct function_traits<ReturnType(Args...),
std::enable_if_t<std::is_function<ReturnType(Args...)>::value>>
: public reg_function_traits<ReturnType, Args...>
{ };
// is_callable
template <typename T>
struct is_callable : public std::integral_constant<bool, function_traits<T>::valid> {};
// result_of
template <typename T>
struct result_of {
using type = std::result_of_t<T>;
};
template <typename R, typename... Args>
struct result_of<R(*)(Args...)> {
using type = R;
};
template <typename R, typename... Args>
struct result_of<R(&)(Args...)> {
using type = R;
};
template <typename R, typename C, typename ... Args>
struct result_of<R(C::*)(Args...)> {
using type = R;
};
template <typename R, typename C, typename ... Args>
struct result_of<R(C::*)(Args...) const> {
using type = R;
};
} // namespace meta
template <typename F, typename = void>
struct forward_tuple_impl {
template <typename... TupleArgs, size_t... I, typename = void>
auto operator()(F&& f, std::tuple<TupleArgs...>&& tuple, std::index_sequence<I...>) {
return f(std::get<I>(std::forward<std::tuple<TupleArgs...>>(tuple))...);
}
};
template <typename F>
struct forward_tuple_impl<F, std::enable_if_t<std::is_member_function_pointer<F>::value>> {
template <typename ClassPtrType, typename... TupleArgs, size_t... I>
auto operator()(F&& f, std::tuple<ClassPtrType, TupleArgs...>&& tuple, std::index_sequence<0, I...>) {
static_assert(
std::is_same<std::remove_cv_t<typename meta::function_traits<F>::class_type*>, std::remove_cv_t<ClassPtrType>>::value,
"First argument in parameter pack must be a pointer to an class with member function F");
return (std::get<0>(tuple)->*f)(std::get<I>(std::forward<std::tuple<ClassPtrType, TupleArgs...>>(tuple))...);
}
};
template<typename F, typename... Args>
constexpr auto forward_tuple(F&& f, std::tuple<Args...>&& args) {
return forward_tuple_impl<F>()(
std::forward<F>(f),
std::forward<std::tuple<Args...>>(args),
std::index_sequence_for<Args...>{}
);
}
template <typename F, typename... Args>
class bound_function {
public:
bound_function(F f, Args... args)
: _f(std::move(f))
, _args(std::forward<Args>(args)...)
{}
auto operator()(){
return forward_tuple(std::move(_f), std::move(_args));
}
private:
F _f;
std::tuple<Args...> _args;
};
template <typename... Args>
auto bind(Args&&... args) {
return bound_function<Args...>(std::forward<Args>(args)...);
}
template <typename T>
struct Vector2 {
void setX(T v) { x = v; }
void setY(T v) { y = v; }
T getX() { return x; }
T getY() { return y; }
T x;
T y;
};
using Vector2f = Vector2<float>;
#include <cstdio>
int main() {
Vector2f vec{0.0f, 2.0f};
auto f = bind(&Vector2f::setX, &vec, 3.0f);
printf("%f\n", vec.getX());
f();
printf("%f\n", vec.getX());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment