2016-12-02 2 views
0

У меня есть много строк кода, как показано ниже:C макрос: Как использовать макрос для создания списка параметров другого макроса

 sp_setup_point(setup, get_vert(vertex_buffer, i-0, stride)); 

для каждого из них, я хочу, чтобы иметь возможность извлечь (i- 0) и передать его другой функции. как:

 sp_setup_point(setup, get_vert(vertex_buffer, i-0, stride)); 
    my_test_func(i-0); 

так что я написал два макроса:

 #define GET_VERT(_x, _y, _z) get_vert(_x, _y, _z) , _y 
     #define SP_SETUP_POINT(_x, _y, _z) sp_setup_point(_x, _y); my_test_func(_z); 

и называть их люблю:

 SP_SETUP_POINT(setup, GET_VERT(vertex_buffer, i-0, stride)); 

однако, это не дает то, что я хочу, это расширяется:

 sp_setup_point(setup, get_vert(vertex_buffer, i-0, stride), i-0); my_test_func(); 

и компилятор MSVC жалуются

not enough actual parameters for macro 'SP_SETUP_POINT' 

Я искал совсем немного, по https://gcc.gnu.org/onlinedocs/cpp/Argument-Prescan.html

Макро аргументов полностью макро вспененных, прежде чем они подставляются в тело макроса, если они не строковые или вставленные с другими маркерами. После подстановки весь макрос, включая замещенные аргументы, снова сканируется для расширения макросов. В результате аргументы сканируются дважды, чтобы развернуть в них макросы.

аргумент полностью расширен, но дополнительный аргумент не распознается. как это? любое предложение приветствуется.

+0

Подсказка: используйте опцию вашего компилятора, чтобы испускать предварительно обработанный исходный код, чтобы узнать, что оценивает выражение. – doynax

+0

Да, я попробовал. Я получил код после предварительной обработки, но до сих пор не знаю, как получить желаемое расширение макроса. – luckyyang

ответ

1

Причины, IIRC, является то, что GET_VERT внутри фактического список параметров получает только после того, как расширяется вмещающих Макровызы сканируются. Поэтому PP сначала хочет увидеть «)» в конце списка параметров SP_SETUP_POINT. В этом процессе он распознает, что это один параметр, который меньше объявленной формы. Один уровень косвенности помогает, но будьте осторожны, макро-программирование на C странно и страдает от ранних неудачных вариантов реализации, которые никто никогда не решался исправить из-за огромного долга по совместимости.

#define GET_VERT(_x, _y, _z) get_vert(_x, _y, _z) , _y 
#define SP_SETUP_POINT(_x, expandme) SP_SETUP_POINT_(_x,expandme) 
#define SP_SETUP_POINT_(_x,_y,_z) sp_setup_point(_x, _y); my_test_func(_z) 

Последняя форма лучше написана как

#define SP_SETUP_POINT_(_x,_y,_z) do{ sp_setup_point(_x, _y); my_test_func(_z); }while(0) 

, потому что писать Sg. например

if (a==1) SP_SETUP_POINT(setup, GET_VERT(vertex_buffer, i-0, stride)); 

приведет к невидимым ошибкам в потоке управления.

+1

Спасибо за подробный андерсер. Я тоже об этом подумал, но так не повезло. вот моя версия: '#define SP_SETUP_POINT (...) SP_SETUP_POINT_IMPL (__ va_args __)' '#define SP_SETUP_POINT_IMPL (_x, _y, _z) sp_setup_point (_x, _y); \t my_test_func (_z) ' – luckyyang

1

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

sp_setup_point(setup, get_vert(vertex_buffer, i-0, stride)); 
my_test_func(i-0); 

У вас есть 4 параметра: setup, vertex_buffer, expr и stride, где expr является параметром i-0 (что само по себе является своеобразным обозначением, как оно отличается от i?). Я мог бы использовать те более длинные имена в макросах производственного качества; Я буду использовать короткие имена a ..d здесь.

Так что, я бы разработать свой макрос с этой отправной точки:

#define SP_SETUP_POINT(a, b, c, d) \ 
    do { sp_setup_point((a), get_vert((b), (c), (d))); \ 
     my_test_func(c); } while (0) 

Вы могли бы вызвать:

SP_SETUP_POINT(setup, vertex_buffer, i-0, stride); 

Это будет генерировать код, который вы желаете, даже в контексте таких как :

if (x > y) 
    SP_SETUP_POINT(setup, vertex_buffer, i-0, stride); 
else 
    SP_SETUP_POINT(setup, vertex_buffer, i+2, stride); 

Если вы не используете do { … } while (0) обозначения, то вы должны использовать запятую ор Erator отделить вызовы функций:

#define SP_SETUP_POINT(a, b, c, d) \ 
    (sp_setup_point((a), get_vert((b), (c), (d))), \ 
    my_test_func(c)) 

Это имеет значение возвращаемого значения my_test_func(). Нет проблем, если вам не нужно проверять возвращаемое значение от sp_setup_point() - например. если он возвращает void в любом случае - и нет проблем, если my_test_func() возвращает void.

Вы также можете изменить макрос для вызова MY_TEST_FUNC(c), а затем условно определить его:

#ifdef CALL_MY_TEST_FUNCTION 
#define MY_TEST_FUNC(c) my_test_func(c) 
#else 
#define MY_TEST_FUNC(c) ((void)(c)) 
#endif 

Преимущество оценки c в случае «не называйте» является то, что компилятор гарантирует, что c остается в силе даже код меняется. Не недооценивайте преимущества этого в долговечном коде.

+1

Спасибо за другую точку зрения. Если бы я не мог понять мистерия макроса. Я буду принимать этот путь, ему нужно много инвазивного изменения исходного кода, поэтому я не принимаю его как лучшее решение. – luckyyang

Смежные вопросы