Макрос FOR_EACH с двумя или более параметрами в макросе вызова

Вот некоторый макрос-код C ++, который имитирует циклы for для исключения копирования и вставки кода.

#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("!");

А также я могу показать код для создания этого макроса (я нашел это здесь, на переполнении стека):

#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__)

Но у меня есть трудности с двумя аргументами для функции:
Мне нужно использовать func (x, y) на одной итерации этого макроса.
Вызывающий макрос должен выглядеть так:

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);

Если я добавлю «y» рядом с аргументом «x» в макросе, я получу х2 unsed замены вызова аргумента «что»:

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

Если у вас есть опыт в этом, помогите мне перекодировать этот макрос в макрос с двумя аргументами типа FOR_EACH.

3

Решение

Вопреки здравому смыслу, я собираюсь ответить на это (потому что это может быть полезно в нескольких редких случаях). Я изменил код, который вы разместили, поэтому он требует двух параметров (демонстрация):

#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);
}

Выход:

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

Вам нужно будет добавить дополнительные случаи, если вы хотите позвонить foo более 3 раз, которые я оставлю читателю в качестве упражнения.

Это может быть очищено, и, вероятно, следует дать другое имя, чем FOR_EACH (возможно что-то вроде FOR_EACH_2 чтобы указать, что он оперирует двумя аргументами за раз), что я оставлю читателю в качестве другого упражнения.

Причина, по которой я оставляю вещи в качестве упражнения для читателя, состоит в том, чтобы препятствовать использованию этого ответа (но не предотвращать его в целом). Если вы не можете позаботиться о том, чтобы очистить это и адаптировать его к вашим потребностям, тогда это на вас.

3

Другие решения

Обычно я выступаю за ответ на поставленный вопрос, предполагая, что решение было выбрано по определенной причине. Но у меня довольно твердое мнение об использовании макросов в C ++ в 21-м веке, поэтому я все равно публикую это как ответ.


Пожалуйста, пожалуйста, пожалуйста не злоупотребляйте макросами таким образом.

Если вы хотите вызвать функцию с парами аргументов, вот совершенно приемлемое, читаемое, отлаживаемое и поддерживаемое решение без макросов:

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

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

Вы также можете поместить их в один вектор, если вы предпочитаете:

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]);
}
2

Если вы сгруппируете аргументы макроса в скобках, каждая группа будет обрабатываться как один аргумент. Возможно, вы могли бы изменить макрос так, чтобы он мог быть вызван так:

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

Затем вставьте макрос func а также (x1, y1) все вместе.

1
По вопросам рекламы [email protected]