1

I am working on a code that can't be rewritten completely. Somewhere in the code I have this snippet

#define ITERATE_AND_CALL(vectorOfObjs, function)\
    do\
        for(auto & v : vectorOfObjs)\
        {\
            v.function();\
        }\
    while(false)\

later on in the code it is called like this in several places

class Foo {
public:
    void foo(){}
    int foo2(){ return 1;}
}
std::vector<Foo> vecOfFoos;
ITERATE_AND_CALL(vecOfFoos, foo)
ITERATE_AND_CALL(vecOfFoos, foo2)

Is there anyway to do this without macros?

Ashkan
  • 1,050
  • 15
  • 32

2 Answers2

3

The do/while(false) thing is a way to protect the macro from accidents when you use it in an if statement.

You can replace the macro with a nice function template:

template <typename Container, typename FPtr>
void ITERATE_AND_CALL(Container& container, FPtr callable)
{
   for (auto& v : container)
      (v.*callable)();
}

(live demo)

This is almost a drop-in replacement, except you can't just pass the name of a function… you have to use the address-of operator:

struct Foo
{
    void func() {}
};

int main()
{
    std::vector<Foo> v(3);
    ITERATE_AND_CALL(v, &Foo::func);
}

So &Foo::func rather than just func. There's no way around that.


If you can modify the callsites further, though, there are better ways of doing this that don't restrict you to function pointers and no-arg callbacks:

template <typename Container, typename Callable>
void IterateAndCall(Container& container, Callable func)
{
    for (auto& v : container)
       func(v);
}

Now your func can be a lambda or any other thing that binds whatever you like, e.g.

std::vector<int> vecOfFoos{1,2,3};
IterateAndCall(vecOfFoos, [](const int v) { std::cout << v; });

This is pretty much just a shorter, re-invented version of std::for_each. You should probably just use std::for_each if you get to this point… or simply write out the loop.

Asteroids With Wings
  • 17,071
  • 2
  • 21
  • 35
2

c++ Replace macro fuction that calls a method of an object
Is there anyway to do this without macros?

Looks like a job for std::for_each:

#include <algorithm>
#include <vector>
#include <iostream>

struct Foo {
    void foo() {
        std::cout << "a_func\n";
    }
};

#define ITERATE_AND_CALL(vector, function)\
    do\
        for(auto & v : vector)\
        {\
            v.function();\
        }\
    while(false) \

int main()
{
    std::vector<Foo> vecOfFoos(2);

    ITERATE_AND_CALL(vecOfFoos, foo);

    std::for_each(vecOfFoos.begin(), vecOfFoos.end(), [](auto& v) {
        v.foo();
    });
}

This produces 2 a_func outputs for ITERATE_AND_CALL and 2 for std::for_each.

a_func
a_func
a_func
a_func
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • *"and `;` should not be put there"* - Too strong a statement. It very well *may* be put there. It's just an empty statement. Besides, the OP (rightly) edited their question to make it more focused. This bit is irrelevant, and explained not quite as well as the duplicates explain it. – StoryTeller - Unslander Monica May 14 '20 at 12:21
  • @StoryTeller-UnslanderMonica Oups, didn't notice the edit of the question. _should not_ - well, it's not needed and some linters are tweaked to refuse it. :) Anyway, I'll remove that part. – Ted Lyngmo May 14 '20 at 12:23
  • This off course is an answer to the question I wrote here but I simplified and in the process lost the point. I will explain a bit here and if it makes sense update my question. Imagine that you have a vector of a vector of a map and then you loop through all of them and call the method `foo` of the object inside and process the response. Now assume that you do this several times. There will be a lot of std::for_each inside of std::for_each and all that is different is the method that is called. Any thought on this? – Ashkan May 14 '20 at 12:25
  • @Ashkan I don't have the full picture in my head but it could possibly be solved with a function template that takes a function as argument `template complex_nesting_of_for_eaches(Func f) { .... f(v); }`. You could then pass a lambda to it, just like in the for_each in my answer. – Ted Lyngmo May 14 '20 at 12:28