Delayed stringification without macro expansion - c

Is it possible to modify the following code fragment to prevent the diagnostic printed by #pragma GCC warning from changing if any of the identifier tokens in deprecation_message are defined as object-like macros at the point where dmacro is expanded, while preserving the ability to substitute symbol into the message? Ugliness no object, GCC extensions are fair game as long as clang also implements them, but the contents of the diagnostic may not be modified.
#define deprecation_message(symbol) \
#symbol will be removed from <header.h> in the next release\n\
of $LIBRARY. To use #symbol, include <moved/header.h> instead.
#define make_pw(...) make_pw_(__VA_ARGS__)
#define make_pw_(...) make_pw__(GCC warning #__VA_ARGS__)
#define make_pw__(...) _Pragma(#__VA_ARGS_)
#define dmacro(a,b,c) make_pw(deprecation_message(dmacro)) xmacro(a,b,c)
// Uncommenting any of the following #define lines should *not*
// change the text of the diagnostic in any way.
//#define header
//#define n f
//#define will won't'
dmacro(x,y,z)
(You might be tempted to reach for string-literal concatenation, but that won't work; both _Pragma itself and #pragma GCC warning accept only a single string literal. _Pragma("this" "that") is a syntax error.)

Maybe I misunderstood the question, but the following code is working and compiling on both clang and gcc, and tested with -std=c99 and -std=c11:
#include <stdio.h>
#include <stdlib.h>
#define deprecation_message(symbol) \
#symbol " will be removed from <header.h> in the next release\n" \
"of $LIBRARY. To use " #symbol ", include <moved/header.h> instead."
#define make_pw__(...) _Pragma(#__VA_ARGS__)
#define make_pw_(...) make_pw__(GCC warning #__VA_ARGS__)
#define make_pw(...) make_pw_(__VA_ARGS__)
#define xmacro(a, b, c) puts("I'm working: " #a #b #c "!")
#define dmacro(a, b, c) make_pw(deprecation_message(dmacro)) xmacro(a, b, c)
int
main(void)
{
dmacro(x, y, z);
return EXIT_SUCCESS;
}
Now, the above code will expand this with clang:
int
main(void)
{
#pragma GCC warning "\042dmacro\042 \042 will be removed from <header.h> in the next release\134n\042 \042of $LIBRARY. To use \042 \042dmacro\042 \042, include <moved/header.h> instead.\042"
puts("I'm working: " "x" "y" "z" "!");
return 0 /* Successful exit status. */;
}
and will produce the following warnings on clang:
src/main.c:18:5: warning: "dmacro" " will be removed from <header.h> in the next release\n" "of $LIBRARY. To use " "dmacro" ", include <moved/header.h> instead." [-W#pragma-messages]
dmacro(x, y, z);
^
and this on gcc:
src/main.c:18:13: warning: "dmacro" " will be removed from <header.h> in the next release\n" "of $LIBRARY. To use " "dmacro" ", include <moved/header.h> instead."
dmacro(x, y, z);
^~~~~~~~
Basically all I did was, to put the deprecation message into quotes...
UPDATE1:
Now, the funny thing is, if you remove the stringification from make_pw_, that is:
#define make_pw_(...) make_pw__(GCC warning __VA_ARGS__)
then clang will give you this:
src/main.c:18:5: warning: dmacro will be removed from <header.h> in the next releaseof $LIBRARY. To use dmacro, include <moved/header.h> instead. [-W#pragma-messages]
dmacro(x, y, z);
^
Which is nice, as it does not have the unwanted quotes, however GCC will only give you this:
src/main.c:18:13: warning: dmacro
dmacro(x, y, z);
^~~~~~~~
What is even more strange, is that if you change the deprecation_message to this:
#define deprecation_message(symbol) "" #symbol "..."
then it will give you an empty warning, like it was only using the first non-white-space token from the macro argument.
To be honest with you, I don't know if this is a bug from GCC, or this is the well-defined behavior and clang is doing some extra work to make sense out of this, none the less, this is happening with the latest versions of the two compilers. (If I have to bet, I would say, this is a bug :P)
UPDATE2:
Here is a slightly modified version, which works on both compilers as you described in your comment. The trick is, that the macro DEPRECATED has two stages, instead of one.
#include <stdio.h>
#include <stdlib.h>
#define DEPRECATED_(...) #__VA_ARGS__
#define DEPRECATED(symbol) \
DEPRECATED_(#symbol will be removed from <header.h> in the next release\n \
of $LIBRARY. To use #symbol, include <moved/header.h> instead.)
#define PRAGMA_WARN_(message) _Pragma(#message)
#define PRAGMA_WARN(message) PRAGMA_WARN_(GCC warning message)
#define dmacro(a, b, c) \
PRAGMA_WARN(DEPRECATED(dmacro)) \
puts("I'm working: " #a #b #c "!")
int
main(void)
{
dmacro(x, y, z);
return EXIT_SUCCESS;
}
So this will produce the following output in clang:
src/main.c:19:5: warning: "dmacro" will be removed from <header.h> in the next release of $LIBRARY. To use "dmacro", include <moved/header.h> instead. [-W#pragma-messages]
dmacro(x, y, z);
^
And the following one in GCC:
src/main.c:19:13: warning: "dmacro" will be removed from <header.h> in the next release
of $LIBRARY. To use "dmacro", include <moved/header.h> instead.
dmacro(x, y, z);
^~~~~~~~
NOTE: I would highly recommend you, to remove the \n character from your message!

Related

C Macro with _Pragma fails to compile

I'm trying to define a macro to return the result of a range check which works on signed and unsigned types. However, because I am compiling with -Wextra which includes -Wtype-limits, I want to ignore -Wtype-limits just for this macro. Unfortunately, this:
#define IN_RANGE(v, min, max) \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wtype-limits\"") \
( (((v) < (max)) && ((v) > (min))) ? true : false ) \
_Pragma("GCC diagnostic pop")
Fails to compile (because I'm also using -Werror) with:
... error: expected expression before '#pragma'
[build] 40 | _Pragma("GCC diagnostic push") \
[build] | ^~~~~~~
Edit: here's a complete example.
#include <stdio.h>
#include <stdint.h>
#define IN_RANGE(v, min, max) \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wtype-limits\"") \
( (((v) < (max)) && ((v) > (min))) ? true : false ) \
_Pragma("GCC diagnostic pop")
const uint32_t MIN_NUM = 0;
const uint32_t MAX_NUM = 10;
int main() {
printf("%s", IN_RANGE(8, MIN_NUM, MAX_NUM) ? "OK": "FAIL");
return 0;
}
Fails with (on gcc 9):
$ gcc -Wall -Wextra example.c
example.c: In function ‘main’:
example.c:14:1: error: expected expression before ‘#pragma’
14 | printf("%s", IN_RANGE(8, MIN_NUM, MAX_NUM) ? "OK": "FAIL");
| ^ ~
Given some experimentation, it seems that you can't just embed the _Pragma() operator in the middle of an expression. In effect, it needs to be used where a statement can be used.
This code compiles, and AFAICT it is because there's a complete statement sandwiched between the _Pragma() operators:
#include <stdbool.h>
#include <stdio.h>
#define IN_RANGE2(r, v, min, max) \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wtype-limits\"") \
((r) = (((v) < (max)) && ((v) > (min)))); \
_Pragma("GCC diagnostic pop")
int main(void)
{
int x = 10;
bool y;
IN_RANGE2(y, x, -9, +9);
printf("%d\n", y);
return 0;
}
Note the semicolon before the third _Pragma() operator. Without that, I was getting expected ‘;’ before ‘#pragma’ (as an error since I too compile with -Werror).
I've also simplified the conditional so it doesn't use the ternary operator, as I noted in a comment.
The relevant section of the standard is §6.10.9 Pragma operator. It says:
A unary operator expression of the form:
_Pragma ( string-literal )
is processed as follows: The string literal is destringized by deleting any encoding prefix, deleting the leading and trailing double-quotes, replacing each escape sequence \" by a double-quote, and replacing each escape sequence \\ by a single backslash. The resulting sequence of characters is processed through translation phase 3 to produce preprocessing tokens that are executed as if they were the pp-tokens in a pragma directive. The original four preprocessing tokens in the unary operator expression are removed.
Think about what the code expands to in your example:
int main(void)
{
printf("%s", IN_RANGE(8, MIN_NUM, MAX_NUM) ? "OK": "FAIL");
return 0;
}
is approximately equivalent to:
/* SO 7548-0114 */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
const uint32_t MIN_NUM = 0;
const uint32_t MAX_NUM = 10;
int main(void)
{
printf("%s",
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wtype-limits"
((((8) < (MAX_NUM)) || (8) > (MIN_NUM)) ? true : false)
#pragma GCC diagnostic push
? "OK": "FAIL");
return 0;
}
This doesn't compile. It produces the error:
pragma67.c: In function ‘main’:
pragma67.c:12:11: error: expected expression before ‘#pragma’
12 | #pragma GCC diagnostic push
| ^~~
Line 12 is the first #pragma directive.
I'm not totally convinced that this is what the standard mandates, but it is what I get from GCC 11.2.0.
However, when I compile with Apple's Clang (Apple clang version 14.0.0 (clang-1400.0.29.202)), both versions of the code I show compile OK. Thus, there is a discrepancy in the interpretation of the standard between these two major C compilers.
And, indeed, when I compile with Clang, your original code compiles cleanly. You can probably make a case out for a bug report to the GCC team. However, if you're going to write portable code, you'll need to accept the limitations imposed by GCC for a few years yet (though that depends on your portability requirements).

C macros, what's the meaning of ((void)0)?

Given the following code written according to the C99 standard:
#define LOW 1
#define MEDIUM 2
#define HIGH 3
#define LOGGING_LEVEL HIGH
#if LOGGING_LEVEL >= MEDIUM
#define LOG_MEDIUM(message) printf(message)
#else
#define LOG_MEDIUM(message) ((void)0)
#endif
void load_configuration() {
//...
LOG_MEDIUM("Configuration loaded\n");
}
what's the purpose of ((void)0) I searched the web a lot but nothing found regarding this.
Plus, why didn't we wrote ; after using printf(message)
The void-cast fixes a compiler warning. Here's an analogous testcase:
int main(void)
{
0; // generates "foo.c:3:2: warning: statement with no effect"
(void)0;
return 0;
}
and (using a script to add gcc's warning flags) you see a warning for the line without a cast:
$ gcc-stricter -c foo.c
foo.c: In function ‘main’:
foo.c:3:2: warning: statement with no effect [-Wunused-value]
0;
^
The extra parentheses and lack of semicolon allow the macro's result to be used interchangeably with the printf.
Main idea is to exclude all LOG_MEDIUM if the criteria was not meet.
After compilation those calls will not affect functionality.

Why is the `_Generic` keyword in my macro not working?

Probably I'm doing some very basic thing wrong here, but I just cannot figure out what that could be. I can explain my code snippet, but I think it is pretty obvious what I'm trying to do here: create a type-generic macro overload "print function" for my DynamicArray's dynarr_printf method. The question is: why isn't it working?
Thanks in advance!
ENV:
// Mac OS X 10.9.2
// Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
cc -Wall -v -g -std=c11 -I/usr/local/include -c -o build/tmp/main.o main.c
CODE:
void
__dynarr_printf(DynamicArray *dynarr,
void (*print_func)(int, void*));
#define __line_fmt__(fmt) " [%d] " fmt "\n"
static inline void
__dynarr_printf_i(int i, int *v)
{
printf(__line_fmt__("%d"), i, *v);
}
// tons of type specific functions ...
static inline void
__dynarr_printf_def(int i, void *v)
{
printf(__line_fmt__("ptr(%p)"), i, v);
}
#define __dynarr_typef(type) _Generic((type), int: __dynarr_printf_i, \
// tons of type specific funcs ...
default: __dynarr_printf_def)
#define dynarr_printf(dynarr, type) \
(__dynarr_printf(dynarr, __dynarr_typef(type)))
OUTPUT:
main.c:102:5: error: expected expression
dynarr_printf(dynarr, float);
^
./dynarr.h:168:31: note: expanded from macro 'dynarr_printf'
(__dynarr_printf(dynarr, __dynarr_typef(type)))
^
./dynarr.h:157:45: note: expanded from macro '__dynarr_typef'
#define __dynarr_typef(type) _Generic((type), int: __dynarr_printf_i, \
^
1 error generated.
The syntax of a generic selection is:
_Generic ( assignment-expression , generic-assoc-list )
(For the definition of a generic-assoc-list and more information about generic selections, see N1570 section 6.5.1.1.)
The first "operand" is an expression, not a type name. It selects on the type of that expression.
If the type is always scalar, you could change
_Generic((type), ...)
to
_Generic((type)0), ...)

gcc warnings for no-effect statements.

I have a logging macro which in release mode becomes:
#define LOG (void)
So statement
LOG("foobar %d", 0xbabecafe);
is expanded to
(void)("foobar %d", 0xbabecafe);
The problem is that the last expression produces an warning under gcc:
warning: left-hand operand of comma expression has no effect [-Wunused-value]
How can I change the logging macro such that no warning is issued? (Note, that I don't want to add compiling flag -Wunused-value).
EDIT I see already a couple of answers involving (...). The same file is compiled under Minix which doesn't support variadic macros. The best would be to have a C89 conforming solution. While your answer is correct (and I upvoted it), it is my fault that I didn't include this small detail.
I think the old school way of dealing with this is to take advantage of double parens. Something like this:
LOG(("message: %d", 10));
Then for your macro, you define it like this:
#define LOG(x) printf x
or
#define LOG(x) (void)0
Because of the double parens, the pre-processor treats the whole inner paren as a single parameter. This at least used to work in visual studio.
EDIT: I did a quick test, it works with gcc with -ansi, so it should be good:
gcc -DNDEBUG -ansi -pedantic -W -Wall test.c -o test
#include <stdio.h>
#ifdef NDEBUG
#define LOG(x) printf x
#else
#define LOG(x) (void)0
#endif
int main() {
LOG(("message: %d\n", 10));
return 0;
}
The easiest should be
#define LOG(...) (void)0
(gcc supports the C99 variadic macros and most other compilers also do these days) That will discard the arguments list, which has two advantages:
it does not create statements with no effect and
the arguments are not evaluated at all (if you call non-inline functions in the argument list, in your version the compiler can't eliminate them, while with the variadic macro, the compiler won't see them at all.
#define LOG(...) seems to do the trick.
For your problems with a non-conforming C implementation (not even C89?) you could do something like
static void LOG(char *format, ...) { /* empty */ }
even a completely dumb compiler should be able to optimize that out.
I've used
#define LOG( t) t
for the development version and
#define LOG( t)
for the release version, with a typical use being
LOG( printf( "here\n"));

printf(" LIST.H ") where LIST.H is a macro

#include <stdio.h>
#include<stdlib.h>
#define LIST.H onus;
int main ()
{
char *p,*s;
printf(" LIST.H ");
}
I expect LIST.H to print onus as out put.
But this does not happen.
upon compiling I get a warning
temp.c:3:13: warning: missing whitespace after the macro name
and the output is LIST.H not onus.
How can I get desired thing printed by the above macro?
UPDATE
I want to have the output
as onus with one space before and after the string.
Macros names cannot have . inside them. That's why you get the warning:
warning: missing whitespace after the macro name, after LIST it expects a space, but it gets a . instead.
Also, when a macro name is inside a string(between "string") it is not replaced by the macro definition.
You could do this instead:
#define LISTH "onus"
// and then
printf(LISTH);
which the preprocessor will transform to:
printf("onus");
If you do:
#define LISTH "onus";
the preprocessor will transform it to:
printf("onus";);
which won't compile.
Firstly, you can't use . in macro names.
Secondly, you should "expect" it to print ouns;, since you included a ; into your macro definition.
Thirdly, in order to achieve that you can use "stringization" macro-operator # with some helper macros
#define TO_STRING_(x) #x
#define TO_STRING(x) TO_STRING_(x)
#define LIST_H onus
...
printf(" " TO_STRING(LIST_H) " ");
or, better
printf(" %s ", TO_STRING(LIST_H));
#include <stdio.h>
#define LIST_H "onus"
int main()
{
printf(LIST_H);
}
macros in strings aren't resolved, you need to layers of macro resolution to do that:
#define __STR(x) #x
#define _STR(x) __STR(x)
printf(_STR(LIST));
you also cannot have dots in macro defines last I checked, which would be what your error is about, so rather use LIST_H...

Resources