Last active
March 8, 2018 19:25
-
-
Save 2bbb/cb2e2c0831a9fc8283ee1c887e8eb903 to your computer and use it in GitHub Desktop.
polyfunction
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 <cstddef> | |
#include <cstdint> | |
#include <type_traits> | |
#include <functional> | |
namespace bbb { | |
namespace { | |
template <bool b, typename type> | |
using enable_if_t = typename std::enable_if<b, type>::type; | |
}; | |
inline namespace function_traits { | |
template <typename> | |
struct is_function | |
: std::false_type {}; | |
template <typename res, typename ... arguments> | |
struct is_function<std::function<res(arguments ...)>> | |
: std::false_type {}; | |
template <std::size_t index, typename ... arguments> | |
using type_at_t = typename std::tuple_element<index, std::tuple<arguments ...>>::type; | |
template <typename patient> | |
struct has_call_operator { | |
template <typename inner_patient, decltype(&inner_patient::operator())> struct checker {}; | |
template <typename inner_patient> static std::true_type check(checker<inner_patient, &inner_patient::operator()> *); | |
template <typename> static std::false_type check(...); | |
using type = decltype(check<patient>(nullptr)); | |
static constexpr bool value = type::value; | |
}; | |
namespace detail { | |
template <typename ret, typename ... arguments> | |
struct function_traits { | |
static constexpr std::size_t arity = sizeof...(arguments); | |
using result_type = ret; | |
using arguments_types_tuple = std::tuple<arguments ...>; | |
template <std::size_t index> | |
using argument_type = type_at_t<index, arguments ...>; | |
using function_type = std::function<ret(arguments ...)>; | |
template <typename function_t> | |
static constexpr function_type cast(function_t f) { | |
return static_cast<function_type>(f); | |
} | |
}; | |
}; | |
template <typename T> | |
struct function_traits : public function_traits<decltype(&T::operator())> {}; | |
template <typename class_type, typename ret, typename ... arguments> | |
struct function_traits<ret(class_type::*)(arguments ...) const> | |
: detail::function_traits<ret, arguments ...> {}; | |
template <typename class_type, typename ret, typename ... arguments> | |
struct function_traits<ret(class_type::*)(arguments ...)> | |
: detail::function_traits<ret, arguments ...> {}; | |
template <typename ret, typename ... arguments> | |
struct function_traits<ret(*)(arguments ...)> | |
: detail::function_traits<ret, arguments ...> {}; | |
template <typename ret, typename ... arguments> | |
struct function_traits<ret(arguments ...)> | |
: detail::function_traits<ret, arguments ...> {}; | |
template <typename ret, typename ... arguments> | |
struct function_traits<std::function<ret(arguments ...)>> | |
: detail::function_traits<ret, arguments ...> {}; | |
}; | |
template <typename function_type> | |
struct polyfunction; | |
template <typename res, typename ... arguments> | |
struct polyfunction<res(arguments ...)> { | |
using function_type = std::function<res(arguments ...)>; | |
polyfunction() | |
: f([](arguments ...) -> res { return {}; }) {}; | |
polyfunction(const polyfunction &) = default; | |
polyfunction(polyfunction &&) = default; | |
polyfunction(const function_type &f) | |
: f(f) {}; | |
polyfunction(function_type &&f) | |
: f(std::move(f)) {}; | |
template <typename res_, typename ... arguments_> | |
polyfunction(const std::function<res_(arguments_ ...)> &f_) | |
: f(polyfunction::convert(f_, std::make_index_sequence<sizeof...(arguments_)>())) | |
{}; | |
template <typename res_, typename ... arguments_> | |
polyfunction(std::function<res_(arguments_ ...)> &&f_) | |
: f(polyfunction::convert(std::move(f_), std::make_index_sequence<sizeof...(arguments_)>())) | |
{}; | |
template < | |
typename function_type, | |
typename = typename std::enable_if< | |
!is_function<function_type>::value && | |
has_call_operator<function_type>::value | |
>::type | |
> | |
polyfunction(function_type f) | |
: polyfunction(function_traits<function_type>::cast(f)){}; | |
polyfunction &operator=(const polyfunction &) = default; | |
polyfunction &operator=(polyfunction &&) = default; | |
polyfunction &operator=(const function_type &f) { this->f = f; return *this; }; | |
polyfunction &operator=(function_type &&f) { this->f = std::move(f); return *this; }; | |
template <typename function_type> | |
auto operator=(function_type f) | |
-> typename std::enable_if< | |
!is_function<function_type>::value && | |
has_call_operator<function_type>::value, | |
polyfunction & | |
>::type | |
{ return operator=(function_traits<function_type>::cast(f)); }; | |
template <typename res_, typename ... arguments_> | |
polyfunction &operator=(const std::function<res_(arguments_ ...)> &f) { | |
this->f = polyfunction::convert(f, std::make_index_sequence<sizeof...(arguments_)>()); | |
return *this; | |
}; | |
template <typename res_, typename ... arguments_> | |
polyfunction &operator=(std::function<res_(arguments_ ...)> &&f) { | |
this->f = polyfunction::convert(std::move(f), std::make_index_sequence<sizeof...(arguments_)>()); | |
return *this; | |
}; | |
template <typename ... other_arguments> | |
res operator()(arguments ... args, other_arguments && ...) const { return f(std::forward<arguments>(args) ...); }; | |
template <typename ... other_arguments> | |
operator std::function<res(arguments ..., other_arguments && ...)>() const { | |
auto &&f_ = f; | |
return [f_](arguments ... args, other_arguments && ...) -> res { return f_(args ...); }; | |
}; | |
template <typename function_type_> | |
std::function<function_type_> as() const { return std::function<function_type_>(*this); }; | |
private: | |
template <typename res_, typename ... arguments_, std::size_t ... indices> | |
static auto convert(std::function<res_(arguments_ ...)> f, std::index_sequence<indices ...> &&) | |
-> enable_if_t<sizeof...(arguments_) <= sizeof...(arguments), function_type> | |
{ | |
return [f](arguments ... args) { | |
return static_cast<res>(f(get<indices>(std::forward<arguments>(args) ...) ...)); | |
}; | |
}; | |
template <std::size_t index> | |
static auto get(arguments ... args) | |
-> typename std::tuple_element<index, std::tuple<arguments ...>>::type | |
{ return std::get<index>(std::tuple<arguments ...>(std::forward<arguments>(args) ...)); }; | |
function_type f; | |
}; | |
}; | |
#include <iostream> | |
int main(int argc, char *argv[]) { | |
{ | |
std::function<int(int)> g = [](int x) { return x * x; }; | |
std::function<float()> h = [] { return 17.2f; }; | |
bbb::polyfunction<int(int, int)> f1(g); | |
bbb::polyfunction<int(int, int)> f2(h); | |
std::cout << f1(4, 2) << std::endl; | |
std::cout << f2(4, 2) << std::endl; | |
} | |
{ | |
std::function<void(std::string)> g = [](std::string a) { std::cout << a << std::endl; }; | |
bbb::polyfunction<void(const std::string &, int)> f(g); | |
bbb::polyfunction<void(const std::string &, int)> f_([](std::string a) { std::cout << a << std::endl; }); | |
f_ = []() { std::cout << "p" << std::endl; }; | |
f_("a", 4); | |
f("a", 4); | |
decltype(f) f2; | |
f2("a", 4); | |
std::function<void(const std::string &, int, float, long)> h = f; | |
f.as<void(const std::string &, int, int)>()("abc", 1, 2); | |
std::function<void(const std::string &, int, int)> p = f; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment