3

I would like to ask if it's possible to define for each algorithm (like in STL) that would take multiple functions as input arguments and evaluate them in left to right order?

template <typename Iterator, typename ... Args>
void for_each(Iterator begin, Iterator end, Args ... args) {
// apply functions passed in Args... to range [begin,end)
}

How would I access those functions passed by Args? Is it possible only with some template recursion?

Alexander Bily
  • 925
  • 5
  • 18
  • I would use the old fashioned `for` loop which would be quicker to write than posting to StackOverflow waiting for an answer. Try it. – Thomas Matthews Oct 30 '15 at 19:40

3 Answers3

2

You can use something like this:

#include <iostream>
#include <utility>
#include <algorithm>

template <typename Iterator, typename F1>
void for_each(Iterator begin, Iterator end, F1 f1)
{
   std::for_each(begin, end, f1);
}

template <typename Iterator, typename F1, typename... Fun>
void for_each(Iterator begin, Iterator end, F1 f1, Fun... fs)
{
   std::for_each(begin, end, f1);
   for_each(begin, end, fs...);
}

int main()
{
    std::array<int, 5> a =  {1,2,3,4,5};

    auto f1 = [](int i){std::cout << "f1: " << i << " ";};
    auto f2 = [](int i){std::cout << "f2: " << i << " ";};

    for_each(a.begin(), a.end(), f1, f2);
}

output:

f1: 1 f1: 2 f1: 3 f1: 4 f1: 5 f2: 1 f2: 2 f2: 3 f2: 4 f2: 5 

live example

m.s.
  • 16,063
  • 7
  • 53
  • 88
  • 1
    No need to recurse. `int [] { (std::for_each(begin, end, args), 0) ... };` would do the job in just ONE line! – Nawaz Oct 30 '15 at 20:28
  • That array should have a name, shouldn't it? – LogicStuff Oct 30 '15 at 20:41
  • @LogicStuff: Not necessarily, as you'll ignore it anyway, so better dont give it a name to begin with; that also avoids compiler warning! – Nawaz Oct 30 '15 at 20:42
  • @Nawaz thanks for the hint; I once read that in another SO question/answer where this idea was explained in detail, maybe you can point me to that? – m.s. Oct 31 '15 at 12:17
  • 1
    @LogicStuff: It has nothing to do with *"array should have a name"*, but rather the syntax `int [] {...}` is not allowed *(I was suspecting this when I posted my first comment, but then I thought the readers would figure out themselves how to fix it)*. **So a typedef would do the job**. [See this](http://coliru.stacked-crooked.com/a/64117328dbbc0c37). – Nawaz Oct 31 '15 at 12:40
1

You don't have to do some special template trickery for this, just define a recursion like below:

template <typename Iterator, typename F>
void recurse(Iterator first, Iterator last, F f) {
  if(first != last) {
    f(*(first++));
  }   
}

template <typename Iterator, typename F, typename ...Args>
void recurse(Iterator first, Iterator last, F f, Args ...args) {
  if(first != last) {
    f(*(first++));
    recurse(first, last, args...);
  }   
}

template <typename Iterator, typename ...Args>
void variadic_for_each(Iterator first, Iterator last, Args ...args) {
  recurse(first, last, args...); 
}

LIVE DEMO

101010
  • 41,839
  • 11
  • 94
  • 168
  • 1
    How did you know that OP wants only to apply only one function at a time and not every function on every element? That information seems to be missing and more intuitive is the second one. – LogicStuff Oct 30 '15 at 20:09
  • 1
    @LogicStuff I knew it because Mercury was retrograde, Jupiter was in Pisces and the moon was full :PPPPPP In one word intution plus doing the other thing after defining the recursion is smooth sailing. – 101010 Oct 30 '15 at 20:16
1

I wrote something more general – some pseudocode showing what can it do:

auto funcs = mk<TupleOfFunctions>(f, g, h); // f, g, h -- callables
// mk is my helper function, I defined it in # Usage # section
funcs(arg) == h(g(f(arg))); // similiar to pipe in shell
                            // echo arg | f | g | h

In plain english, it's template of class. It's constructor that takes any amount of callables. Calling it's instance will return argument transformed by every function which constructor received.
And, because it's callable, you should be able to pass it to for_each.

Code

#include <utility>
// 1 //
template <typename T, typename U>
struct PairOfFunctions: std::pair<T, U> {
    using std::pair<T, U>::pair;

    template <typename... Args>
    auto operator() (Args&&... args) {
        return std::pair<T, U>::second(std::pair<T, U>::first(args...));
    }
};


template<typename...>
struct TupleOfFunctions;
// 2 //
template<typename T, typename... U>
struct TupleOfFunctions<T, U...>: PairOfFunctions<T, TupleOfFunctions<U...> >{
    using PairOfFunctions<T, TupleOfFunctions<U...> >::PairOfFunctions;
    TupleOfFunctions(T t, U... u):
        PairOfFunctions<T, TupleOfFunctions<U...> >(
            t,
            TupleOfFunctions<U...>(u...)
        )
        {}
};
// 3 //
template<>
struct TupleOfFunctions<>{
    template <typename T>
    T operator() (T t) { // probably not optimal, too lazy to overload
        return t;
    }
};

Some explanation

  1. PairOfFunctions – subclass of pair:

    mk<PairOfFunctions>(f, g)(arg) == g(f(arg));
    
  2. TupleOfFunctions – Generalization of PairOfFunctions, takes one or more callables.

  3. TupleOfFunctions<>: special case – takes no functions, returns copy of argument.

Usage

My example is dumb, feel free to replace it.

// My universal helper
template<template <typename...> class T, typename... Args>
auto mk(Args... args){
    return T<Args...>{args...};
}

int main()
{
    auto a = mk<TupleOfFunctions>(
        [](int a) {return a*2;},
        [](int a) {return a + 10;},
        [](int a) {return a / 2;}
    );
    std::cout << a(4) << '\n'; // (4 * 2 + 10) / 2
}

See it working online

GingerPlusPlus
  • 5,336
  • 1
  • 29
  • 52