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();
}
}
}
CodePudding user response:
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
CodePudding user response:
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.
CodePudding user response:
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 c 17) expansion.
In addition, using the ranges::views::iota
(Since c 20) 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)
CodePudding user response:
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();
}
}
}
}
CodePudding user response:
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;
}
CodePudding user response:
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()
.
CodePudding user response:
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