0

I'm looking at a macro, or more likely a combination of macros, that would achieve the following effect :

BRACKET(a)    =>  { a }
BRACKET(a, b) =>  { a }, { b }
BRACKET(a, b, c) => { a }, { b }, { c }

Sounds pretty simple ? Alas, I couldn't find any reasonable solution. There are possible heavyweight ones, based on counting the nb of arguments, and then creating one dedicated macro for each possible nb of arguments, which felt seriously overkill for the problem at hand (and hard to maintain for the poor successor). But I couldn't find any simpler solution so far.

Edit : Current solution we are trying to improve upon : Uses one macro per list size.

BRACKET1(a)    =>  { a }
BRACKET2(a, b) =>  { a }, { b }
BRACKET3(a, b, c) => { a }, { b }, { c }
etc.
Cyan
  • 13,248
  • 8
  • 43
  • 78
  • 1
    The reason why you didn't receive any answers and only had the question viewed 10 times is the lack of the C tag. I've added it now. See the important note in C11 tag usage: https://stackoverflow.com/tags/c11/info – Lundin May 10 '22 at 10:09
  • 1
    Regarding the question, is there a reason why each `{ }` can't always end with a `,`? In case they are only to be used in initializer lists etc then trailing comma is well-defined. – Lundin May 10 '22 at 10:11
  • @Lundin : I believe adding a comma systematically will be fine since the primary usage I can think of is an initializer list. – Cyan May 11 '22 at 18:04

1 Answers1

2

Variadic macros can rarely be easily used for generic programming purposes. There are some quite complex ways you could expand them up to a certain fixed maximum amount, but it might be better to look for alternative solutions instead. Especially in case you are risking to invent some project-specific macro language.

One such alternative solution would be to force the caller to work with specific variadic data sets instead of variadic inputs to some function-like macro.

For example if the caller can define a data set of any length and content through an "X-macro":

#define DATA(X) X(1) X(2) X(3) X(4) X(5)

Then we can apply all manner of specific behavior to that data set, for example:

#define BRACKET(x) {x},
...
DATA(BRACKET);

Which will expand to {1},{2},{3},{4},{5},.

With this method you can create all manner of similar macros:

#define COMMAS(x) x, -> 1,2,3,4,5,
#define STR_LITERAL(x) #x -> "1" "2" "3" "4" "5" -> "12345"

Or whatever you fancy.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • This solution works, aka it generates valid code that compiles. Unfortunately, the main objective is to make the code more readable, for future maintainers. And on this front, this solution is actually more difficult to read than current solution, which employs one macro per list size, because it now requires to create one new macro for each list, since list elements are present directly in the body of the `DATA(x)` macro. – Cyan May 11 '22 at 18:16
  • @Cyan: If readability and maintainability is the goal then macro tricks are going in exactly the wrong direction. Past a certain point, it's usually better to write a simple script to process some nice readable input into the desired C source, and incorporate the script into your build process. IMHO most preprocessor hacks fall in the category of "you can, but it doesn't mean you should". – Nate Eldredge May 11 '22 at 18:39
  • @Cyan "Unfortunately, the main objective is to make the code more readable, for future maintainers." Then macros is never the solution. Macros are used to reduce code repetition and centralize maintenance of the code. Often readability and maintainability go hand in hand, but in case of X macros they actually make code easier to maintain but harder to read. So if readability is the goal, you should be removing macros, not adding them. – Lundin May 12 '22 at 06:24