0

I'm trying to work with some existing code and I came across this code that I don't quite understand how it works:

char *c_token = R(
{
  "1":"abcdef",
  "2":"ghijkl",
  "3":"mnopqr",
  "4":"stuvwx"
});

Where R() is defined as:

#define R(...) #__VA_ARGS__

What does the c_token buffer look like after this? Is there any advantage to doing this over just assigning a map<string,string>? I am trying to read the code but I honestly don't understand what this is doing.

Assuming this was used to pass this map in, how would you translate this back into a map on the other end?

rich
  • 9
  • 1
  • 1
    So why not try it? You sure tried to compile the code and print `c_token`. So what was the result? Can't you check it? `Is there any advantage to doing this over just assigning a map?` It's comparing apples to oranges. – KamilCuk Feb 23 '21 at 22:22
  • 2
    C or C++? They are different languages and will affect any answer. Your reference to `map` implies the code is C++ but it is not clear whether that is actually the case or whether that's just your own interpretation. – kaylum Feb 23 '21 at 22:23
  • I dumped the buffer which ended up looking like a JSON object. `{ "1":"abcdef", "2":"ghijkl", "3":"mnopqr", "4":"stuvwx" }` So I understand what it's doing. Maybe I just don't understand how it's doing it. – rich Feb 23 '21 at 22:29
  • probably needs `const`. e.g.: `const char *c_token =` ... – Wyck Feb 23 '21 at 22:30
  • Cool trick, sort of. Until you need a `)` in the text – M.M Feb 23 '21 at 22:45
  • Does this answer your question? [Embedding JSON as a string in C++ code using preprocessor](https://stackoverflow.com/questions/32082004/embedding-json-as-a-string-in-c-code-using-preprocessor) – Wyck Feb 24 '21 at 14:53

2 Answers2

0

The preprocessor is converting everything between the parentheses including the commas to a string literal. It can be almost complete syntactic garbage. e.g.: This is fine. R(!@#,*&#,(,));

As an experiment, try:

#define R(...) "BEGIN" #__VA_ARGS__ "END"

This yields: "BEGIN!@#,*&#,(,)END"

and compare what happens when you just do:

#define R(x) "BEG" #x "END"

This yields: "BEGIN!@#END" with a warning something like:

too many arguments for function-like macro invocation 'R'

Wyck
  • 10,311
  • 6
  • 39
  • 60
  • Thanks. That answers it. I was trying to understand what/how it was doing that. I noticed that it skips line breaks though. So #__VA_ARGS__ will ignore line breaks? – rich Feb 23 '21 at 22:33
  • I believe the rules about spacing in the preprocessor are that any non-zero amount of whitespace (outside of string or character literals) including newlines is expanded to a single space. – Wyck Feb 23 '21 at 22:38
  • And string literals separated by whitespace are concatenated (in [phase 6](https://en.cppreference.com/w/cpp/language/translation_phases)). – Useless Feb 23 '21 at 23:07
0

What's going on here is Stringification by the preprocessor. Whatever appears as the argument(s) to R() is converted to a string. The # in front of the argument(s) to the macro is syntax that causes this.

So R("foo", abc!@#) becomes string string literal "\"foo\", abc!@#". Newlines and leading whitespace will be converted to a single space.

The reason to do this is to make a string with lots of " in it easier to write by not needing to escape them and to allow for easier line wrapping. If you simply wrote the jSON string directly, every " would need to be written as \" to keep it from delimiting the string. Each line would also need to end the string and begin a new one on the next line, since you can't have line breaks inside a string literal.

So the example would be written as:

const char *c_token = 
"{"
  "\"1\":\"abcdef\","
  "\"2\":\"ghijkl","
  "\"3\":\"mnopqr","
  "\"4\":\"stuvwx""
"}";

Which is rather more complex that the more natural syntax the macro allowed.

TrentP
  • 4,240
  • 24
  • 35