0

I was wandering if there is a way to define a variadic macro that handles its last argument in a different way.

For example, can I define a macro that prints a custom separator after every argument except for the last one?

i.e. how to define MACRO:

MACRO(a, lot, of, bars)

In order to produce this output:

a|lot|of|bars

(EDIT: it was pointed out to me by the comments that the case of , as separator is trivial)

Zehanort
  • 458
  • 2
  • 10
  • @R..GitHubSTOPHELPINGICE my bad, I edited the original post, thank you. My question is about any separator. – Zehanort Jan 14 '20 at 02:20
  • For the new form of the question, there's nothing particularly special about the last slot; the difficulty is doing the transformation *at all*. – R.. GitHub STOP HELPING ICE Jan 14 '20 at 02:27
  • @jxh missed it from previous edit, I am really sorry – Zehanort Jan 14 '20 at 02:37
  • please clarify whether you mean expressons with bitwise-or as shown, or a string with that content – M.M Jan 14 '20 at 02:39
  • @M.M a string with that content, but does it make a difference given that macros have no such semantics (they just process raw text) ? – Zehanort Jan 14 '20 at 02:42
  • There is certainly a difference, `printf(a|lot|of|bars);` is not the same as `printf("a|lot|of|bars");` – M.M Jan 14 '20 at 02:43

2 Answers2

3

You can apply the macro argument counting trick. For up to five macro arguments (the original answer supported 4 macro arguments, so you can view the diff to see how to extend the macro for additional arguments):

#define MACRO(...) MACRO_X(__VA_ARGS__,5,4,3,2,1)(__VA_ARGS__)

#define MACRO_X(_5,_4,_3,_2,_1,X,...) MACRO_##X

#define MACRO_5(X,...) X|MACRO_4(__VA_ARGS__)
#define MACRO_4(X,...) X|MACRO_3(__VA_ARGS__)
#define MACRO_3(X,...) X|MACRO_2(__VA_ARGS__)
#define MACRO_2(X,...) X|MACRO_1(__VA_ARGS__)
#define MACRO_1(X,...) X

To create a string, you can use the "stringify" operator:

#define STR2(X) STR(X)
#define STR(X) #X

So:

puts(STR2(MACRO(a,lot,of,bars)));
jxh
  • 69,070
  • 8
  • 110
  • 193
  • I tried your answer with `cpp` and calls of `MACRO` from 1 to 4 arguments, but I am getting this error: `error: unterminated argument list invoking macro "PASTE"` – Zehanort Jan 14 '20 at 02:47
  • I had a typo, but there were a couple more things to add. Answer updated. – jxh Jan 14 '20 at 02:49
  • Technique is more or less described here: https://stackoverflow.com/q/11761703/315052 – jxh Jan 14 '20 at 02:51
  • https://tio.run/##fdBNC4JAEAbgu79iwcsuTILreioC6dApAo3Y22B@1IJp5HqqfrtpRukePM3y8rwDs8ninCStrcqkaNKMrGqdqsq5rC1LlZpcY1USysijtdMsV2VGdsEm3FPHcdjwREkRjwEG4TZCBB8EeMDBZeOYWdak35d8QAHoAXJAFySMVtq2NAs@/Qr5HAIx3T/VwtTenPZMzec0N7U7p92f/l8UHcJuCetnN8Zxn/a33xpd0w8bvjuGotJQ5XCK7zVjbNm@2jc – jxh Jan 14 '20 at 05:24
2

If the output does not need to be stringised, then you can produce an equivalent expression relatively simply:

#define MACRO(...) BIGMACRO(__VA_ARGS__,0,0,0,0,0,0,0,0)
#define BIGMACRO(a0,a1,a2,a3,a4,a5,a6,a7,...) a0|a1|a2|a3|a4|a5|a6|a7

so MACRO(a,lot,of,bars) expands to a|lot|of|bars|0|0|0|0 , which has the same type and value as a|lot|of|bars.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • 1
    I was able to convert your solution into a stringized version, but I still needed to do a counting trick... https://tio.run/##bY9Pa8MwDMXv/hQivlighdr9d1gZODuUHsYggZGbUZO0M3TJWNJTt8@eUTdbU6gOP5D0nngqHvZF0UtfF4djWcGq7UrfxO9PQvi6gw/2NSiEUy/LaufrCl7sc/qq4jhGSDbrS@fcm3U2XWfO0VAo/gz/Kp4Qa2JDPCWeEc@JF8RLCrckTyBVrPFMEzgNnAXOAxeBy@vt9BIkU9F3RFKOclw12aBx@U1OQxrVfcNZachpymmwSpmP1kZZggTBymQ01coiRJEQn8euVcPLdGg6ana05a8W8bH/6X8B – jxh Jan 14 '20 at 05:41
  • @jxh phat, maybe post another answer with that! – M.M Jan 14 '20 at 07:40