This simple code:
#define WIDTH 500.5
#define NB 23.2
int x[(int)(WIDTH/NB)];
gives me a warning:
prog.c:4:1: warning: variably modified 'x' at file scope [enabled by default]
If I set #define WIDTH 500 and #define NB 23, the warning disappears.
Passing a float value for WIDTH macro forces evaluation by the compiler and thus issues a warning because array has not a constant size.
The preprocessed C code looks like int x[(int)(500.5/23.2)];, whereas int x[(int)(500/23)]; is OK for the compiler (value is already constant integer)
I would like to find a way either
to ignore this particular warning (but leaving the others so I can enable -Werror: seems that is a lost cause: GCC, C: Finding out name of default warnings for use in #pragma ignore
fix the code so it does what I want without issuing warnings.
force the pre-processor to perform the computation as integer
Funny thing: compiling with g++ I don't have the warning whereas I read here that variable length arrays are not officially supported in C++, only in C99. But that's not an option for me, since I need to stick to C.
It just violates the standard:
Integer constant expression
An integer constant expression is an
expression that consists only of operators other than assignment,
increment, decrement, function-call, or comma, except that cast
operators can only cast arithmetic types to integer types, integer
constants, enumeration constants, character constants, floating
constants, but only if they are immediately used as operands of casts
to integer type
And further:
The following contexts require expressions that are known as integer
constant expressions':
...
The index in an array designator (since C99)
One could ensure that a normal array (non VLA) is created like this:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
enum{ARRAY_DIMENSION_OF_x = (int)(WIDTH/NB)};
int x[ARRAY_DIMENSION_OF_x];
#pragma GCC diagnostic pop
This compiles without warning in gcc and clang in C mode and with a old-style cast to 'int' warning in C++ mode.
One could also hide the creation of the enum within a macro:
/* ================= define MAKE_CONST_INT() macro ================= */
#ifdef __cplusplus
template<class T> struct make_const_int_helper{static T t;};
# define MAKE_CONST_INT(x) (sizeof(*(make_const_int_helper<char (*)[int((x)+0L)]>::t))) /* +0L avoids "useless cast" warning*/
#else
# define EVALUATE_TO_0(type) (0*__builtin_types_compatible_p(type,int))
# define EVALUATE_TO_0_PRAGMA(x) EVALUATE_TO_0(struct{int dummy; _Pragma(x)})
# define EVALUATE_TO_0_START_DISABLE_WARNINGS \
( EVALUATE_TO_0_PRAGMA("GCC diagnostic push") \
+ EVALUATE_TO_0_PRAGMA("GCC diagnostic ignored \"-Wpedantic\"") \
+ EVALUATE_TO_0_PRAGMA("GCC diagnostic ignored \"-Wc++-compat\"") )
# define EVALUATE_TO_0_END_DISABLE_WARNINGS \
EVALUATE_TO_0_PRAGMA("GCC diagnostic pop")
# define MAKE_CONST_INT(x) MAKE_CONST_INT_HELPER2(x,__LINE__,__COUNTER__)
# define MAKE_CONST_INT_HELPER2(x,line,counter) MAKE_CONST_INT_HELPER(x,line,counter)
# define MAKE_CONST_INT_HELPER(x,line,counter) \
( EVALUATE_TO_0_START_DISABLE_WARNINGS \
+ EVALUATE_TO_0(enum{ INT_CONSTANT_##counter##_AT_LINE_##line = (int)(x) }) \
+ INT_CONSTANT_##counter##_AT_LINE_##line \
+ EVALUATE_TO_0_END_DISABLE_WARNINGS)
#endif
/* ================= test MAKE_CONST_INT() macro ================= */
#define WIDTH 500.5
#define NB 23.2
extern int x[MAKE_CONST_INT(WIDTH/NB)];
This compiles without warning in gcc and clang in C and in C++ mode.
The structs with the int dummy members are only necessary to get a place where gcc accepts a _Pragma("GCC diagnostic ....") pragma.
Example
Extended implementation that works for MSVC, gcc, clang and icc, but still won't compile on really picky compilers:
/* ================= define MAKE_CONST_INT() macro ================= */
#ifdef __cplusplus
# if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wtemplates"
# endif
template<class T> struct make_const_int_helper{static T t;};
# if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic pop
# endif
# define MAKE_CONST_INT(x) (sizeof(*(make_const_int_helper<char (*)[int((x)+0L)]>::t))) /* +0L avoids "useless cast" warning*/
#else
# if defined(__GNUC__)
# define EVALUATE_TO_0(type) (0*__builtin_types_compatible_p(type,int))
# define EVALUATE_TO_0_PRAGMA(x) EVALUATE_TO_0(struct{int dummy; _Pragma(x)})
# define EVALUATE_TO_0_START_DISABLE_WARNINGS \
( EVALUATE_TO_0_PRAGMA("GCC diagnostic push") \
+ EVALUATE_TO_0_PRAGMA("GCC diagnostic ignored \"-Wpedantic\"") \
+ EVALUATE_TO_0_PRAGMA("GCC diagnostic ignored \"-Wc++-compat\"") )
# define EVALUATE_TO_0_END_DISABLE_WARNINGS \
EVALUATE_TO_0_PRAGMA("GCC diagnostic pop")
# else
# define EVALUATE_TO_0(type) (0*sizeof(type))
# if defined(_MSC_VER)
# define EVALUATE_TO_0_START_DISABLE_WARNINGS \
(0 __pragma(warning( push )) __pragma(warning(disable:4116)))
# define EVALUATE_TO_0_END_DISABLE_WARNINGS (0 __pragma(warning( pop )) )
# else
# define EVALUATE_TO_0_START_DISABLE_WARNINGS 0 /*other compilers will not disable warning*/
# define EVALUATE_TO_0_END_DISABLE_WARNINGS 0
# endif
# endif
# define MAKE_CONST_INT(x) MAKE_CONST_INT_HELPER2(x,__LINE__,__COUNTER__)
# define MAKE_CONST_INT_HELPER2(x,line,counter) MAKE_CONST_INT_HELPER(x,line,counter)
# define MAKE_CONST_INT_HELPER(x,line,counter) \
( EVALUATE_TO_0_START_DISABLE_WARNINGS \
+ EVALUATE_TO_0(enum{ INT_CONSTANT_##counter##_AT_LINE_##line = (int)(x) }) \
+ INT_CONSTANT_##counter##_AT_LINE_##line \
+ EVALUATE_TO_0_END_DISABLE_WARNINGS)
#endif
/* ================= test MAKE_CONST_INT() macro ================= */
#define WIDTH 500.5
#define NB 23.2
extern int x[MAKE_CONST_INT(WIDTH/NB)];
Here one example from GNU GCC that can help you:
#pragma GCC diagnostic error "-Wuninitialized"
foo(a); /* error is given for this one */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wuninitialized"
foo(b); /* no diagnostic for this one */
#pragma GCC diagnostic pop
foo(c); /* error is given for this one */
#pragma GCC diagnostic pop
foo(d); /* depends on command-line options */
Related
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).
Considering this piece of code:
#define STC_ASSERT(X,Msg) \
(!!sizeof(struct{char STC_ASSERT;_Static_assert((X),Msg "");}))
#define A 43
#define B 42
#define C (STC_ASSERT(A-B>=0,"")?(A-B):0)
enum { c = C };
int main()
{
STC_ASSERT(1,""); //can this not generate a warning on gcc?
}
Is there anything I can do inside the STC_ASSERT _Static_assert wrapper to prevent the line in main from generating an -Wunused-value warning when compiling with gcc -Wall -Wextra?
(_Pragma with GCC diagnostic push/pop doesn't work here. For a macro like #define FOO 42, embracing it in an expression statement ( ({ 42; }) ) would do the trick but that would prevent the macro from being used in contexts where an Integer Constant Expression is required (such as the enum definition))
What is the alternative token (args...) for the CL compiler?
#define DECLARE_C_ARRAY(__type, __name, __page, __args...) \
enum { __name##_page_size = __page }; \
typedef __type __name##_element_t; \
typedef C_ARRAY_SIZE_TYPE __name##_count_t; \
typedef struct __name##_t {\
volatile __name##_count_t count;\
volatile __name##_count_t size;\
__name##_element_t * e;\
__args ;\
} __name##_t, *__name##_p;
Example for the GCC compiler
Take a look to Variadic macros
Variadic macros are a new feature in C99. GNU CPP has supported them
for a long time, but only with a named variable argument (‘args...’,
not ‘...’ and __VA_ARGS__). If you are concerned with portability to
previous versions of GCC, you should use only named variable
arguments. On the other hand, if you are concerned with portability to
other conforming implementations of C99, you should use only
__VA_ARGS__.
Change
#define DECLARE_C_ARRAY(__type, __name, __page, __args...) \
to
#define DECLARE_C_ARRAY(__type, __name, __page, ...) \
and
__args ;\
to
__VA_ARGS__;\
Unfortunately, this method does not work, if we exclude the args
In this case, remove the semicolon __VA_ARGS__\ (but pass-it when args is used).
An example: http://rextester.com/GYVS61567
I have the same problem as described by Jonathon Reinhart here:
Temporarily disable gcc warning on redefinition
That is because I have to use thrid party libraries (C only) which throws tons of warnings like this
Warning "__always_inline" redefined [enabled by default]
What I want is something like this:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-W???"
#include "someheader.h"
#include "otherheader.h"
#pragma GCC diagnostic pop
Is there a way to disable warnings by gcc which are enabled by default with a
#pragma GCC diagnostic ignored
EDIT:
here is the block causing the warning (file: compiler.h):
#if defined(__CC_ARM)
# define __always_inline __forceinline
#elif (defined __GNUC__)
# define __always_inline inline __attribute__((__always_inline__))
#elif (defined __ICCARM__)
# define __always_inline _Pragma("inline=forced")
#endif
I fixed it by undefining all lines where __always_inline was defined. :-(
Thanks Jasen for helping!
To check that two variables have the same structure type I use a macro
#define assert_same_struct_types(a, b) ((void) (sizeof((a)=(b))))
If some function-like macro
#define m(a,b) blablabla
assumes a and b should be of the same structure type, I add a compile time check:
#define m(a,b) (assert_same_struct_types(a, b), blablabla)
which provokes compiler error if caller of m(a,b) accidentally passes to m different types of structs.
However, this approach doesn't always work for builtin and pointer types due to implicit conversions between them.
So, is it possible to solve this problem for arbitrary types, not necessarily structs?
I need a solution for C89, however, it would be interesting to hear about C99 or C11 possibilities.
#define ASSERT_SAME_TYPE(a, b) ((void) (&(a) == &(b)))
will get you a compile diagnostic and an error with -Werror (for gcc or a similar option for other compilers).
Note that a lot of compilers have a non standard extension typeof operator to get the type of an object and this can be used to check two types are the same.
This questions asks about a C89 solution, so far I didn't find a good way to do this, so instead of relying on C89, you can check if the compiler supports typeof and use it when available. Its not ideal but means as long as some developers use GCC/Clang/IntelC, the error gets caught. of course, if you tell your compiler only to support C89 this isn't going to help.
It gives better type checking but obviously fails to be of any use at all when not supported.
#ifdef __GNUC__
#define CHECK_TYPE(var, type) { \
typeof(var) *__tmp; \
__tmp = (type *)NULL; \
(void)__tmp; \
} (void)0
#define CHECK_TYPE_PAIR(var_a, var_b) { \
typeof(var_a) *__tmp; \
__tmp = (typeof(var_b) *)NULL; \
(void)__tmp; \
} (void)0
#define CHECK_TYPE_PAIR_INLINE(var_a, var_b) ((void)({ \
typeof(var_a) *__tmp; \
__tmp = (typeof(var_b) *)NULL; \
(void)__tmp; \
}))
#else
# define CHECK_TYPE(var, type)
# define CHECK_TYPE_PAIR(var_a, var_b)
# define CHECK_TYPE_PAIR_INLINE(var_a, var_b) (void)0
#endif
/* inline type checking - can mix in with other macros more easily using the comma operator,
* C11 gives best results here */
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
# define CHECK_TYPE_INLINE(val, type) \
(void)((void)(((type)0) != (0 ? (val) : ((type)0))), \
_Generic((val), type: 0, const type: 0))
#else
# define CHECK_TYPE_INLINE(val, type) \
((void)(((type)0) != (0 ? (val) : ((type)0))))
#endif