0

I have a lot of lines of code like below:

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

for each of them, I want to be able to extract (i-0) and pass it to another function. like:

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

so I wrote two macro:

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

and call them like:

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

however, it does not give what I want, it expands to:

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

and MSVC compiler complains

    not enough actual parameters for macro 'SP_SETUP_POINT'

I searched quite a bit, according to https://gcc.gnu.org/onlinedocs/cpp/Argument-Prescan.html

Macro arguments are completely macro-expanded before they are substituted into a macro body, unless they are stringified or pasted with other tokens. After substitution, the entire macro body, including the substituted arguments, is scanned again for macros to be expanded. The result is that the arguments are scanned twice to expand macro calls in them

the argument are fully expanded, but the additional argument is not recognized. how it that ? any suggestion is appreciated.

2
  • Hint: Use your compiler's option to emit the preprocessed sourcecode to see what the expression evaluates to. Commented Dec 2, 2016 at 9:33
  • Yes, I tried. I got the code after preprocessed, but still dont know how to get desired macro expansion. Commented Dec 5, 2016 at 0:18

2 Answers 2

1

The reason, IIRC, is that GET_VERT inside of an actual parameter list gets only expanded after the enclosing macro call is scanned. So the PP first wants to see the ")" at the end of the parameter list of SP_SETUP_POINT. In this process it recognizes that it is one parameter short of the declared form. One level of indirection helps, but beware, macro programming in C is strange and suffers from early bad implementation choices that no one ever dared to correct due to massive compatibility debt.

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

The last form is better written as

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

because writing sg. like

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

will lead to invisible errors in the control flow.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for detailed anwser. I thought about it also, but no luck in this way. here is my version: #define SP_SETUP_POINT(...) SP_SETUP_POINT_IMPL(__VA_ARGS__) #define SP_SETUP_POINT_IMPL(_x, _y, _z) sp_setup_point(_x, _y); my_test_func(_z)
1

If I was going to tackle this, I think I'd start from the desired output:

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

You have 4 parameters: setup, vertex_buffer, expr, and stride, where expr is the i-0 parameter (which is itself a peculiar notation; how does that differ from i?). I might use those longer names in production-quality macros; I'm going to use short names a .. d here.

So, I'd design my macro from that starting point:

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

You could then invoke:

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

This would generate the code you desire, even in a context such as:

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

If you don't use the do { … } while (0) notation, then you have to use a comma operator to separate the function calls:

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

This evaluates to the return value from my_test_func(). There's no problem if you don't need to test the return value from sp_setup_point() — e.g. if it returns void anyway — and there's no problem if my_test_func() returns void.

You can also modify the macro to call MY_TEST_FUNC(c) and then conditionally define it:

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

The advantage of evaluating c in the 'do not call' case is that the compiler ensures that c remains valid even as the code changes. Do not under-estimate the benefit of that in long-lived code.

1 Comment

Thanks for another point of view. If I could not figure out the mistery of the macro. I will adopt this way, It needs much invasive change of original code, so I am not taking it as best solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.