This question is a searching for robust approach to prevent such errors in existing and new projects. A code bellow illustrate the problem. GCC compiler show warning only if macro are conflicts but when it enum vs macro - no.
I can't found gcc -Wxxx options to get warnings for such conflicts.
It's possible to use macro with __COUNTER__ instead of enum.
It's possible to change format rules for enum names.
But it's helpful solutions only for new projects.
What I can do with old projects ?
enum my_enum {
NONE = -1,
// #define NONE -1
ONE = 1,
TWO,
THREE,
};
// use name NONE something else in included header
#define NONE 2
#include "stdio.h"
int main(void)
{
printf("NONE is %d, it's %s\n",
NONE,
(-1 == NONE) ? "OK" : "FAIL");
return 0;
}
So result with commented macro NONE -1:
gcc ./c-test.c -o c-test && ./c-test
NONE is 2, it's FAIL
And uncommented.
gcc ./c-test.c -o c-test && ./c-test
./c-test.c:11: warning: "NONE" redefined
11 | #define NONE 2
|
./c-test.c:4: note: this is the location of the previous definition
4 | #define NONE -1
|
Env:
Linux 5.4.0-132-generic #148-Ubuntu
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
Related
This is a 40-line MCVE (Minimal, Complete, Verifiable Example) — or something close to minimal — cut down from a 1675 line source file that originally included 32 headers (and most of those included multiple other headers — compiling it with gcc -H lists 464 headers from the project and the system, many of them several times). That file is working code that previously compiled without warnings (GCC 8.3.0), but not with GCC 9.1.0. All structure, function, type, variable names have been changed.
pf31.c
#include <string.h>
enum { SERVERNAME_LEN = 128 };
typedef struct ServerQueue
{
char server_name[SERVERNAME_LEN + 1];
struct ServerQueue *next;
} ServerQueue;
extern int function_under_test(char *servername);
#ifdef SUPPRESS_BUG
extern int function_using_name(char *name);
#endif /* SUPPRESS_BUG */
extern int GetServerQueue(const char *servername, ServerQueue *queue);
int
function_under_test(char *servername)
{
ServerQueue queue;
char name[SERVERNAME_LEN + 1];
if (GetServerQueue(servername, &queue) != 0)
return -1;
char *name_in_queue = queue.server_name;
if (name_in_queue)
strncpy(name, name_in_queue, SERVERNAME_LEN);
else
strncpy(name, servername, SERVERNAME_LEN);
name[SERVERNAME_LEN] = '\0';
#ifdef SUPPRESS_BUG
return function_using_name(name);
#else
return 0;
#endif /* SUPPRESS_BUG */
}
Compilation
When compiled using GCC 9.1.0 (on a Mac running macOS 10.14.5 Mojave, or on a Linux VM running RedHat 5.x — don't ask!), with the option -DSUPPRESS_BUG I get no error, but with the option -USUPPRESS_BUG, I get an error:
$ gcc -std=c11 -O3 -g -Wall -Wextra -Werror -DSUPPRESS_BUG -c pf31.c
$ gcc -std=c11 -O3 -g -Wall -Wextra -Werror -USUPPRESS_BUG -c pf31.c
In file included from /usr/include/string.h:417,
from pf31.c:1:
pf31.c: In function ‘function_under_test’:
pf31.c:30:9: error: ‘__builtin_strncpy’ output may be truncated copying 128 bytes from a string of length 128 [-Werror=stringop-truncation]
30 | strncpy(name, name_in_queue, SERVERNAME_LEN);
| ^~~~~~~
cc1: all warnings being treated as errors
$
When I compile using GCC 8.3.0, I get no errors reported.
Question
Two sides of one question:
Why does GCC 9.1.0 complain about the use of strncpy() when the code is compiled with -USUPPRESS_BUG?
Why doesn't it complain when the code is compiled with -DSUPPRESS_BUG?
Corollary: is there a way to work around this unwanted warning that works with older GCC versions as well as 9.1.0. I've not yet found one. There's also a strong element of "I don't think it should be necessary, because this is using strncpy() to limit the amount of data copied, which is what it is designed for".
Another variant
I have another non-erroring variant, changing the signature of the function_under_test() — here's a set of diffs:
11c11
< extern int function_under_test(char *servername);
---
> extern int function_under_test(char *servername, ServerQueue *queue);
20c20
< function_under_test(char *servername)
---
> function_under_test(char *servername, ServerQueue *queue)
22d21
< ServerQueue queue;
25c24
< if (GetServerQueue(servername, &queue) != 0)
---
> if (GetServerQueue(servername, queue) != 0)
27c26
< char *name_in_queue = queue.server_name;
---
> char *name_in_queue = queue->server_name;
This compiles cleanly regardless of whether SUPPRESS_BUG is defined or not.
As you can guess from the SUPPRESS_BUG terminology, I'm tending towards the view that this is bug in GCC, but I'm kinda cautious about claiming it is one just yet.
More about the the original code: the function itself was 540 lines long; the strncpy() block occurs about 170 lines into the function; the variable corresponding to name was used further down the function in a number of function call, some of which take name as an argument and supply a return value for the function. This corresponds more to the -DSUPPRESS_BUG code, except that in the 'real code', the bug is not suppressed.
This is a GCC bug tracked as PR88780. According to Martin's comment, this warning did not exist prior to GCC 8.
GCC is shipped with this known bug, as it is not deemed release-critical.
To be honest, I am not 100% sure it is the bug. The point is, there are known false-positives. If you feel like helping the GCC project, you can find the most appropriate bug among strncpy / Wstringop-truncation bugs and post your example there. It would be more helpful if you minimized it further (say, with creduce); minimizing the compile string is also appreciated (that would be rather trivial, I guess).
Several compilation warnings related to strncpy were found in GCC 9.0 and reported here and here.
One of them is the error mentioned in the question which seems to occur in the file string_fortified.h:
/usr/include/bits/string_fortified.h:106:10: warning: ‘__builtin_strncpy’ output may be truncated copying 16 bytes from a string of length 16 [-Wstringop-truncation]
106 | return __builtin___strncpy_chk (__dest, __src, __len, __bos (__dest));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The response to this was given on April 15, 2019 was:
Thank you for the report, however as GCC 9 still under development. We do not see the above errors in the current stable GCC 7.4 or GCC 8.3. We appreciate the advanced notice, and will accept PRs to fix issues against GCC 9, but for now our target compiler is gcc stable.
So I believe the errors are probably a result of versions 9 and 9.1 being not stable versions. Hopefully they will be eliminated when these versions become stable.
I am using go-hdf5 to read an hdf5 file into golang. I am on windows7 using a pretty recent copy of mingw and hdf5 1.8.14_x86 and it seems like trying to use any of the predefined types doesn't work, let's focus for example on T_NATIVE_UINT64. I have reduced the issue to the following, which basically leaves go-hdf5 out of the problem and points at something quite fundamental going wrong:
package main
/*
#cgo CFLAGS: -IC:/HDF_Group/HDF5/1.8.14_x86/include
#cgo LDFLAGS: -LC:/HDF_Group/HDF5/1.8.14_x86/bin -lhdf5 -lhdf5_hl
#include "hdf5.h"
#include <stdio.h>
void print_the_value2() { printf("the value of the constant is %d\n", H5T_NATIVE_UINT64); }
*/
import "C"
func main() {
C.print_the_value2()
}
You obviously need to have hdf5 and point the compiler at the headers/dlls and running go get, then executing prints this on my pc
the value of the constant is -1962924545
Running variations of the above, in how/where the constant is read, will give different answers for the value of H5T_NATIVE_UINT64. However I am pretty sure that is none are the right value and in fact trying to use a type with the id returned doesn't work, unsurprisingly.
If I write and run a "real" C program, I get different results
#include <stdio.h>
#include "hdf5.h"
hid_t _go_hdf5_H5T_NATIVE_UINT64() { return H5T_NATIVE_UINT64; }
int main()
{
printf("the value of the constant is %d", _go_hdf5_H5T_NATIVE_UINT64());
}
Compiling using
C:\Temp>gcc -IC:/HDF_Group/HDF5/1.8.14_x86/include -LC:/HDF_Group/HDF5/1.8.14_x86/bin -lhdf5 -lhdf5_hl -o stuff.exe stuff.c
and running gives me
the value of the constant is 50331683
And that appears to be the right value as I can use it directly from my go program. Obviously I want to be able to use the constants instead. Any idea why this could be happening?
Extra info following comments below:
I looked for the definition of H5T_NATIVE_UINT64 in the hdf5 headers and see the following
c:\HDF_Group\HDF5\1.8.14_x86\include>grep H5T_NATIVE_UINT64 *
H5Tpkg.h:H5_DLLVAR size_t H5T_NATIVE_UINT64_ALIGN_g;
H5Tpublic.h:#define H5T_NATIVE_UINT64 (H5OPEN H5T_NATIVE_UINT64_g)
H5Tpublic.h:H5_DLLVAR hid_t H5T_NATIVE_UINT64_g;
The whole header is here
http://www.hdfgroup.org/ftp/HDF5/prev-releases/hdf5-1.8.14/src/unpacked/src/H5Tpublic.h
Thanks!
H5T_NATIVE_UINT64 is NOT a constant but a #define that ultimately evaluates to (H5Open(), H5T_NATIVE_UINT64_g), which cgo does not understand.
It's easy to check by turning on debug output on gcc's preprocessor:
gcc -E -dM your_test_c_file.c | grep H5T_NATIVE_UINT64
Result:
#define H5T_NATIVE_UINT64 (H5OPEN H5T_NATIVE_UINT64_g)
Now the same for H5OPEN:
gcc -E -dM test_go.c | grep '#define H5OPEN'
gives:
#define H5OPEN H5open(),
Right now, cgo does understand simple integer constant defines like #define VALUE 1234, or anything that the gcc preprocessor will turn into an integer constant. See the function func (p *Package) guessKinds(f *File) in $GOROOT/src/cmd/cgo/gcc.go.
I'm attempting to refactor a piece of legacy code and I'd like a snapshot of all of the macros defined at a certain point in the source. The code imports a ridiculous number of headers etc. and it's a bit tedious to track them down by hand.
Something like
#define FOO 1
int myFunc(...) {
PRINT_ALL_DEFINED_THINGS(stderr)
/* ... */
}
Expected somewhere in the output
MACRO: "FOO" value 1
I'm using gcc but have access to other compilers if they are easier to accomplish this task.
EDIT:
The linked question does not give me the correct output for this:
#include <stdio.h>
#define FOO 1
int main(void) {
printf("%d\n", FOO);
}
#define FOO 0
This very clearly prints 1 when run, but gcc test.c -E -dM | grep FOO gives me 0
To dump all defines you can run:
gcc -dM -E file.c
Check GCC dump preprocessor defines
All defines that it will dump will be the value defined (or last redefined), you won't be able to dump the define value in all those portions of code.
You can also append the option "-Wunused-macro" to warn when macros have been redefined.
In code reviews I ask for option (1) below to be used as it results in a symbol being created (for debugging) whereas (2) and (3) do not appear to do so at least for gcc and icc. However (1) is not a true const and cannot be used on all compilers as an array size. Is there a better option that includes debug symbols and is truly const for C?
Symbols:
gcc f.c -ggdb3 -g ; nm -a a.out | grep _sym
0000000100000f3c s _symA
0000000100000f3c - 04 0000 STSYM _symA
Code:
static const int symA = 1; // 1
#define symB 2 // 2
enum { symC = 3 }; // 3
GDB output:
(gdb) p symA
$1 = 1
(gdb) p symB
No symbol "symB" in current context.
(gdb) p symC
No symbol "symC" in current context.
And for completeness, the source:
#include <stdio.h>
static const int symA = 1;
#define symB 2
enum { symC = 3 };
int main (int argc, char *argv[])
{
printf("symA %d symB %d symC %d\n", symA, symB, symC);
return (0);
}
The -ggdb3 option should be giving you macro debugging information. But this is a different kind of debugging information (it has to be different - it tells the debugger how to expand the macro, possibly including arguments and the # and ## operators) so you can't see it with nm.
If your goal is to have something that shows up in nm, then I guess you can't use a macro. But that's a silly goal; you should want to have something that actually works in a debugger, right? Try print symC in gdb and see if it works.
Since macros can be redefined, gdb requires the program to be stopped at a location where the macro existed so it can find the correct definition. In this program:
#include <stdio.h>
int main(void)
{
#define X 1
printf("%d\n", X);
#undef X
printf("---\n");
#define X 2
printf("%d\n", X);
}
If you break on the first printf and print X you'll get the 1; next to the second printf and gdb will tell you that there is no X; next again and it will show the 2.
Also the gdb command info macro foo can be useful, if foo is a macro that takes arguments and you want to see its definition rather than expand it with a specific set of arguments. And if a macro expands to something that's not an expression, gdb can't print it so info macro is the only thing you can do with it.
For better inspection of the raw debugging information, try objdump -W instead of nm.
However (1) is not a true const and cannot be used on all compilers as an array size.
This can be used as array size on all compilers that support C99 and latter (gcc, clang). For others (like MSVC) you have only the last two options.
Using option 3 is preferred 2. enums are different from #define constants. You can use them for debugging. You can use enum constants as l-value as well unlike #define constants.
Here's my code:
#include <stdio.h>
#include <CL/cl.h>
#include <CL/cl_platform.h>
int main(){
cl_float3 f3 = (cl_float3){1, 1, 1};
cl_float3 f31 = (cl_float3) {2, 2, 2};
cl_float3 f32 = (cl_float3) {2, 2, 2};
f3 = f31 + f32;
printf("%g %g %g \n", f3.x, f3.y, f3.z);
return 0;
}
When compiling with gcc 4.6, it produces the error
test.c:14:11: error: invalid operands to binary + (have ‘cl_float3’ and ‘cl_float3’)
Very strange to me, because the OpenCL Specification demontrates in section 6.4 just that, an addition of two floatn. Do I need to include any other headers?
But even more strange is that when compiling with -std=c99 I get errors like
test.c:16:26: error: ‘cl_float3’ has no member named ‘x’
..for all components (x, y and z)...
The reason for the compilation problem with structure subscripts can be seen in the implementation of the standard in the AMD SDK.
If you look at the <CL/cl_platform.h> header from AMD toolkit you could see how the structures are defined.
typedef cl_float4 cl_float3;
typedef union
{
cl_float CL_ALIGNED(16) s[4];
#if (defined( __GNUC__) || defined( __IBMC__ )) && ! defined( __STRICT_ANSI__ )
__extension__ struct{ cl_float x, y, z, w; };
....
#endif
}cl_float4;
The #if clause is ignored when gcc is invoked with --std=c99.
To make your code work with --std=c99 you could replace references to f3.x with f3.s[0] and so on.
OpenCL programs consist of two parts.
A program which runs on the host. This is normally written in C or C++, but it's nothing special except that it uses the API described in sections 4 & 5 of the OpenCL Specification.
A kernel which runs on the OpenCL device (normally a GPU). This is written in the language specified in section 6. This isn't C, but it's close. It adds things like vector operations (like you're trying to use). This is compiled by the host program passing a string which contains the kernel code to OpenCL via the API.
You've confused the two, and tried to use features of the kernel language in the host code.
cl_float.v is another option:
#include <assert.h>
#include <CL/cl.h>
int main(void) {
cl_float4 f = {{1, 2, 3, 4}};
cl_float4 g = {{5, 6, 7, 8}};
cl_float4 h;
h.v4 = f.v4 + g.v4;
assert(h.s[0] == 6);
assert(h.s[1] == 8);
return EXIT_SUCCESS;
}
which can be run as:
gcc -std=c89 -Wall -Wextra tmp.c -lOpenCL && ./a.out
in Ubuntu 16.10, gcc 6.2.0.
v is defined in Linux GCC x86 via GCC vector extensions.
The file https://github.com/KhronosGroup/OpenCL-Headers/blob/bf0f43b76f4556c3d5717f8ba8a01216b27f4af7/cl_platform.h contains:
#if defined( __SSE__ )
[...]
#if defined( __GNUC__ )
typedef float __cl_float4 __attribute__((vector_size(16)));
[...]
#define __CL_FLOAT4__ 1
and then:
typedef union
{
[...]
#if defined( __CL_FLOAT4__)
__cl_float4 v4;
#endif
}cl_float4;
Not sure this barrage of ifdefs was a good move by Khronos, but it is what we have.
I recommend that you just always use .s[0], which is the most portable option. We shouldn't need to speedup the host SIMD if we are focusing on the GPU...
C11 anonymous structs
The error error: ‘cl_float3’ has no member named ‘x’ happens because of the lines mentioned at: https://stackoverflow.com/a/10981639/895245
More precisely, this feature is called "anonymous struct", and it is an extension that was standardized in C11.
So in theory it should also work with -std=c11, but it currently doesn't because the CL headers weren't updated to check for C11, see also: https://github.com/KhronosGroup/OpenCL-Headers/issues/18