1

The commas in __VA_ARGS__ leads to wrong expansion of macro definition, like this

My_Stu_t my_stu = {

    .member2 = {1},
    3}( member3, {2,3} )
};

I hope the correct result should be like this

My_Stu_t my_stu = {

    .member1 = {1},
    .member3 = {2,3},
};

I know why this error is generated, but I don't know how to solve it, please help, thanks.

here is my code: test.c

typedef struct {
    int member1[1];
    int member2[1];
    int member3[2];
} My_Stu_t;

#define MEMBER_AUX( a, b, c, ... )  c
#define MEMBER_WITHOUT_DEFAULT_VALUE( Name, ... )
#define MEMBER_WITH_DEFAULT_VALUE( Name, ... )  .Name = __VA_ARGS__,

#define MEMBER( Name, ... )  MEMBER_AUX( , ##__VA_ARGS__, MEMBER_WITH_DEFAULT_VALUE, MEMBER_WITHOUT_DEFAULT_VALUE )( Name, __VA_ARGS__ )
#define STD_MEMBER( ArrSize, Name, ... )  MEMBER( Name, ##__VA_ARGS__ )

My_Stu_t my_stu = {
    STD_MEMBER( 1, member1)
    STD_MEMBER( 1, member2, {1} )
    STD_MEMBER( 2, member3, {2,3} )
};

preprocess output: gcc test.c -E

My_Stu_t my_stu = {

    .member2 = {1},
    3}( member3, {2,3} )
};
yyd
  • 33
  • 5
  • 1
    Macros don't know anything about nesting of C expressions. It's very simple text replacement, there's no way to have commas inside a parameter. – Barmar Jun 30 '23 at 16:41
  • 1
    This isn't specific to variadic macros. If you have a macro like `#define MYMAC(x, y)` you can't call it like `MYMAC(foo, {bar, baz})` – Barmar Jun 30 '23 at 16:42
  • You can use `#define X 2,3` / `STD_MEMBER(2, member3, X)` to work around this. – Eric Postpischil Jun 30 '23 at 16:49
  • Also see: https://stackoverflow.com/a/24793828/1708801 – Shafik Yaghmour Jun 30 '23 at 16:50
  • @Barmar: There are ways to have commas inside a parameter. Besides my secondary macro above, commas inside parentheses are treated as internal to the argument, not an argument separator. – Eric Postpischil Jun 30 '23 at 16:51
  • @Eric,I miss you so much and the answer you gave me 2 months ago was very helpful. I'll think carefully about your solution. – yyd Jun 30 '23 at 16:55
  • @EricPostpischil: C11 standard [§6.10.3 Macro replacement ¶10](http://port70.net/~nsz/c/c11/n1570.html#6.10.3p10) lists one alternative as: ``# define identifier lparen ... ) replacement-list new-line`` which means it allows variable argument macros with no named arguments. C99 contained the same production. You can't have a variable argument _function_ without a named parameter; you can have a variable argument _macro_ without a named parameter. – Jonathan Leffler Jun 30 '23 at 17:19
  • 1
    I do not understand, what has happened to `member2` in the correct result? Please post what result do you want to actually get? Why not just _write_ the correct result, why all the macros? Also, subjective, but I have to, I just have to, mix of pascal and camel case looks bad to me. – KamilCuk Jun 30 '23 at 17:21
  • @JonathanLeffler: Thanks, my mistake, deleted. – Eric Postpischil Jun 30 '23 at 17:51
  • @EricPostpischil,so does this mean that it can be solved like this,`#define List(...) __VA_ARGS__` – yyd Jun 30 '23 at 17:56
  • @molly: That may work for the initial macro invocation, but you have some token pasting issue (`##`) I have not looked at yet. – Eric Postpischil Jun 30 '23 at 18:23
  • @molly: yes, you can write a macro `#define MACRO(...) snprintf(buffer, sizeof(buffer), __VA_ARGS__)`, for example. It might even work. – Jonathan Leffler Jun 30 '23 at 18:26
  • @EricPostpischil: you mean `##__VA_ARGS__` ? – yyd Jun 30 '23 at 18:36

2 Answers2

2

Let's drive into the modern age.

#define STD_MEMBER(arrsize, name, ...)  __VA_OPT__(.name = __VA_ARGS__,)
My_Stu_t my_stu = {
    STD_MEMBER(1, member1)
    STD_MEMBER(1, member2, {1})
    STD_MEMBER(2, member3, {2,3})
};
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • 2
    This needs more explanation than "Let's drive into the modern age". Little details like "This becomes standard in C23, but was supported by C++20 previously" would be sensible extra information. (I believe I'm correct about C++20 — I'm tolerably certain that C++ and C will both support `__VA_OPT__` after C23 and C++23 are both released, but I think it was a case of C catching up with C++.). It might already be available in GCC and Clang — that should be tracked down too, ideally. Also, an explanation of what it does, rather than just a demonstration would help. – Jonathan Leffler Jun 30 '23 at 17:45
  • `__VA_OPT__` is really useful – yyd Jun 30 '23 at 17:46
0

solution about #define List( ... ) __VA_ARGS__

typedef struct {
    int member1[1];
    int member2[1];
    int member3[2];
} My_Stu_t;


#define MEMBER_AUX( a, b, c, ... )                  c
#define MEMBER_WITHOUT_DEFAULT_VALUE( Name, ... )
#define MEMBER_WITH_DEFAULT_VALUE( Name, ... )      .Name = __VA_ARGS__,
#define MEMBER( Name, ... )                         MEMBER_AUX( , ##__VA_ARGS__, MEMBER_WITH_DEFAULT_VALUE, MEMBER_WITHOUT_DEFAULT_VALUE )( Name, __VA_ARGS__ )


#define List( ... ) __VA_ARGS__
#define STD_MEMBER( ArrSize, Name, ... )            MEMBER( Name, ##__VA_ARGS__ )

My_Stu_t my_stu = {
    STD_MEMBER( 1, member1)
    STD_MEMBER( 1, member2, List( { 1 } ) )
    STD_MEMBER( 2, member3, List( { 2, 3 } ) )
};

preprocess output: gcc test.c -E

My_Stu_t my_stu = {

    .member2 = { 1 },
    .member3 = { 2, 3 },
};
yyd
  • 33
  • 5
  • The `##__VA_ARGS__` technique works but is specific to GCC (and Clang emulating GCC). Other compilers won't support it. – Jonathan Leffler Jun 30 '23 at 17:43
  • @JonathanLeffler,It is true that there is a compatibility problem, is there any other way to solve it? – yyd Jun 30 '23 at 17:50
  • Yes, no, maybe. Yes, use `__VA_OPT__` in C23 or with C23 support (or in C++20 or with C++20 support). No, using pure C18, C11, C99, there isn't a way around it. Maybe — it depends on your C compiler. It is worth noting the limitations of an answer. It helps those who only need to work with GCC; it warns those who don't use GCC. – Jonathan Leffler Jun 30 '23 at 18:24
  • See also the GCC 13.1.0 C Preprocessor documentation [§3.6 Variadic macros](https://gcc.gnu.org/onlinedocs/gcc-13.1.0/cpp/Variadic-Macros.html). It discusses both `__VA_OPT__` and `## __VA_ARGS__` (for both C and C++). – Jonathan Leffler Jun 30 '23 at 18:30
  • @JonathanLeffler: I see, Use GCC and drive into the modern age. Thank you, and have a good day. – yyd Jun 30 '23 at 18:50