Skip to content

Instantly share code, notes, and snippets.

@pnck
Last active July 23, 2020 07:11
Show Gist options
  • Save pnck/b5de55e635397597eac34cea7e58ad69 to your computer and use it in GitHub Desktop.
Save pnck/b5de55e635397597eac34cea7e58ad69 to your computer and use it in GitHub Desktop.
convert std::function into a currying-able object
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))();
}
@pnck
Copy link
Author

pnck commented Feb 28, 2019

don't support executor = executor(v)

maybe PIMPL idiom would help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment