4

I have the following lines of code, created by a database export program:

typedef struct _s8_VARB
{
    unsigned char _byte[8];
} s8_VARB;

const s8_VARB varb00[] = {
    mMM(1,25,22,12,0,0,0,0,27)
    mMM(0,1,29,12,0,0,0,0,21)
    mMM(1,1,36,12,0,0,0,0,22)
}

The mMM is a macro that I want to define with a functionality that will produce the following data during compilation:

const s8_VARB varb00[] = {
    1,25,22,12,0,0,0,0,27,
    1,1,36,12,0,0,0,0,22,
}

So it basically should check the 1st parameter. If it is 0, that complete line should omitted. If it is 1, all the parameters (except the 1st) should be 'put on the line', ending with a comma.

What I have tried is this:

#define COMMA ,
#define mMM(cond, a,b,c,d,e,f,g,h)  (cond) ? (a),(b),(c),(d),(e),(f),(g),(h) COMMA :

But this is not working. Not even compiling, as gcc complains:

error: expected expression before ':' token

How should this macro definition should look like? Is this possible at all in C?

1
  • 1
    The way you have tried to do this makes cond to be evaluated at run time, not at compile time. The ? : operator cannot be used for macro expansion. Commented Mar 19, 2015 at 23:57

2 Answers 2

6

You can initialize an array with constant data only. A conditional statement is by definition not constant (even if it's known to the compiler at compile time).

Instead you could do it like this:

#define _mMM0(...)
#define _mMM1(...)  {__VA_ARGS__},
#define mMM(cond, ...) _mMM##cond(__VA_ARGS__)

const unsigned char varb00[][8] = {
      mMM(1,25,22,12,0,0,0,0,27)
      mMM(0,1,29,12,0,0,0,0,21)
      mMM(1,1,36,12,0,0,0,0,22)
};

I removed the struct and replaced it with its only member directly. In case C99 is not available, you can name every parameter as you used to do.

7
  • This won't compile because the last comma is not allowed.
    – user0815
    Commented Mar 20, 2015 at 0:11
  • @user0815 Good point, the trailing comma is a C99 addition, especially made for such use cases. I edited my answer.
    – a3f
    Commented Mar 20, 2015 at 0:15
  • 3
    @user0815 No, I was wrong! :-) C99 added the trailing comma for enumerator-list, C89 already had the trailing comma for initializer-list. Editing my answer again.
    – a3f
    Commented Mar 20, 2015 at 0:22
  • 1
    Got curious and dug around a bit. Seems to go back to at least 1985: lysator.liu.se/c/ANSI-C-grammar-y.html#initializer. I wonder if the pre-ANSI edition of K&R mentions it...
    – Ulfalizer
    Commented Mar 20, 2015 at 1:01
  • 1
    Yes, K&R 1st Edition does document trailing commas in initializer lists. To be precise, on p198, in section 8.6 of Appendix A (which is the C Reference Manual), it explicitly documents a trailing comma in the initializer specification: initializer : = expression | = { initializer-list } | = { initializer-list , } Commented Mar 20, 2015 at 1:34
4

Here's a hackish solution. If the number of arguments is fixed and you can't use C99+, then you could list them explicitly instead of using ... and __VA_ARGS__.

#define mMM(x, ...) mMM##x(__VA_ARGS__)
#define mMM0(...)
#define mMM1(...) __VA_ARGS__,

The ## operator pastes the token mMM and the x argument together to form a new macro name -- either mMM0 or mMM1 -- which is then called with the remaining arguments. mMM0() in turn expands to nothing, and mMM1() expands to its arguments.

(The extra trailing comma after the last element won't be a problem by the way. int a[] = { 1, 2, 3, } is explicitly allowed syntax in C.)

As a side note, invoking a macro like

#define m(x) (x) ? 1 : 2

using e.g. m(0) will simply expand it to (0) ? 1 : 2. The ternary operator will not be handled in the preprocessor.

4
  • You miss the commas at the end and however the last comma becomes a problem.
    – user0815
    Commented Mar 20, 2015 at 0:11
  • Thanks for clarifying the comma issue. Didn't know that so far.
    – user0815
    Commented Mar 20, 2015 at 0:24
  • @user0815: I hadn't realized it was a C99 thing, so I learned something too. :)
    – Ulfalizer
    Commented Mar 20, 2015 at 0:30
  • 1
    Oh, looks like it might've been correct after all, judging from the comments on a3f's answer. Might've just been an enum change. :P
    – Ulfalizer
    Commented Mar 20, 2015 at 0:31

Not the answer you're looking for? Browse other questions tagged or ask your own question.