Higher-order functions with templates?

I have been programming in Clojure (functional language) for a while now and I have to use C ++ for the class. I am trying to use some of the features I enjoyed in Clojure (like higher order functions, lambdas, threading arguments, dynamic typing, etc.), but I ran into some brick walls.

First, I implemented a function get

that takes two arguments:

  • a collection (vector, list, hashmap, etc.) and
  • index

and returns the element at that index.

I also implemented a function conj

that takes two arguments:

  • a collection (vector, list, queue, etc.) and
  • item / object (any type of collection)

and returns the collection with the added item. In the case of vectors, this is pretty much the same as push_back

.

Now I want to be able to "forward" or "thread" arguments using higher order functions:

using std::vector;
vector<double> my_vec;

forward(my_vec,    // take "my_vec"
        conj(0.1), // "push" the value of 0.1 to the back of "my_vec"
        get(0),    // retrieve the first value
        inc);      // increment that value

      

This is the same as inc(get(conj(my_vec, 0.1), 0);

but a lot (!) More readable.

The return value forward

in this case should be 1.1.

To make the function forward

work, the arguments after the initial argument must be higher-order functions. That is, they should work similarly to the following:

template<typename Func>
Func get(int i){
  return [i](vector<boost::any> coll)
           -> boost::optional<boost::any> {
             return get(coll, i);
           };
}

      

However, the compiler cannot infer the return type of the lambda function. Also, my guess, based on my extremely limited experience with boost::any

, is that it won't be able to convert vector<double>

to vector<boost::any>

, despite seeming claims boost::any

that it can act as a replacement for just about any type.

I want the function to get

be generic, so I don't want to use boost::function<double (vector <double>, int)>

or any similar concrete typing.

Also, I use boost::optional

instead vector

to return null_ptr

if the index requested from get

is out of scope.

It's okay what my function looks like forward

:

template <typename T1>
optional<T1> forward (T1 expr1){
  return expr1;
}
template <typename T1, typename T2>
optional<T1> forward (T1 expr1, T2 expr2){
  return forward(expr2(expr1));
}
template <typename T1, typename T2, typename T3>
optional<T1> forward (T1 expr1, T2 expr2, T3 expr3){
  return forward(expr2(expr1), expr3);
}

      

etc.....

Any ideas on how to get this feature forward

to work?

I am also sure there is a more efficient way to implement it than doing arity overloading like I did.

+3


source to share


2 answers


Just an addition to Horstling's answers, it is actually forward

much simpler to implement:

template <typename Value>
Value forward(Value v) {
    return v;
}

template <typename Value, typename Func, typename... Funcs>
auto forward(Value v, Func f, Funcs... fs) -> decltype(forward(f(v), fs...)) {
    return forward(f(v), fs...);
}

      

And C ++ 14 sweetens things up a bit:



template <typename Value>
Value forward(Value v) {
    return v;
}

template <typename Value, typename Func, typename... Funcs>
decltype(auto) forward(Value v, Func f, Funcs... fs) {
    return forward(f(v), fs...);
}

      

Take a look at the complete code here (Coliru seems to support boost)

+2


source


What can I think of:

http://coliru.stacked-crooked.com/a/039905c5deff8dcf

Instead of lambdas, I used full-fledged functors in three different ways. It supports your example and doesn't require type erasure or such (like boost :: any or std :: function).

boost::optional<double> result = forward(my_vec, conj(0.1), get(0), inc);

      



In addition, the function is forward

implemented as a variational template allowing any number of functions.

The code is not politicized, but maybe it can provide some inspiration.

EDIT: Anton is absolutely right, my forward implementation was unnecessarily complicated. Now the link above points to its revised version of the code.

+2


source







All Articles