3

Is there a better way to do this (in GCC C)?

I'm trying to define some symbols representing the hardware platform, to be used for conditional compilation.

But I also want printable strings describing the hardware (for diagnostics).

Ideally I'd like be able to do:

#define HARDWARE "REV4C"

#if (HARDWARE == "REV4C")
    #define LED_RED      // define pin addresses, blah blah...
#endif

printf("HARDWARE %s\n", HARDWARE);

But I don't think that's allowed in C. This works, but it's ugly:

#define REV4C   (403)    // symbols for conditional compilation
#define REV421  (421) 

//#define HARDWARE REV4C // choose hardware platform (just one)
#define HARDWARE REV421

#if (HARDWARE == REV421) // define strings for printing
    #define HARDWARE_ID "REV421"
#elif (HARDWARE == REV4C)
    #define HARDWARE_ID "REV4C"
#else
    #define HARDWARE_ID "unknown"
#endif

#if (HARDWARE == REV421)
    #define LED_RED      // define pin addresses, blah blah...
#endif

/* ... */

printf("HARDWARE_ID %s\n", HARDWARE_ID);

This is ugly because it requires two separate symbols, HARDWARE (an integer, for comparison) and HARDWARE_ID (a string, for printing). And logic to generate the value of HARDWARE_ID.

Is there a better way?


Edit: I don't think this is a duplicate of how to compare string in C conditional preprocessor-directives.

That question (and answer) doesn't address how to get a printable string without ending up with two similar symbols.

(I did look at that answer before posting this question.)

Community
  • 1
  • 1
nerdfever.com
  • 1,652
  • 1
  • 20
  • 41
  • Your second suggestion isn't so bad. Of course, you should not repeat the `#if` blocks for each thing you define, but define everything together. This approach also lets you control the valid values and allows you to issue an `#error` in the `#else` clause when no hardware was specified. (In `#if`s, unknown macros have a value of zero.) – M Oehm Dec 18 '15 at 21:07
  • @JosephQuinsey That question doesn't address how to get a printable string. – nerdfever.com Dec 18 '15 at 21:44
  • To get a printable string, see Jens Gustedt's answer [Way to compare strings in C preprocessor? (GCC)](//stackoverflow.com/a/34364515). – Joseph Quinsey Jul 31 '18 at 16:25

3 Answers3

6

The usual way of doing it is just to define the required configuration symbol, and then conditionally to define all of the others:

// Select config here
#define REV4C
//#define REV421

#ifdef REV4C
    #define HARDWARE_ID "REV4C"
    #define LED_RED
#elif defined(REV421)
    #define HARDWARE_ID "REV421"
    // .....
#else
//.......
#endif
Eugene Sh.
  • 17,802
  • 8
  • 40
  • 61
2

To do the actual case analysis look at the comment that marks this as duplicate. To actually also have the value as a string you can use stringification, something like

#define STRINGIFY(...) #__VA_ARGS__
#define HARDWARE_STR STRINGIFY(HARDWARE)
Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
2

A completely different approach would be to move the string-dependent logic to the build.

The C pre-processor is quite limited, but make (along with shell commands in rules) is the perfectly flexible string processor. E.g. with GNU make:

ifeq ($(CONFIG),REV4C)
    HARDWARE_ID = \"REV4C\"
else
    HARDWARE_ID = \"DEFAULT\"
endif
CFLAGS += -DHARDWARE_ID=$(HARDWARE_ID)

and call as

gmake CONFIG=REV4C
Jens
  • 69,818
  • 15
  • 125
  • 179