Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FOR_EACH macro with two or more params in call macro

Here is some C++ macro code that simulates for-loops to eliminate copy and pasted code.

#define SEMICOLON ;
#define LOL(x) print(x)
#define LOLZ(...) FOR_EACH(LOL, SEMICOLON, ##__VA_ARGS__)

LOLZ("hi", "my", "friend", "!");

// result
print("hi"); print("my"); print("friend"); print("!");

And also I can show the code to create this macros (I found this here on Stack Overflow):

#define EXPAND(x) x
#define FOR_EACH_1(what, delimiter, x, ...) what(x)
#define FOR_EACH_2(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_1(what, delimiter, __VA_ARGS__))
#define FOR_EACH_3(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_2(what, delimiter, __VA_ARGS__))
#define FOR_EACH_4(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_3(what, delimiter, __VA_ARGS__))
#define FOR_EACH_5(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_4(what, delimiter, __VA_ARGS__))
#define FOR_EACH_6(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_5(what, delimiter, __VA_ARGS__))
#define FOR_EACH_7(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_6(what, delimiter, __VA_ARGS__))
#define FOR_EACH_8(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_7(what, delimiter, __VA_ARGS__))
#define FOR_EACH_9(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_8(what, delimiter, __VA_ARGS__))
#define FOR_EACH_10(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_9(what, delimiter, __VA_ARGS__))
#define FOR_EACH_11(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_10(what, delimiter, __VA_ARGS__))
#define FOR_EACH_12(what, delimiter, x, ...)\
  what(x) delimiter\
  EXPAND(FOR_EACH_11(what, delimiter, __VA_ARGS__))
#define FOR_EACH_13(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_12(what, delimiter, __VA_ARGS__))
#define FOR_EACH_14(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_13(what, delimiter, __VA_ARGS__))
#define FOR_EACH_15(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_14(what, delimiter, __VA_ARGS__))
#define FOR_EACH_16(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_15(what, delimiter, __VA_ARGS__))

#define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__, FOR_EACH_RSEQ_N())
#define FOR_EACH_NARG_(...) EXPAND(FOR_EACH_ARG_N(__VA_ARGS__))
#define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, N, ...) N
#define FOR_EACH_RSEQ_N() 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
#define CONCATENATE(x,y) x##y
#define FOR_EACH_(N, what, delimiter, ...) EXPAND(CONCATENATE(FOR_EACH_, N)(what, delimiter, __VA_ARGS__))


#define FOR_EACH(what, delimiter, ...) FOR_EACH_(FOR_EACH_NARG(__VA_ARGS__), what, delimiter, __VA_ARGS__)

But I have difficulty with two arguments for the function: I need use func(x, y) at one iteration of this macro. Calling macro must be looks like:

MY_DUAL_FOREACH_LIKE_MACRO(
  x1, y1,
  x2, y2,
  x3, y3
)

// and I expect to get:
func(x1, y1); func(x2, y2); func(x3, y3); 

If I add "y" near the "x" argument at macro I will got x2 unsed substitutions of calling "what" argument:

func(x1, y1); func(x2, y2); func(x3, y3); func(, ); func(, ); func(, ); 

If you have experience in this, please help me recode this macro to dual arguments type FOR_EACH macro.

like image 430
Artem Selivanov Avatar asked Mar 12 '23 19:03

Artem Selivanov


2 Answers

Against my better judgement, I'm going to answer this (because it can be useful in a few rare cases). I modified the code you posted so it requires two parameters (demo):

#include <iostream>

#define EXPAND(x) x
#define FOR_EACH_2(what, delimiter, x, y) what((x), (y))
#define FOR_EACH_4(what, delimiter, x, y, ...)\
  what((x), (y)) delimiter \
  EXPAND(FOR_EACH_2(what, delimiter, __VA_ARGS__))
#define FOR_EACH_6(what, delimiter, x, y, ...)\
  what((x), (y)) delimiter \
  EXPAND(FOR_EACH_4(what, delimiter, __VA_ARGS__))

#define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__, FOR_EACH_RSEQ_N())
#define FOR_EACH_NARG_(...) EXPAND(FOR_EACH_ARG_N(__VA_ARGS__))
#define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, N, ...) N
#define FOR_EACH_RSEQ_N() 6, 5, 4, 3, 2, 1, 0
#define CONCATENATE(x,y) x##y
#define FOR_EACH_(N, what, delimiter, ...) EXPAND(CONCATENATE(FOR_EACH_, N)(what, delimiter, __VA_ARGS__))

#define FOR_EACH(what, delimiter, ...) FOR_EACH_(FOR_EACH_NARG(__VA_ARGS__), what, delimiter, __VA_ARGS__)

void foo(int x, float y) {
    std::cout << "foo(" << x << ", " << y << ")\n";
}

int main() {
    FOR_EACH(foo, ;, 1, 3.14, 2, 1.41, 3, 1.73);
}

Output is:

foo(1, 3.14)
foo(2, 1.41)
foo(3, 1.73)

You'll have to add additional cases if you want to call foo more than 3 times, which I'll leave as an exercise to the reader.

This can be cleaned up, and should probably be given a different name than FOR_EACH (perhaps something like FOR_EACH_2 to indicate it operates on 2 arguments at a time), which I'll leave as another exercise to the reader.

The reason I'm leaving things as an exercise to the reader is to discourage use of this answer (but not altogether prevent it). If you can't be bothered to clean this up and tailor it to your needs, well then that's on you.

like image 59
Cornstalks Avatar answered Mar 20 '23 11:03

Cornstalks


Usually, I am in favor of answering the question as stated, assuming that the solution was chosen for a reason. But I have quite a strong opinion on the use of macros in C++ in the 21st century, so I am posting this as an answer anyway.


Please, please, please do not abuse macros this way.

It you want to call a function with pairs of arguments, here is a perfectly acceptable, readable, debuggable, maintainable solution without macros:

std::vector<std::pair<double, double>> values = {
    { x1, y1 },
    { x2, y2 },
    { x3, y3 }
};

for(auto& pair : values)
{
    f(pair.first, pair.second);
}

You could also put them in one vector, if you prefer:

std::vector<double> values = {
    x1, y1,
    x2, y2,
    x3, y3
};

for(int i = 0; i < values.size(); i += 2)
{
    f(values[i], values[i + 1]);
}
like image 23
CompuChip Avatar answered Mar 20 '23 11:03

CompuChip