Last active
March 3, 2016 10:32
-
-
Save vmrob/c177309d66a3989f4d85 to your computer and use it in GitHub Desktop.
Tuple forwarding for function types
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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