3

I came across the following code:

#define ERROR 0
#define WARN 1
#define INFO 2
#define DEBUG 3  

extern int log_level;

char const *LEVEL_TO_STRING[] = { "ERROR", "WARN", "INFO", "DEBUG" };

#define LOG(level, s, ...)                                                  \
    do                                                                      \
    {                                                                       \
        if(level <= log_level)                                              \
            printf( "[%s] " s "\n", LEVEL_TO_STRING[level], ##__VA_ARGS__)  \
    }                                                                       \
    while(0)                                                                \   

I do not understand what the s is doing outside the quotes in the printf statement. I tried searching for what this is and how it works, but I'm not sure what to look for. Could someone explain to me how this code works?

As a follow-up, is it possible to write code like the example above outside a macro? The closest I've seen to this is using format specifiers:

#define FORMAT "ld"
long num = 1000000;
printf("%" FORMAT "\n", num);

It would help to understand how these two cases work internally, and why C does not let me do something like, printf("%s" s "\n", string1, string2) as is done in the macro above.

EDIT : Not a clean dup of How does concatenation of two string literals work? because this post is specific to printf (and format specifiers) as it relates to macros. Also, there is useful information in the responses to this post that isn't available in the other.

aspen100
  • 965
  • 11
  • 22
  • Possible duplicate of [How does concatenation of two string literals work?](https://stackoverflow.com/questions/12120944/how-does-concatenation-of-two-string-literals-work) – Jongware Jan 16 '18 at 15:37
  • 2
    Tip: Better to use `unsigned log_level;` and `LEVEL_TO_STRING[level & 3]` here. The nature of troublesome code should assume as little as possible about the correctness of `level, log_level`. – chux - Reinstate Monica Jan 16 '18 at 16:06

3 Answers3

3

I do not understand what the s is doing outside the quotes in the printf statement

In order to see what happens you need to recall that s is replaced with the second parameter of LOG macro in the text of the program. The only way that this could work is when s is a string literal, because C merges them. In other words, there is no difference between

"quick brown fox"

and

"quick" " brown " "fox"

These two forms of writing a string literal are equivalent.

In the same way, passing "ld" to FORMAT in

printf("%" FORMAT "\n", num);

is equivalent to

printf("%ld\n", num);

and is legal.

why C does not let me do something like, printf("%s" s "\n", string1, string2) as is done in the macro above?

Passing anything other than a string literal is illegal:

char FORMAT[] = "ld";
printf("%" FORMAT "\n", num); // <<== Does not compile

s and FORMAT in your code must be not just strings, but string literals:

#define s "[%s]"
...
printf("%s" s "\n", string1, string2); // This compiles
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • makes sense - but isn't `const char *s = "[%s]"` also a string literal? Why does that fail? Does it only work if it is in the preprocessor stage, like @sjsam suggests? – aspen100 Jan 16 '18 at 15:45
  • 1
    @aspen100 `const char *s = "[%s]"` is a variable of type constant `char` pointer, which is initialized with a string literal. The compiler cannot use it in a merge. – Sergey Kalinichenko Jan 16 '18 at 15:48
  • 1
    @aspen100 yes and no: `"[%s]"` is a string literal, while `s` is not. – Gerhardh Jan 16 '18 at 15:52
1
"[%s] " s "\n"

when s is defined as a macro ie using #define would concatenate everything together.

As the substitution happens during the preprocessing, it won't be flagged as an error. In all other cases, you should get a syntax error.

sjsam
  • 21,411
  • 5
  • 55
  • 102
0

The key is the line continuation '\' at the end of the definition. The code defines a macro function LOG which does the specified logging.

Apparently the user of the macro can specify their own formatted string in s and give the arguments in ... -> ##__VA_ARGS_

Felix
  • 2,548
  • 19
  • 48