I'd like to generate a compiler warning for a specific situation. I'm building a sort of universal code that will be built based on several pre-compiler definitions. For instance I might have something like the following.
sdcard.h
#ifdef PART_BOARD1
#include "port/board1.h"
#elif PART_BOARD2
#include "port/board2.h"
#endif
extern void sdcardConfigure(void);
sdcard.c
#ifndef FLAG_SDCARD
#warning "No SD Card available for this board, or this board has not been defined"
#define sdcardConfigure(...) void(0)
#endif
port/board1.h (similar for port/board2)
#define FLAG_SDCARD
void sdcardConfigure(void);
port/board1.c
void sdcardConfigure(void) {
// sd card is configured here
}
Essentially what I'd like to do is modify the #define sdcardConfigure(...) statement in sdcard.c so that I'm only seeing the warning message if sdcardConfigure() is actually called. Making it act like:
void sdcardConfigure(void) {
#warning "sdcardConfigure() not available on this board"
}
The idea being that this gives me the flexibility to create a universal (I know many fear universal) program that adapts to the parts that it's using. I.E. if there is an SD Card use it, otherwise generate a warning saying "you're calling a function that doesn't exist for this part" and let the developer decide if the function is needed.
The answer to this will most-likely be compiler-dependent.
For gcc et al you can use __attribute__ ((deprecated)) (see gcc manual).
For Visual Studio you can use __declspec(deprecated).
Related
I'm trying to install the NVIDIA version of an externally supplied toolkit (for the purposes of this message it doesn't matter what the toolkit is, this is a problem about how to use nvcc), and I'm getting error messages like "usr/include/c++/6/utility(329): error: this declaration may not have extern "C" linkage".
I'm not a C or C++ programmer, but I am happy enough poking around in things like Makefiles. I'm pretty sure that I've got all the paths set to point to the right places, and /usr/include/c++/6 contains all the files that are generating the error messages. But I have no idea what these error messages mean and what I should do to get round them.
I believe these errors are from C/C++ name mangling differences and the fact that NVCC compiles as a C++ compiler. I was able to compile HTK 3.5 by simply removing the extern "C" declarations from HCUDA.cu:
#ifdef __cplusplus
extern "C" {
#endif
/* ... */
#ifdef __cplusplus
}
#endif
I suspect that, because they're already declared extern "C" in HCUDA.h, they don't need to be declared such in HCUDA.cu, but I'm not sure.
I don't have much experience with C and am now working on a HPC project which very often uses __attribute__ ((always_inline)) void foo() function definitions.
I see the point of doing it for performance runs, but for debugging, testing, and developing, the compilation time is way to long. Just earlier I wanted to add a simple printf in a function to see when the function is called, and had to wait 5min for it to compile.
Is there a possibility to pass gcc a flag to ignore the attribute requests? I already am compiling with the lowest level of optimization.
A hackish way of disabling the attribute might be to define it away in the preprocessor:
#define always_inline noinline
A better approach, however, would be to apply those attributes in a macro so that they can be configured globally, e.g.
#ifndef DEBUG
# define HOT_FUNCTION __attribute__ ((always_inline))
#else
# define HOT_FUNCTION /* nothing */
#endif
…
HOT_FUNCTION void foo() { … }
I'm looking for a (clean) way of writing a function definition and a function prototype without code duplication. Since DRY is well established as a good idea and hand coding prototypes in header files is a clear violation this seems like a reasonable requirement.
The example code below indicates a (crude) way of solving the problem with the preprocessor. It seems unlikely to be optimal, but does appear to work correctly.
Using separate files and duplication:
foo.h:
#ifndef FOO_H
#define FOO_H
// Normal header file stuff
int dofoo(int a);
#endif /* FOO_H */
foo.c:
#include "foo.h"
int dofoo(int a) {
return a * 2;
}
Using the C preprocessor:
foo.h:
#ifndef FOO_H
#define FOO_H
// Normal header file stuff
#ifdef PROTOTYPE // if incorrect:
// No consequences for this test case, but we lose a sanity check
#error "PROTOTYPE set elsewhere, include mechanism will fall over"
#endif
#define PROTOTYPE // if incorrect:
// "error: redefinition of 'dofoo'" in clang & gcc,
// referring to int dofoo() line in foo.c
#include "foo.c"
#undef PROTOTYPE //if incorrect:
// No warnings, but should trigger the earlier #error statement if
// this method is used in more than one file
#endif /* FOO_H */
foo.c:
#include "foo.h"
int dofoo (int a)
#ifdef PROTOTYPE // if incorrect:
// "error: redefinition of 'dofoo'" in clang & gcc,
// referring to int dofoo() line in foo.c
;
#else
{
return a * 2;
}
#endif
The mechanism is a bit odd - the .h file doesn't conventionally include the .c file! The include guard halts the recursion. It compiles cleanly and looks reasonable when run through a standalone preprocessor. Otherwise though, embedding preprocessor conditionals throughout the source doesn't look great.
There are a couple of alternative approaches I can think of.
Don't worry about the code duplication
Change to a language which generates the interface automatically
Use a code generator (e.g. sqlite's makeheaders)
A code generator would work but seems overkill as a solution for a minor annoyance. Since C has been around for somewhere over 25 years at this point there's hopefully a community consensus on the best path to take.
Thank you for reading.
edit: Compiler warnings with gcc 4.8.2 and clang 5.1
Messing up the macro statements produces fairly coherent compiler error messages. Missing an #endif (easily done if the function definition is long) produces "error: unterminated #else" or "error: unterminated conditional directive", both referring to the #ifdef line.
Missing #else means the code is no longer valid C. gcc "error: expected identifier or '(' before '{' token" and clang adds "expected function body after function declarator". Both point to the correct line number, but neither suggest an #else is missing.
Spelling PROTOTYPE wrong produces coherent messages if the result is fatal and no warning if the result doesn't matter. The compiler warnings aren't quite as specific as they can be when definition and declaration differ, but they're probably specific enough.
The generally accepted path is your option 1), to not worry and just write the declaration twice.
The repetition coming from prototypes is only a small percentage compared to the function implementations. Macro hacks like in your question quickly become unwieldy and provide little gain. The macro machinery ends up being just as much code as the original prototypes, only that it's now much harder to understand what's going on and that you'll get more cryptic error messages. The trivial to understand duplication gets replaced by about the same amount of much harder to understand trickery.
With normal prototypes the compiler will issue warnings when things don't match up, with such a macro base solution you get hard to understand errors if you forget an #endif or something else doesn't match up. For example any mention of foo.c in an error might be with or without PROTOTYPE defined.
I would like to take a look at it from another point of view. As I like to see DRY principle, it is meaningful for the code that provides logic, not taking it as repeating strings literally.
This way it would not touch declarations, as they introduce no logic. When you see few pieces of code, that do (as in perform some task) the same, just arguments change, then it should be avoided/refactored.
And this is what you actually do. You just introduced some new pre-processing logic into code, i.e. #ifdef PROTOTYPE... #else ... #endif, that you will repeat over and over just changing the prototype and the body. If you could wrap it up into something that does not enforce to repeat the branch I'd say it is somewhat ok.
But currently you really do repeat some logic in code, just to eliminate a multiple declarations, which is basically harmless in the context you provide. If you forget something the compiler will tell you something is mismatched. It's c.
I'd say your proposed approach violates it more, than repeated declarations.
Hi I am compiling ffmpeg using xcode, which I believe uses clang for compilation. In ffmpeg there is a struct with a member variable named 'class' I believe this is perfectly fine in C but clang is trying to parse it as a keyword. Any idea how to fix? Basically the following in a cpp file will cause the error:
extern C {
typedef struct {
int class;
} SomeStruct;
}
It tries to interpret class as a keyword.
FYI the file that is throwing the error in ffmpeg is libavcodec/mpegvideo.h and I need to include this to have access to the MpegEncContext struct to pull out motion map info.
EDIT
The above code sample was just to demonstrate the error. But perhaps its fixable in another way. In my actual code I have it like this:
#ifdef __cplusplus
extern "C" {
#endif
#include "libavcodec/mpegvideo.h"
#include "libavformat/avformat.h"
#if __cplusplus
} //Extern C
#endif
How would I get that to include the two files as C files and not C++?
Thanks
It's completely fine in C. When you build that as C++, you encounter an error because class is a C++ keyword.
As far as fixing it, you would normally choose an identifier other than class. However, ffmpeg developers may not be so agreeable with that change. Therefore, you may need to either:
restrict the visibility of that header to C translations
or edit your own copy in order to use it in C++ translations
Fortunately, you are also using a C compiler which has good support of C99 features in this case. C Compilers which do not support C99 well are particularly troublesome with ffmpeg sources (because you would then compile the whole program as C++ for the C99 features, and the conflict count would be much higher).
(there are other dirty tricks you could do to try to work around the problem, but i will not mention them)
Basically the following in a cpp file will cause the error
.cpp files are processed as C++ files, not C, and class is a reserved word in C++.
If you don't have a choice to rename anything in those header files, you could just replace the class token by something else
#ifdef __cplusplus
extern "C" {
# define class videoClass
#endif
#include "libavcodec/mpegvideo.h"
#include "libavformat/avformat.h"
#if __cplusplus
# undef class
} //Extern C
#endif
This is quite a dirty hack, but for such badly interfaced code you don't have much choice. The real solution would be to have all the struct members in these files use names that us some sort of prefix or so, as it is done in the network layer code. There all members have some prefixes as ss_ or sa_ and such problems are very unlikely to occur.
Let's assume I define BAR in foo.h. But foo.h might not exist. How do I include it, without the compiler complaining at me?
#include "foo.h"
#ifndef BAR
#define BAR 1
#endif
int main()
{
return BAR;
}
Therefore, if BAR was defined as 2 in foo.h, then the program would return 2 if foo.h exists and 1 if foo.h does not exist.
In general, you'll need to do something external to do this - e.g. by doing something like playing around with the search path (as suggested in the comments) and providing an empty foo.h as a fallback, or wrapping the #include inside a #ifdef HAS_FOO_H...#endif and setting HAS_FOO_H by a compiler switch (-DHAS_FOO_H for gcc/clang etc.).
If you know that you are using a particular compiler, and portability is not an issue, note that some compilers do support including a file which may or may not exist, as an extension. For example, see clang's __has_include feature.
Use a tool like GNU Autoconf, that's what it's designed for. (On windows, you may prefer to use CMake).
So in your configure.ac, you'd have a line like:
AC_CHECK_HEADERS([foo.h])
Which, after running configure, would define HAVE_FOO_H, which you can test like this:
#ifdef HAVE_FOO_H
#include "foo.h"
#else
#define BAR 1
#endif
If you intend to go down the autotools route (that is autoconf and automake, because they work well together), I suggest you start with this excellent tutorial.