Last active
July 23, 2020 07:11
-
-
Save pnck/b5de55e635397597eac34cea7e58ad69 to your computer and use it in GitHub Desktop.
convert std::function into a currying-able object
This file contains 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
template <typename F> | |
struct curry_wrapped {}; | |
template <typename R> | |
struct curry_wrapped<std::function<R()>> { | |
std::function<R()> _f; | |
// Calling with no parameters ==> call original function directly | |
// 无参调用 ==> 直接调用原函数 | |
inline R operator()() { | |
return _f(); | |
} | |
explicit curry_wrapped(decltype(_f) f) : _f(std::move(f)) {} | |
}; | |
template <typename R, typename A1> | |
struct curry_wrapped<std::function<R(A1)>> { | |
std::function<R(A1)> _f; | |
// With only 1 parameter ==> also direct call | |
// 单参数调用 ==> 无需返回无参的函数,所以也直接调用 | |
inline R operator()(A1 &&a) { | |
return _f(std::forward<A1>(a)); | |
} | |
explicit curry_wrapped(decltype(_f) f) : _f(std::move(f)) {} | |
}; | |
// 2 or more parameters need to be fixed | |
// 若有2个以上参数需要固定 | |
template <typename R, typename A1, typename ... ARGS> | |
struct curry_wrapped<std::function<R(A1, ARGS...)>> { | |
using orig_ftype = std::function<R(A1, ARGS...)>; | |
orig_ftype _f; | |
// if 1 parameter is fixed, at least one another should be passed later, | |
// thus a wrapper which accepts remaining parameters is returned | |
// 若只固定单参数,必然至少还有一个参数需要传入,那么返回接受剩余参数的wrapper | |
auto operator()(A1 &&a1) { | |
return curry_wrapped<std::function<R(ARGS...)>>{ | |
[this, &a1](ARGS &&... args) -> R { | |
return _f(std::forward<A1>(a1), std::forward<ARGS>(args)...); | |
} | |
}; | |
} | |
// if all parameters has been passed, make direct call | |
// 若一次性传进了所有参数,视为直接调用 | |
inline R operator()(A1 &&a1, ARGS &&...args) { | |
return _f(std::forward<A1>(a1), std::forward<ARGS>(args)...); | |
} | |
// if multiple parameters need to be fixed | |
// because 1/all parameter call has been specialized | |
// a wrapper which accepts 2 to (total-1) parameters should be returned | |
// 若一次性固定多个参数,提取已固定参数的数量,返回一个接受剩余空位的新wrapper | |
template <typename ...Ts> | |
struct any_args_helper { | |
static constexpr size_t total_arg_num = sizeof...(ARGS) + 1; | |
static constexpr size_t fixed_arg_num = sizeof...(Ts); | |
template <size_t ... Is>/*use its type only*/ | |
constexpr static auto _get_ftype_helper(std::index_sequence<Is...>) { | |
return std::function<R(decltype(std::get<Is + fixed_arg_num>(std::make_tuple(A1{}, ARGS{}...)/*empty tuple*/))...)>{}; | |
} | |
using extracted_ftype = decltype(_get_ftype_helper(std::make_index_sequence<total_arg_num - fixed_arg_num>{})); | |
template <size_t ...Is, typename _EF_R, typename ..._EF_ARGS> | |
constexpr static auto _make_wrapper_helper(orig_ftype &_f, | |
std::tuple<Ts...> &&tp/*packed fixed args*/, | |
std::function<_EF_R(_EF_ARGS...)>/*only type needed*/, | |
std::index_sequence<Is...>/*for extracting tuple*/) { | |
return [_f/*must copy*/, tp/*must copy*/](_EF_ARGS &&...args) -> _EF_R { | |
return _f(std::get<Is>(tp)..., std::forward<_EF_ARGS>(args)...); | |
}; | |
} | |
static curry_wrapped<extracted_ftype> make_wrapped(orig_ftype &_f, Ts &&...args) { | |
return curry_wrapped<extracted_ftype>{ | |
_make_wrapper_helper(_f, | |
std::forward_as_tuple(args...), | |
extracted_ftype{}, | |
std::make_index_sequence<fixed_arg_num>{}) | |
}; | |
} | |
}; | |
template <typename ...Ts> | |
auto operator()(Ts &&...args) { | |
return any_args_helper<Ts...>::make_wrapped(_f, std::forward<Ts>(args)...); | |
} | |
explicit curry_wrapped(orig_ftype f) : _f(std::move(f)) {} | |
}; | |
template <typename F> | |
curry_wrapped<std::function<F>> make_currying(std::function<F> f) { | |
return curry_wrapped<std::function<F>>{f}; | |
} | |
double plus4(char v1, int v2, float v3, double v4) { | |
return v1 + v2 + v3 + v4; | |
} | |
int plus3(int v1, int v2, int v3) { | |
return v1 + v2 + v3; | |
} | |
int main() { | |
printf("plus3 currying:%d\n", make_currying(std::function{plus3})(1, 2, 3)); | |
printf("plus3 currying:%d\n", make_currying(std::function{plus3})(1, 2)(3)); | |
printf("plus3 currying:%d\n", make_currying(std::function{plus3})(1)(2, 3)); | |
printf("plus3 currying:%d\n", make_currying(std::function{plus3})(1)(2)(3)); | |
printf("plus4 currying:%lf\n", make_currying(std::function{plus4})('\x01')(2)(3.1f)(4.2)); | |
printf("plus4 currying:%lf\n", make_currying(std::function{plus4})('\x01', 2)(3.1f)(4.2)); | |
printf("plus4 currying:%lf\n", make_currying(std::function{plus4})('\x01', 2)(3.1f, 4.2)); | |
printf("plus4 currying:%lf\n", make_currying(std::function{plus4})('\x01', 2, 3.1f)(4.2)); | |
printf("plus4 currying:%lf\n", make_currying(std::function{plus4})('\x01', 2, 3.1f, 4.2)); | |
std::function printv = []{puts("v!");}; | |
make_currying(printv)(); | |
make_currying(std::move(printv))(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
don't support
executor = executor(v)
maybe PIMPL idiom would help