Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compiler warning about missing preprocessor symbol [duplicate]

Background

I have C project which has a configuration header file, for example:

// config.h
#define FEATURE_1_AVAILABLE      1
#define FEATURE_2_AVAILABLE      0
#define FEATURE_3_AVAILABLE      1

Modules use these flags to include/exclude features from build:

#include "config.h"
void foo(void) {
    ...
    #if FEATURE_1_AVAILABLE
    useFeature1();
    #endif
    ...

There are different versions of config.h for different builds.

Problem

#include "config.h" got accidentally removed, so feature wasn't active even when it was enabled in config.

Unfortunately #if FEATURE_1_AVAILABLE silently evaluates to 0 when it's not defined. So I need an error if it's not defined.

Option 1

I could add extra 'if defined' test on each condition.

#ifndef FEATURE_1_AVAILABLE
    #error No feature 1  
#elif FEATURE_1_AVAILABLE
    useFeature1();
#endif

Downside is that one feature flag may be tested in many different places in module, and adding multiple preprocessor lines is not going to make code prettier.

Option 2

Add single test at the beginning of the module:

#ifndef FEATURE_1_AVAILABLE
    #error  No feature 1  
#endif

But this can be lost/forgotten for the same reasons that #include "config.h" was.

Option 3

Define flags as macros:

#define FEATURE_1_AVAILABLE()      1

and use as:

#if FEATURE_1_AVAILABLE()

Unfortunaltely this will also fail silently, if you forget the parenthesis:

#if FEATURE_1_AVAILABLE

Option X

Is there a better option? Something that will give error message at compile time if symbol is not defined or is incorrectly written. And hopefully doesn't make code look unmaintainable preprocessor mess.

Other things

  • Runtime checks are not possible. Disabled features must not be in the binary.
  • Features must be defined in file. Defining them as global compiler options has other problems.
  • This for embedded system. Some of these features require hardware to work, and testing is very time consuming. So having errors at compile time is preferred.
like image 867
user694733 Avatar asked Jan 23 '15 08:01

user694733


People also ask

How do I turn off GCC warnings?

You can request many specific warnings with options beginning with ' -W ', for example -Wimplicit to request warnings on implicit declarations. Each of these specific warning options also has a negative form beginning ' -Wno- ' to turn off warnings; for example, -Wno-implicit .

What is #warning in C?

#warning is a preprocessor directive in C which is used to raise compilation warnings for various situations. It is different from #error directive in that #warning will not cancel the compilation process while #error will cancel it.

What is the purpose of the preprocessor directive error?

A preprocessor error directive causes the preprocessor to generate an error message and causes the compilation to fail. The #error directive is often used in the #else portion of a #if – #elif – #else construct, as a safety check during compilation.


2 Answers

Another solution is to have the compiler warn you if a macro being evaluated is not defined.

With gcc for example there is the -Wundef option (not included in -Wall):

#if FEATURE_1_AVAILABLE
useFeature1();
#endif

this gives the warning (add -Werror to make it an error):

tst.c:6:9: warning: "FEATURE_1_AVAILABLE" is not defined [-Wundef]
     #if FEATURE_1_AVAILABLE

This option is also available with other compilers, e. g., clang and icc.

like image 170
ouah Avatar answered Sep 30 '22 07:09

ouah


I know it is being frowned upon, but for smaller C projects there is nothing wrong with having one project-wide include file (say myproject.h) with all your #include .. directives (like #include "config.h") , and then have only #include "myproject.h at the top of each source file.

Accidentally forgetting this single #include will usually break the compile immediately, so that won't happen unnoticed.

Alternatively, you could put one essential #include (say #include assert.h) in your config.h (and only there), again to annoy the compiler/linker when you forget to include it

like image 30
Hans Lub Avatar answered Sep 30 '22 08:09

Hans Lub