4

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.

Community
  • 1
  • 1
Artem Selivanov
  • 1,867
  • 1
  • 27
  • 45
  • 3
    I would _really_ not recommend doing this. What problem are you trying to solve? Can you solve it by representing your data in a structure and then iterating over it? – TartanLlama Mar 24 '16 at 09:50
  • 4
    The macros are evil. I wish i never debug such code. Btw. this "dual" iteration is called zip iterator, can be found in boost library. – Radek Mar 24 '16 at 09:53
  • 1
    "I found this on stack overflow at one of answers" please link to the source. – VLL Mar 24 '16 at 09:54
  • 4
    Why use macros when you have the standard *function* [`std::for_each`](http://en.cppreference.com/w/cpp/algorithm/for_each), or [range `for` loops](http://en.cppreference.com/w/cpp/language/range-for)? – Some programmer dude Mar 24 '16 at 09:56
  • 3
    "This is cool thing that eliminates the copy and paste code." For this purpose we have functions!!! – Radek Mar 24 '16 at 09:57
  • 2
    @Ville-ValtteriTiittanen you mean, so we can downvote that topic into oblivion so that nobody would ever again get the idea that such macro abuse is acceptable? :) – CompuChip Mar 24 '16 at 10:00
  • 1
    The underlying technical question, as I perceive it, seems to be OK. I think it's the stated motivation that puts people off. The main technical problem is to get things to work with Visual C++. – Cheers and hth. - Alf Mar 24 '16 at 10:03
  • 2
    in C++ we have `inline` function for avoiding those nasty macros and still have ability to debug – phuclv Mar 24 '16 at 10:12
  • 4
    No, this is most definitely not cool. – n. m. could be an AI Mar 24 '16 at 10:22
  • Your code (example) is also perfectly fitted for std::accumulate or folds ... There was recently a very nice article about it: https://ngathanasiou.wordpress.com/2015/12/15/182/ ... – CppChris Mar 24 '16 at 10:26

3 Answers3

4

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.

Cornstalks
  • 37,137
  • 18
  • 79
  • 144
2

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]);
}
CompuChip
  • 9,143
  • 4
  • 24
  • 48
  • 1
    While I dislike the macro approach, one advantage is that it doesn't have to allocate/deallocate any memory. – Cornstalks Mar 24 '16 at 10:08
  • Thank you for your answer, and I understood about abusing macros. I need to automatize some processes. This is not just calling functions. This is types definition and method declaration too. Framework where I work this is good reason to use this. – Artem Selivanov Mar 24 '16 at 10:13
  • @Broly say like that, it's not macro you need, but template – Garf365 Mar 24 '16 at 10:14
  • 3
    @Cornstalks: You may replace `std::vector` by `std::initializer_list` or `array` to avoid allocations. – Jarod42 Mar 24 '16 at 10:20
  • @Broly What you are doing is an useful technique in C (and in C++03 to some extent), but in recent C++ versions macros have become useless because of templates. – VLL Mar 24 '16 at 10:23
  • 1
    @Jarod42: `std::initializer_list` doesn't work with move-only types, and `std::array` requires the arguments to be homogenous. – Cornstalks Mar 24 '16 at 10:25
1

If you group your macro arguments with parentheses, each group will be handled as one argument. You could probably modify the macro so that it can be called like this:

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

Then have the macro paste func and (x1, y1) together.

VLL
  • 9,634
  • 1
  • 29
  • 54