Reputation: 5
I'm playing with functional programming in C++20 and write something like that:
template <class OuterFn, class InnerFn, class... Args>
concept Composable =
std::invocable<OuterFn, std::invoke_result_t<InnerFn, Args...>>;
template <class OuterFn, class InnerFn>
constexpr auto
compose(OuterFn&& outer, InnerFn&& inner)
{
return [
out = std::forward<OuterFn>(outer),
in = std::forward<InnerFn>(inner)
]<class... Args>(Args && ... args)
requires Composable<OuterFn, InnerFn, Args...>
{
using std::invoke, std::forward;
return invoke(out, invoke(in, forward<Args>(args)...));
};
}
template <class OuterFn, class InnerFn>
constexpr auto
operator*(OuterFn&& outer, InnerFn&& inner)
{
using std::forward;
return compose(forward<OuterFn>(outer), forward<InnerFn>(inner));
}
template <class... RestFn>
constexpr auto
compose(RestFn&&... rest)
{
return (std::forward<RestFn>(rest) * ...);
}
This is work, but I want to refactor compose for two arguments by using std::bind* instead of lambda. So, I think that this:
using eq_t = std::equal_to<const std::string_view>;
constexpr auto eq_42 = std::bind_front(eq_t{}, "42");
is clearly than(especially if using function without overloaded operator for it):
constexpr auto eq_42 = [](const std::string_view sv){ return sv == "42"; };
Maybe You have an ideas how can I do this, or reasons why I can't do this?
The problem is how to retrieve arguments to inner function.
std::bind(outer, std::bind_front(inner));
I tried this for variadic template arguments(and million other variants) by analogy from
std::bind(outer, std::bind(inner, _1));
for one argument, but it doesn't work.
P.S. Sorry for my english)
Upvotes: 0
Views: 215
Reputation: 119847
You need a couple of utility classes.
#include <utility>
#include <tuple>
#include <functional>
template <typename Func>
struct wrap
{
wrap (Func func) : func{func} {}
template <typename ... T>
auto operator()(std::tuple<T...>&& args) {
return std::apply(func, std::forward<std::tuple<T...>>(args));
}
Func func;
};
template <typename Func>
struct unwrap
{
unwrap (Func func) : func{func} {}
template <typename ... T>
auto operator()(T&& ... args) {
return func(std::tuple(std::forward<T>(args)...));
}
Func func;
};
With this:
template <typename Func1, typename Func2>
auto compose (Func1 func1, Func2 func2)
{
using namespace std::placeholders;
return unwrap(std::bind(func1, std::bind(wrap(func2), _1)));
}
And a test:
int foo(int a, int b) { return a+b; }
int bar(int a) { return a*2; }
int main()
{
std::cout << compose(bar, foo)(3,4);
}
Conceptification and forwardification are left as an exercise for the reader.
But you should really forget about std::bind
and use lambdas.
Upvotes: 1
Reputation: 62531
You can't replace your template compose
with an implementation using std::bind
, because you would need to supply a variable number of placeholders to the bind
call. The best you can do is support a specific arity of arguments to the inner call, up to the (implementation-defined) limit of how many std::placeholders
there are.
Nor can you use std::bind_front
, because a function object in general isn't the value it returns when called.
So you have to have an intermediate function object, holding the inner and outer functions, and the simplest syntax for that is a lambda. If you really wanted to, you could wrap that in std::bind_front
, but there's no point.
template <class OuterFn, class InnerFn>
constexpr auto
compose(OuterFn&& outer, InnerFn&& inner)
{
using std::invoke, std::forward;
struct composer {
template <class Out, class In, class... Args>
auto operator()(Out&& out, In&& in, Args&&.. args) {
return invoke(out, invoke(in, forward<Args>(args)...));
}
};
// Fairly gratuitous bind_front, composer could have done the capturing of outer and inner itself
return std::bind_front(composer{}, forward(outer), forward(inner));
}
Upvotes: 0