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.
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
"[%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.
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_
Related
#include<stdio.h>
#define PRINT(A,B) printf("Value of expression %s is %*",#A,#B,(A))
int main(void){
PRINT(1+3+1,%d);
return 0;
}
How to write a macro which gets two arguments: expression and format specifier then printing expression and value of it in given format?
%d is a format string, so it needs to be in double quotes, like any other string literal. Also, use meaningful names for identifiers.
#define PRINT(expr, fmt) printf("Value of expression %s is " fmt "\n", #expr, expr)
PRINT(1+3+1, "%d");
You can get away with the preprocessor's stringification operator to turn something like %d into "%d", but it's fragile (it's very sensitive to whitespace), risk-prone (code analysis tools that don't run a preprocessor may think that the code is using the variable d or choke on a syntax error on % with no left-hand side), and most importantly, it's misleading for human readers.
As a rule of thumb, you should only use unhygienic macros (macros that depart from the syntax of the language) if you have a very good reason. Typesetting an expression for debugging purposes (#expr) is a good reason. Writing programs that are two characters shorter is a good reason only if you're entering an obfuscated programming contest.
You could try something like this;
#define PRINT(A,B) printf("Value of expression %s is "#B"\n",#A,(A))
Say I have a character array:
#define LEN 10
char arr[LEN + 1];
Lets do some scanf operation to it:
scanf("Name: %s", arr);
This could be dangerous if someone is typing a name that is longer than 10 characters. So better use this:
scanf("Name: %10s", arr);
Well now I would run into trouble if LEN is changed. I would have to go through the whole code to correct every line where I used the 10 in context of arr. So I thought about somehting like this:
scanf("Name: %LENs", arr);
But this will not work.LEN is not resolved by the preprocessor beacuse it is used inside a string.
How to use a define inside a format string?
C joins adjacent string literals and you can stringify a preprocessor parameter with #, so the following should do the trick:
#define LEN 10
// this converts to string
#define STR_(X) #X
// this makes sure the argument is expanded before converting to string
#define STR(X) STR_(X)
[...]
scanf("Name: %" STR(LEN) "s", arr);
The macros are needed because with just #LEN, you'd end up with LEN expanded to 10, and with only one macro applying # to its argument, the result would be "LEN" (the argument wouldn't be expanded).
The preprocessor / compiler will transform this in the following steps:
1. scanf("Name: %" STR_(10) "s", arr);
2. scanf("Name: %" "10" "s", arr);
3. scanf("Name: %10s", arr);
In the last step, the string literals are joined into a single one.
On a side note, your scanf() format string would require the user to literally enter
Name: xyz
to actually match. I doubt this is what you wanted. You probably want something like this:
fputs("Name: ", stdout);
fflush(stdout);
scanf("%" STR(LEN) "s", arr);
Also consider not using scanf() at all. With e.g. fgets(), this whole preprocessor magic is obsolete. For reasons why you shouldn't use scanf(), see my beginners' guide away from scanf().
MY_PRINT is a macro throughout the code, which just does printf.
I want to temporarily modify it to also append \n after every printf.
However, when I do this:
#define SENS_PRINT(x) printf(x); printf("\n")
MY_PRINT( "\n #%d: %c ", ++command_line_number, sensor_operation_code );
...the output is trash: #3405240: <alpha symbol>
This prints ok, but no \n at end:
#define SENS_PRINT printf
You want your macro to be able to take various arguments, just like the real printf. You can do this with variadic macros.
There's also a danger that two separate expressions aren't interpreted as the macro suggests when the macro is the only expression in a conditional code block. Think about what the macro does if you say if (flag) SENS_PRINT(...);. One way to prevent this is to wrap the macro in a do { ... } while(0) block.
A variadic macro that appends a newline to printf could look like this:
#define PRINTFLN(...) do { printf(__VA_ARGS__); puts(""); } while (0)
You can use it just like printf:
PRINTFLN("Hello %s!", "cruel world");
if (flag) PRINTFLN("%d + %d == %d", x, y, sum);
The C language lets you concatenate string literals separated by whitespace. Provided all of your calls to MY_PRINT use a string literal as their formatting argument, you can define a variadic macro which will directly append "\n" to your format string, and then apply the rest of your arguments.
Defining your macro like so
#define MY_PRINT(format, ...) printf(format "\n", __VA_ARGS__)
you will be able to directly append a newline to the formatting argument.
Using this method only one function call will be performed, but any call formatted using anything other than a string literal will cause a compile error.
The problem with function-line macros is that the preprocess treats all comma-separated arguments as arguments to the macro and not as a single argument. So your compiler should really have complained that you pass to many arguments to the macro.
A simple solution is to enclose the macro argument in parentheses:
MY_PRINT( ( "\n #%d: %c ", ++command_line_number, sensor_operation_code ) );
// ^ ^
// | |
// Note extra parentheses here... and here
Another solution is to use variadic macros.
On a somewhat related note, your macro, as you show it, can't be used in construct like e.g.
if (some_condition)
MY_PRINT(...);
That will be replaced like
if (some_condition)
printf(...);
printf(...);
If you have multiple statements inside a macro, you need to enclose it in a block, like e.g.
#define MY_PRINTF(...) \
do { \
statement1; \
statement2; \
. \
. \
. \
statementN; \
} while (0)
I need a function to read a file name, with a max length of MAX_FILE_NAME_SIZE, which is a symbolic constant, I did this the following way:
char * readFileName()
{
char format[6];
char * fileName = malloc(MAX_FILE_NAME_SIZE * sizeof(fileName[0]));
if(fileName== NULL)
return NULL;
sprintf(format, "%%%ds", MAX_FILE_NAME_SIZE-1);
scanf(format, fileName);
fileName= realloc(fileName, strlen(fileName)*sizeof(fileName[0]));
return fileName;
}
I'd really like to get read of the sprintf part (and also the format vector), what's the cleanest and most efficient way to do this?
Solution
You can make a little Preprocessor hack:
#define MAX_BUFFER 30
#define FORMAT(s) "%" #s "s"
#define FMT(s) FORMAT(s)
int main(void)
{
char buffer[MAX_BUFFER + 1];
scanf(FMT(MAX_BUFFER), buffer);
printf("string: %s\n", buffer);
printf("length: %d\n", strlen(buffer));
return 0;
}
The FORMAT and FMT macros are necessary for the preprocessor to translate them correctly. If you call FORMAT directly with FORMAT(MAX_BUFFER), it will translate into "%" "MAX_BUFFER" "s" which is no good.
You can verify that using gcc -E scanf.c. However, if you call it through another macro, which will effectively resolve the macro names for you and translate to "%" "30" "s", which is a fine format string for scanf.
Edit
As correctly pointed out by #Jonathan Leffler in the comments, you can't do any math on that macro, so you need to declare buffer with plus 1 character for the NULL terminating byte, since the macro expands to %30s, which will read 30 characters plus the null byte.
So the correct buffer declaration should be char buffer[MAX_BUFFER + 1];.
Requested Explanation
As asked in the comments, the one macro version won't work because the preprocessor operator # turns an argument into a string (stringification, see bellow). So, when you call it with FORMAT(MAX_BUFFER), it just stringifies MAX_BUFFER instead of macro-expanding it, giving you the result: "%" "MAX_BUFFER" "s".
Section 3.4 Stringification of the C Preprocessor Manual says this:
Sometimes you may want to convert a macro argument into a string constant. Parameters are not replaced inside string constants, but you can use the ‘#’ preprocessing operator instead. When a macro parameter is used with a leading ‘#’, the preprocessor replaces it with the literal text of the actual argument, converted to a string constant. Unlike normal parameter replacement, the argument is not macro-expanded first. This is called stringification.
This is the output of the gcc -E scanf.c command on a file with the one macro version (the last part of it):
int main(void)
{
char buffer[30 + 1];
scanf("%" "MAX_BUFFER" "s", buffer);
printf("string: %s\n", buffer);
printf("length: %d\n", strlen(buffer));
return 0;
}
As expected. Now, for the two levels, I couldn't explain better than the documentation itself, and in the last part of it there's an actual example of this specific case (two macros):
If you want to stringify the result of expansion of a macro argument, you have to use two levels of macros.
#define xstr(s) str(s)
#define str(s) #s
#define foo 4
str (foo)
==> "foo"
xstr (foo)
==> xstr (4)
==> str (4)
==> "4"
s is stringified when it is used in str, so it is not macro-expanded first. But s is an ordinary argument to xstr, so it is completely macro-expanded before xstr itself is expanded (see Argument Prescan). Therefore, by the time str gets to its argument, it has already been macro-expanded.
Resource
The C Preprocessor
I thought a C string can be initialized with one and only one quoted string. I just wonder how is this correct?
char const help_message [] =
"Usage: %s [options] files ...\n"
"\n"
"Options include:\n"
" --verbose -v Be verbose\n"
" --help -h Print this help message\n"
" --output -o Specify output file\n"
"\n" ;
printf (help_message, argv [0]) ;
The compiler will automatically concatenate adjacent strings.
This can be quite useful to increase readability, as in your example, or with some preprocessor functions:
#define LOG(x) printf("%s", "Logging: " x)
LOG("HeyHey");
Pretty contrived example, but gets the point across.
Adjacent string literals are concatenated, and this is useful two ways: macros which combine strings and visualizing multi-line string literals such as you have above. Compare how that code would look otherwise:
char const help_message[] = "Usage: %s [options] files ...\n\nOptions include:\n --verbose -v Be verbose\n --help -h Print this help message\n --output -o Specify output file\n\n";
Imagine trying to maintain that instead. In addition, if you use a multi-line string literal, you have to either escape newlines or deal with whatever the source code uses, which may not be '\n', and you'll have to watch your indentation carefully. All of which makes the code in your example better.
Here's an example of the macro case:
#define STRINGIZE_(v) #v
#define STRINGIZE(v) STRINGIZE_(v)
#define LOCATION __FILE__ ":" STRINGIZE(__LINE__)
#define MY_ASSERT(expr) do { \
if (!(expr)) \
some_function(LOCATION ": assertion failed in " \
__PRETTY_FUNCTION__ ": " #expr); \
} while (0)
(There are alternatives to this, such as passing separate parameters, and using the GCC-specific __PRETTY_FUNCTION__ like this is deprecated too, but the rest is handy and this is a decent "real" example, IMHO.)
Other issues brought up in that code to be aware of:
the single # is the preprocessor stringize operator (## is the other special preprocessor operator, for token pasting)
without the second stringize macro, you'll get "filename.c:__LINE__"
using do-while doesn't break if-else and requires the macro to be used as a statement rather than an expression
preventing use as an expression isn't always helpful, but it is what you want for assert-like macros
Breaking if-else example:
if (cond) MY_ASSERT(blah);
else other();
Expands to:
if (cond) do { ... } while(0);
else other();
Instead of:
if (cond) if (...) ...;
else other();
Which is the incorrect and surprising:
if (cond) {
if (...) {
...;
}
else {
other();
}
}
Because adjacent quoted strings are considered to be part of the same string by the compiler. The grammar of C specifically allows this.
Don't be confused with an array of strings:
{"xxx" ,
"yyyy",
"3534"}
What you posted is a single string.
Your initializer contains only one string literal. What looks like multiple ""-enclosed string literals will actually be merged into a single string literal at the 6-th stage of translation (after preprocessing).