0

I am trying to convert boolean values, which represent bits of a signal bus, into an integer. I am using the following construct:

#define B_TO_UINT1(b00)      (((uint32_t) b00 << 0))
#define B_TO_UINT2(b01, ...) (((uint32_t) b01 << 1) | B_TO_UINT1(__VA_ARGS__))
#define B_TO_UINT3(b02, ...) (((uint32_t) b02 << 2) | B_TO_UINT2(__VA_ARGS__))
#define B_TO_UINT4(b03, ...) (((uint32_t) b03 << 3) | B_TO_UINT3(__VA_ARGS__))
// ...

When using the macro cascade to convert a 1-, 2- and 3-bit buses, the first 2 are OK, but the 3-bit conversion gives an error:

cmd = B_TO_UINT1(1);          // line_1
cmd = B_TO_UINT2(1, 0);       // line_2
cmd = B_TO_UINT3(0, 1, 1);    // line_3

Build errors on line_3:

warning C4003: not enough actual parameters for macro 'B_TO_UINT1'

error C2059: syntax error : '<<'

So, looks like the __VA_ARGS__ part is not expanding correctly with the recursion. Is this really the case? If so, is there a workaround?

ysap
  • 7,723
  • 7
  • 59
  • 122
  • 1
    Take `bxx` things into parentheses. – Eugene Sh. Apr 28 '16 at 21:47
  • @EugeneSh. - thanks. Generally a good tip, but it does not solve this particular problem (I am getting other, `)` related error message). Anyway, in the actual code I cannot use parentheses from other reasons. – ysap Apr 28 '16 at 21:50
  • "*.. Anyway, in the actual code I cannot use parentheses from other reasons..*" In this case the code has some problem. Anyway, what message are you getting? – Eugene Sh. Apr 28 '16 at 21:51
  • BTW, can't reproduce: https://ideone.com/8BHOi4 – Eugene Sh. Apr 28 '16 at 21:54
  • OK, I think I got the cause - this seems to be an MSVC bug, and I actually commented on a question dealing with it just earlier...: http://stackoverflow.com/q/26464843/274579 – ysap Apr 28 '16 at 21:54
  • However, before making this observation a self-answer, I am waiting for confirmation on that from knowledgeable users. – ysap Apr 28 '16 at 21:55
  • 2
    Related: http://stackoverflow.com/questions/5134523/msvc-doesnt-expand-va-args-correctly – Eugene Sh. Apr 28 '16 at 21:57
  • I don't know what this macro does, but maybe change `B_TO_UINT2` to `(((uint32_t) b01 << 1) | B_TO_UINT1(b01))` – Barmak Shemirani Apr 28 '16 at 22:12
  • @BarmakShemirani - it is (supposed to be) doing a conversion from a collection of discrete bits to an integer, as described. Doing as you suggest will just move the problem one step up in the cascade. – ysap Apr 28 '16 at 22:17
  • @EugeneSh. - the actual code is more complex in that it concatenates more stuff to the arg name and calls a (C++) method. It is probably easy to wrap the parens in the right place, just not around the arg name itself. Anyway, this is not the problem. – ysap Apr 28 '16 at 22:20
  • Check if you can use C++14 (or is it C++11) binary literals, like `0b101010`. – Cheers and hth. - Alf Apr 28 '16 at 23:22
  • @Cheersandhth.-Alf - good to know 0bXX was formally added to the language. It was overlooked since K&R days... However, the actual code involves variables, not literals, so I am not sure how this will help. – ysap Apr 28 '16 at 23:42

1 Answers1

1

Apparently, MS VC++ behaves differently than expected with the recursive expansion of __VA_ARGS__ that includes multiple comma-separated arguments. When expanded in a nested macro, the the compiler treats the whole arg list as one argument.

Examples can also be seen here and here.

However, @Ise Wisteria suggested the EXPAND(x) macro as a workaround. Using this tip, I changed my code to:

#define EXPAND( x ) x
#define B_TO_UINT1(b00)      (((uint32_t) b00 << 0))
#define B_TO_UINT2(b01, ...) (((uint32_t) b01 << 1) | EXPAND( B_TO_UINT1(__VA_ARGS__)))
#define B_TO_UINT3(b02, ...) (((uint32_t) b02 << 2) | EXPAND( B_TO_UINT2(__VA_ARGS__)))
#define B_TO_UINT4(b03, ...) (((uint32_t) b03 << 3) | EXPAND( B_TO_UINT3(__VA_ARGS__)))
// ...

Now the build errors are eliminated.

Note: I am not explicitly saying "a bug", b/c there may be a place for understanding the standard the MS way, as described here.

Community
  • 1
  • 1
ysap
  • 7,723
  • 7
  • 59
  • 122