Skip to content

Instantly share code, notes, and snippets.

@Redchards
Last active January 9, 2016 13:58
Show Gist options
  • Select an option

  • Save Redchards/fb2a84744567a5d964bf to your computer and use it in GitHub Desktop.

Select an option

Save Redchards/fb2a84744567a5d964bf to your computer and use it in GitHub Desktop.
Very simple (albeit confusing) implementation of function currying in C++.
#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
#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
#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