Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Foolproof way to do configuration #define/#ifdef

Tags:

c++

c

I'm working with a moderately sized embedded C++ project that has a number of configurations for different target products. There are a good number of macros that get set for various configuration items in the different configurations and I'm trying to make that system as error-proof as possible.

Initially, I was just doing the standard thing:

#define CFG_FOO
    
#ifdef CFG_FOO
<code here>
#endif

but I'm always afraid I'm going to mistype a macro name in an ifdef and have a hard to find bug, because this evaluates to false without error:

#ifdef CFG_FOOO

So, I changed all the macros to this format, which requires that the macro in question be defined, defining all the ones that I want to evaluate as false to 0:

#define CFG_FOO() (1)
#define CFG_BAR() (0)

#if CFG_FOO()
<code is active>
#endif

#if CFG_BAR()
<code is inactive>
#endif

// Produces error, as desired:
#ifdef CFG_FOOO()
#endif

This was good, except that then if I accidentally enter the following (which I found I do all the time, just out of habit) it is true and the contained code is compiled:

#ifdef CFG_BAR
<this is active>
#endif

So I'm looking for a solution that:

  1. Always generates an error for mistyped CFG_xxx item.
  2. Doesn't allow for unintended consequences if using the wrong directive #if vs. #ifdef (it's fine if there's a error for "incorrect" usage.)
  3. Doesn't require additional libraries/frameworks (like Boost).
  4. Doesn't require an additional tool to process all the code (this is what I'm doing now, scanning for any #ifdef and generating an error, but that's not ideal.)
  5. Actually removes the unneeded code. A runtime solution is probably impractical as the code size needs to be tightly controlled.

NOTE: I'm aware of the -Wundef option for gcc, but I don't believe that really helps, as the accidental #ifdef situation is still present.

like image 685
ned3000 Avatar asked Sep 11 '25 12:09

ned3000


2 Answers

Instead of repeatedly do

#if CFG_FOO()
<code is active>
#endif

since C99 or C++11, you might have once (per config)

#if CFG_FOO() // or #ifdef CFG_FOO
# define WITH_FOO(...) __VA_ARGS__
#else
# define WITH_FOO(...) /*Empty*/
#endif

And then

WITH_FOO(
<code is active>
)

Note:

  • It might probably break auto-indentation.
  • Not sure it is better than traditional way more prone to typo.
like image 174
Jarod42 Avatar answered Sep 13 '25 02:09

Jarod42


My best recommendation is never get yourself into a situation where CFG_FOO is valid, CFG_BAR is valid, but both together is not valid.

We can do better by simply avoiding this problem. Specialized form for a switch ladder. CFG is a bad prefix but I'm assuming it's an artifact of minimalization and you simply will have a better one.

modeswitch.h:

#define CFGMODE_FOO 1
#define CFGMODE_BAR 2

header.h:

#if CFG == CFGMODE_FOO
#elif CFG == CFGMODE_BAR
#else
#error CFG has unsupported value
#endif

program.c

#include "modeswitch.h"
#define CFG CFGMODE_FOO
#include "header.h"

If I read this wrong and you're not using this stuff in .h files than I wonder why you have both C and C++ tags but just inline the stuff and it will still work.

My understanding is there's enough power in ## that there's a way to get rid of the pre-header but it's so hard that it doesn't meet any reasonable definition of foolproof.

like image 35
Joshua Avatar answered Sep 13 '25 03:09

Joshua