Polymer Polymer - 1 month ago 7
C++ Question

Defining interfaces for functions that only accept functions

Define (f + g) to mean (f + g)(x) := f(x) + g(x). Conventional matrix addition is consistent with this definition.

Here is a naive implementation

template<typename Funcf, typename Funcg>
auto operator+(Funcf f, Funcg g){
return [f, g](auto x){return f(x) + g(x);};
}


This fails because
operator+
only consumes user defined types. The next attempt gives

template<typename R, typename I>
auto operator+(std::function<R(I)> f, std::function<R(I)> g){
return [f, g](auto x){return f(x) + g(x);};
}


This works and it doesn't litter the namespace. However it litters indirections, and the call-site is ugly
auto added = std::function<int(int)>{f} + std::function<int(int)>{g};
.

If the first
operator+
was allowed(or was renamed to add), the call site would be nicer, and the functions would be inlined. But it attempts to match against everything, which seems brittle.

Is it possible to define a template interface that specifies the inputs are functions, still inlines them, but doesn't pollute the namespace with an overly generic name?

In other words, is there a compile time version of
std::function
with a more convenient call site? I strongly suspect the answer is no. And if the answer is no, is there a compromise between the above two extremes?

Or option three, am I thinking about this the wrong way? How would you model (f + g) in c++?

Answer

Operators only works for user defined types. Then let's make your function be!

We can define a type that is basically a lambda, but with a user defined type name:

template<typename F>
struct addable_lambda_impl : F {
    template<typename T>
    addable_lambda_impl(T f) : F{std::move(f)} {}

    using F::operator();
};

template<typename F>
addable_lambda_impl<F> addable_lambda(F f) {
    return addable_lambda_impl<F>{std::move(f)};
}

This is a class that will extends any type and will use it's operator() function.

Now, you can use the addable_lambda function:

auto lambda = addable_lambda([](){});

Implementing your operator is easier too:

template<typename F, typename G>
auto operator+(addable_lambda_impl<F> f, addable_lambda_impl<G> g){
  return addable_lambda([f, g](auto x){ return f(x) + g(x); });
}

It's not perfect, but slightly less ugly. Plus, you don't suffers the overhead that std::function adds.

Comments