Last active
January 9, 2016 13:58
-
-
Save Redchards/fb2a84744567a5d964bf to your computer and use it in GitHub Desktop.
Very simple (albeit confusing) implementation of function currying in C++.
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
| #ifndef IS_CALLABLE_HXX | |
| #define IS_CALLABLE_HXX | |
| #include <functional> | |
| #include <tuple> | |
| template<class ...> | |
| using void_t = void; | |
| // In fact, operator() is defined for the standard functions, even if they are not objects. | |
| // I can't find in the standard where it's specified though. | |
| template<class T> | |
| struct callable_traits : public callable_traits<decltype(&T::operator())> | |
| {}; | |
| template<class Ret, class ... Args> | |
| struct callable_traits<Ret(&)(Args...)> : public callable_traits<Ret(Args...)> | |
| {}; | |
| // NOTE : change std::tuple to typelist ? | |
| template<class Ret, class ... Args> | |
| struct callable_traits<Ret(Args...)> | |
| { | |
| using return_type = Ret; | |
| using function_type = Ret(Args...); | |
| static constexpr size_t arity = sizeof...(Args); | |
| using parameter_list = std::tuple<Args...>; | |
| template<size_t Tindex> | |
| struct argument_type | |
| { | |
| using type = typename std::tuple_element<Tindex, parameter_list>::type; | |
| }; | |
| template<size_t Tindex> | |
| using argument_type_t = typename argument_type<Tindex>::type; | |
| }; | |
| template<class Ret, class ... Args> | |
| struct callable_traits<Ret(*)(Args...)> : public callable_traits<Ret(Args...)> | |
| {}; | |
| template<class FunctionType> | |
| struct callable_traits<std::function<FunctionType>> : public callable_traits<FunctionType> | |
| {}; | |
| template<class Ret, class ClassType, class ... Args> | |
| struct callable_traits<Ret(ClassType::*)(Args...)> : public callable_traits<Ret(Args...)> | |
| { | |
| using class_type = ClassType; | |
| }; | |
| template<class Ret, class ClassType, class ... Args> | |
| struct callable_traits<Ret(ClassType::*)(Args...) const> : public callable_traits<Ret(Args...)> | |
| { | |
| using class_type = const ClassType; | |
| }; | |
| template<class Ret, class ClassType, class ... Args> | |
| struct callable_traits<Ret(ClassType::*)(Args...) volatile> : public callable_traits<Ret(Args...)> | |
| { | |
| using class_type = volatile ClassType; | |
| }; | |
| template<class Ret, class ClassType, class ... Args> | |
| struct callable_traits<Ret(ClassType::*)(Args...) const volatile> : public callable_traits<Ret(Args...)> | |
| { | |
| using class_type = const volatile ClassType; | |
| }; | |
| // is_callable metafunction. Works with any standard metaprogramming library (for instance, boost mpl) | |
| template<class T, class = void, class...> | |
| struct is_callable : std::false_type | |
| {}; | |
| template<class T, class ... Args> | |
| struct is_callable<T, void_t<decltype(std::declval<T>()(std::declval<Args>()...))>, Args...> : std::true_type | |
| {}; | |
| //decltype(std::declval<T>()(std::declval<Args>()...)) | |
| #endif // IS_CALLABLE_HXX |
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
| #ifndef CURRY_HXX | |
| #define CURRY_HXX | |
| #include "CallableTraits.hxx" | |
| #include "StaticIf.hxx" | |
| #include "CTTI.hxx" | |
| template<class Fn, class ... Bounded> | |
| class curry_t | |
| { | |
| public: | |
| /*curry_t(const Fn& f, const Bounded&... args) | |
| : f_(f), | |
| bounded_args{args...} | |
| {}*/ | |
| private: | |
| template<class Seq, class ... Args> | |
| constexpr decltype(auto) call_with_bounded(Seq, Args&&...); | |
| template<size_t ... Seq, class ... Args> | |
| constexpr decltype(auto) call_with_bounded(std::index_sequence<Seq...>, Args&&... args) //noexcept(noexcept(std::declval<Fn>()(std::declval<Args>()...))) | |
| { | |
| static_if(Cond<sizeof...(Bounded) != 0>{}) | |
| ([this](auto&&... targs){ | |
| return f_(std::forward<typename std::tuple_element<Seq, std::tuple<Bounded...>>::type>(std::get<Seq>(bounded_args))..., std::forward<Args>(targs)...); } | |
| ).else_ | |
| ([this](auto&&... targs) { | |
| return f_(std::forward<Args>(targs)...); | |
| } | |
| )(std::forward<Args>(args)...); | |
| } | |
| public: | |
| constexpr curry_t(Fn f, std::tuple<Bounded...>&& args) | |
| : f_(std::forward<Fn>(f)), | |
| bounded_args{std::move(args)} | |
| {} | |
| template<class ... Args> | |
| constexpr decltype(auto) operator()(Args&&... args) & //noexcept(noexcept(std::declval<curry_t>().call_with_bounded(std::declval<Args>()...))) | |
| { | |
| constexpr size_t function_arity = callable_traits<Fn>::arity; | |
| constexpr size_t number_of_arguments = sizeof...(Args) + sizeof...(Bounded); | |
| static_assert(function_arity >= number_of_arguments, | |
| "To many argument passed to curried function"); | |
| return static_if(Cond<function_arity == number_of_arguments>{}) | |
| ([this](auto&&... targs) -> decltype(auto) { | |
| return this->call_with_bounded(std::make_index_sequence<sizeof...(Bounded)>{}, std::forward<decltype(targs)>(targs)...); | |
| } | |
| ).else_ | |
| ([this](auto&&... targs){ | |
| return curry_t<Fn, Bounded..., Args...>{ | |
| f_, std::tuple_cat(bounded_args, std::tuple<decltype(targs)...>{std::forward<decltype(targs)>(targs)...})}; | |
| } | |
| )(std::forward<Args>(args)...); | |
| } | |
| template<class ... Args> | |
| constexpr decltype(auto) operator()(Args&&... args) && //noexcept(noexcept(std::declval<curry_t>().call_with_bounded(std::declval<Args>()...))) | |
| { | |
| constexpr size_t function_arity = callable_traits<Fn>::arity; | |
| constexpr size_t number_of_arguments = sizeof...(Args) + sizeof...(Bounded); | |
| static_assert(function_arity >= number_of_arguments, | |
| "To many argument passed to curried function"); | |
| return static_if(Cond<function_arity == number_of_arguments>{}) | |
| ([this](auto&&... targs) -> decltype(auto) { | |
| return this->call_with_bounded(std::make_index_sequence<sizeof...(Bounded)>{}, std::forward<decltype(targs)>(targs)...); | |
| } | |
| ).else_ | |
| ([this](auto&&... targs){ | |
| return curry_t<Fn, Bounded..., Args...>{ | |
| std::move(f_), std::tuple_cat(bounded_args, std::tuple<decltype(targs)...>{std::forward<decltype(targs)>(targs)...}) | |
| }; | |
| } | |
| )(std::forward<Args>(args)...); | |
| } | |
| private: | |
| Fn f_; | |
| std::tuple<Bounded...> bounded_args; | |
| }; | |
| template<class Fn> | |
| curry_t<Fn> curry(Fn&& f) | |
| { | |
| return {std::forward<Fn>(f), std::tuple<>{}}; | |
| } | |
| #endif // CURRY_HXX |
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
| #ifndef STATIC_IF_HXX | |
| #define STATIC_IF_HXX | |
| #include <type_traits> | |
| #include "CallableTraits.hxx" | |
| namespace details | |
| { | |
| template<bool> | |
| class static_if_statement; | |
| template<class BranchExecutionType> | |
| struct static_if_result | |
| { | |
| template<class Fn> | |
| constexpr static_if_result else_(Fn&&){ return *this; } | |
| template<class Fn> | |
| constexpr static_if_result else_if_(Fn&&){ return *this; } | |
| template<class ... Args> | |
| constexpr decltype(auto) operator()(Args&&... args) noexcept(noexcept(std::declval<BranchExecutionType>()(std::declval<Args>()...))) | |
| { | |
| //static_assert(is_callable<BranchExecutionType, Args...>::value, | |
| // "Result of a static if must be callable."); | |
| return branch_execution(std::forward<Args>(args)...); | |
| } | |
| const BranchExecutionType branch_execution; | |
| }; | |
| template<> | |
| struct static_if_statement<true> | |
| { | |
| template<class Fn> | |
| constexpr static_if_statement else_(Fn&&){ return *this; } | |
| template<class TPred> | |
| constexpr static_if_statement else_if_(TPred){ return *this; } | |
| template<class Fn> | |
| constexpr static_if_result<Fn> operator()(Fn&& f){ return static_if_result<Fn>{f}; } | |
| }; | |
| template<> | |
| struct static_if_statement<false> | |
| { | |
| template<class Fn> | |
| constexpr static_if_result<Fn> else_(Fn&& f){ return static_if_result<Fn>{f}; } | |
| template<class TPred> | |
| constexpr static_if_statement else_if_(TPred){ return static_if_statement<TPred::value>{}; } | |
| template<class Fn> | |
| constexpr static_if_statement operator()(Fn&&){ return *this; } | |
| }; | |
| } | |
| template<bool cond> | |
| using Cond = std::integral_constant<bool, cond>; | |
| template<class TPred> | |
| auto static_if(TPred) | |
| { | |
| return details::static_if_statement<TPred::value>{}; | |
| } | |
| template<bool cond> | |
| auto static_if(Cond<cond>) | |
| { | |
| return details::static_if_statement<cond>{}; | |
| } | |
| #endif // STATIC_IF_HXX |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment