KeKe
KeKe

Reputation: 187

How to refactor nested for loop?

I have two similar functions. Both functions contain a nested for -loop. How I can combine these two functions to decrease duplicated code.

The only difference between funcA and funcB is funcB calls func_2 in the loop.

The two functions like below.

void funcA()
{
    for (int i = 0; i < size; i++)
    {
        for (int j = 0; j < size; j++)
        {
            func_1();
        }
    }
}

void funcB() 
{
    for (int i = 0; i < size; i++) 
    {
        for (int j = 0; j < size; j++)
        {
            func_1();
            func_2();
        }
    }
}

Upvotes: 4

Views: 743

Answers (7)

Karen Baghdasaryan
Karen Baghdasaryan

Reputation: 2271

You could have used variadic templates.

template<class ... FuncTypes>
void funcAB(FuncTypes... Funcs)
{
    for(int i = 0; i < size; ++i) {
        for(int j = 0; j < size; ++j) {
            (Funcs(), ...);
        }
    }
}

Here is how to call the function.

funcAB(&func_1); // if you want to only call func_1
funcAB(&func_1, &func_2) // if you want both to be called

Upvotes: 7

JeJo
JeJo

Reputation: 32732

You can replace it with one single template function. It takes variadic arguments of functions, and each function will be called by the fold expression (Since ) expansion. In addition, using the ranges::views::iota (Since ) you can combine the two for-loops to one.

Something like follows:

#include <ranges> // std::views::iota    

template<typename... Funcs> 
void funcAB(Funcs&&... funcs)
{
    for ([[maybe_unused]] int i : std::views::iota(0, size * size)) {
        (funcs(), ...);                
    }
}

You will call the funcAB:

funcAB(func1);        // for funcA() call
funcAB(func1, func2); // for funcB() call

(Live Demo)

Upvotes: 3

Stephan Lechner
Stephan Lechner

Reputation: 35154

Besides different ways of passing implementations as function arguments (e.g. through lambdas or function pointers) as mentioned in the other posts, you could think of inheritance and overriding for expressing the reuse of behaviour:

struct Base {
    int size = 2;
    void loop() {
      for(int i = 0; i < size; i++) {
        for(int j = 0; j < size; j++) {
            func();
        }
      }
    }
    virtual void func() = 0;
};

struct A : public Base {
    void func() override {
      cout << "for an A, call func1" << std::endl;
    }
};

struct B : public Base {
    void func() override {
      cout << "for a B, call func1 and func2" << std::endl;
    }
};

int main() {
    A a;
    B b;
    
    a.loop();
    b.loop();
}

Output:

for an A, call func1
for an A, call func1
for an A, call func1
for an A, call func1
for a B, call func1 and func2
for a B, call func1 and func2
for a B, call func1 and func2
for a B, call func1 and func2

Upvotes: 0

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122298

Perhaps I am taking it a little too far, but there is no apparent reason for the nested loops (neither func_1() nor func_2() depend on i or j). A straight forward way to pass a callable is the following:

 template <typename F>
 void func(F f) {
      for (int i=0; i < size*size; ++i) f();
 }

and then call either

 func([](){ func_1(); });
 func(&func_1);               // passing function pointer works as well

or

 func([](){ func_1(); func_2(); });

PS: There is a difference between the nested loops and the flat one when size*size can overflow or it is negative. Though passing the callable is orthogonal to that.

Upvotes: 5

Edblocker
Edblocker

Reputation: 430

Maybe create one function with a parameter, depending on that parameter call func_2(). You can also give that parameter a default value.

void funcA(bool callFunc2 = false) {
  for(int i = 0; i < size; i++) {
    for(int j = 0; j < size; j++) {
      func_1();

      if (callFunc2) {
         func_2();
      }
    }
  }
}

funcA() will run without calling func_2() and funcA(true) will run with the execution of func_2().

Upvotes: 0

RoQuOTriX
RoQuOTriX

Reputation: 3001

You can write a function which accepts a function as argument but without more information this is the only example I can give:

#include <iostream>

constexpr int size = 1;

void func(void (*f)()) 
{
  for(int i = 0; i < size; i++) 
  {
    for(int j = 0; j < size; j++) 
    {
      f();
    }
  }
}

void func_1(){ std::cout << "func_1" << std::endl; }

void func_2(){ std::cout << "func_2" << std::endl; }

void funcA()
{
    func_1();
}

void funcB() 
{
    func_1();
    func_2();
}

int main()
{
    func(funcA);
    func(funcB);

    return 0;
}

Upvotes: 0

the only difference between funcA and funcB is that funcB calls func_2(); then use a flag to control whether that method should be called or not

void funcC(bool shouldInvokeFunc2)
{
  for(int i = 0; i < size; i++)
  {
    for(int j = 0; j < size; j++) {
        func_1();
        if(shouldInvokeFunc2) // use the flag
        {
            func_2();
        }     
    }
  }
}

Upvotes: 1

Related Questions