0

I have developed an algorithm to change control flags into an specific order. Think of it as a counter or a clock where you could define minimum, maximum, step, and so on. But every time I want to use it, I need to copy-paste several lines of code and change some lines in the middle.

I was wandering if there is a way of implementing this new flow control statement. My question could be also stated as "how do I write a function where I require a simple or a compound C++ statement as an argument"?

Another way of approaching my question is to figure out how could I convert the following code:

int a;
vector<int> av;
for(int i = 0; i < 10; i++)
{
    a = (i > a)? i : i*2;
    av.push_back(a);
}
for(int i = 20; i >= 10; i--)
{
    a = ((a+i)%2 == 1)? 0 : i/2;
    av.push_back(a*3);
}

into something close to this:

// definition
void another_for_loop(const int &a, const int &b, const int &inc_, [????] )
{
    inc = (inc_ == 0) ? 1 : inc_;
    if(inc > 0)
    {
        for(int i = min(a,b); i <= max(a,b); i += inc)
            [????];
    }
    else
    {
        for(int i = max(a,b); i >= min(a,b); i += inc)
            [????];
    }
}

// [...]
// inside main{}
int a;
vector<int> av;
another_for_loop(0, 9, 1, {a = (i > a)? i : i*2; av.push_back(a);});
another_for_loop(10, 20, -1, {a = ((a+i)%2 == 1)? 0 : i/2; av.push_back(a*3);});

Hope there is a clever way to do so... :-D

Even if there is no way, please, let me know.

iperetta
  • 607
  • 10
  • 19
  • 8
    You might want to read about [functor objects](http://www.cprogramming.com/tutorial/functors-function-objects-in-c++.html), [lambda expressions](http://en.cppreference.com/w/cpp/language/lambda) and see how the standard library handles things like these (see e.g. the [`std::transform`](http://en.cppreference.com/w/cpp/algorithm/transform) function). – Some programmer dude Jul 29 '15 at 11:20
  • 1
    `for(int i = (inc > 0) ? min(a,b) : max(a,b); (inc > 0) ? (i <= max(a,b)) : (i >= min(a,b)); i += inc)` – M.M Jul 29 '15 at 11:48
  • Unfortunately C++ is not Lisp, so no macros, you need to resort to "work arounds" the answers provide you. – Daniel Jour Jul 29 '15 at 13:57

2 Answers2

11

You may use functor and lambda:

// definition
template <typename F>
void another_for_loop(const int &a, const int &b, const int &inc_, F f)
{
    int inc = (inc_ == 0) ? 1 : inc_;
    if(inc > 0)
    {
        for(int i = min(a,b); i <= max(a,b); i += inc)
            f(i);
    }
    else
    {
        for(int i = max(a,b); i >= min(a,b); i += inc)
            f(i);
    }
}

And then

int a = 0;
vector<int> av;
another_for_loop(0, 9, 1, [&a, &av](int i){a = (i > a)? i : i*2; av.push_back(a);});
another_for_loop(10, 20, -1, [&a, &av](int i){a = ((a+i)%2 == 1)? 0 : i/2; av.push_back(a*3);});
MSalters
  • 173,980
  • 10
  • 155
  • 350
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • I'd pass `a,b,inc` as regular `int`. Not only is that more efficient by itself, it would avoid the need for a local modifiable copy of `inc`. On the other hand, copying some unknown `F` may be quite expensive. – MSalters Jul 29 '15 at 14:23
  • Wow! That is exactly what I was expecting to help me out of this! I've adapted it to my algorithm and it work flawless! :-D Is there also a way to include `(int i)` in the `typename F` ? I mean, this very argument will never change... – iperetta Jul 29 '15 at 17:22
  • 1
    @iperetta: You may use `std::function` instead of F – Jarod42 Jul 29 '15 at 17:32
  • @Jarod: Sorry for my poor last comment. I was trying to know if there is a way to avoid `(int i)` by the caller, as in `another_for_loop(0, 9, 1, [&a, &av]{a = (i > a)? i : i*2; av.push_back(a);});`. The argument will always be the same (in this simple case, `int i`). I tried to use `std::function` as you said to accomplish this, but I keep getting `error: could not convert ‘main(int, char**)::__lambda0{(* & a), (* & av)}’ from ‘main(int, char**)::__lambda0’ to ‘std::function’`... – iperetta Jul 29 '15 at 18:14
  • Some other idea? Should I give up and stay with `another_for_loop(0, 9, 1, [&a, &av]**(int i)**{a = (i > a)? i : i*2; av.push_back(a);});` ? – iperetta Jul 29 '15 at 18:14
  • @iperetta: `i` changes in the loop (and it is used in `f(i)` and in loop condition) and doesn't exist in call site so cannot be captured. – Jarod42 Jul 29 '15 at 22:35
  • `std::function` is more explicit of the wanted functor but don't change the syntax of the caller. – Jarod42 Jul 29 '15 at 22:36
1

The approach to solve this kind of problem is called strategy pattern see https://en.wikipedia.org/wiki/Strategy_pattern

  • 1
    No it's not, strategy pattern uses inherited classes of interfaces in the main class to define a behavior, possibly passed in the constructor. This is just passing a callback to a generic function. – stefaanv Jul 29 '15 at 11:47
  • Ok, the behavior of the class does not change, but using the same approach (as in strategy pattern) You can accomplish the same. And this approach is applicable to any object-oriented programming language not just one with nice features of lambdas or function pointers or callbacks or delegates or whatever you call them.. – AccessViolation Jul 29 '15 at 12:17
  • You might be referring to one of my previous answers: http://stackoverflow.com/a/3520629/104774, which shows a callback via interface. Even though it is similar to the strategy pattern, it is not the same because it serves another purpose, especially if the callback is passed as parameter of the function. But since I'm talking about purpose and you're talking about approach, it's not a sensible discussion. – stefaanv Jul 29 '15 at 12:47
  • Thank you for the insight, but I was trying to find something more like a loop-statement. The way explained by **Jarod42** and **MSalters** allows me to implement it as `inline` in my personal functions collection header and use it with any code in which I should need of, similarly to a regular loop-statement. – iperetta Jul 29 '15 at 17:33