DevSolar
DevSolar

Reputation: 70263

Ensure preprocessor define does not change value

When implementing Annex K of the C standard (Bounds-checking Interfaces), there is the following requirement:

The extensions specified in this annex can be "requested" to be declared by defining __STDC_WANT_LIB_EXT1__ to 1, and requested to not be declared by defining that to 0.

Then there is this paragraph:

Within a preprocessing translation unit, __STDC_WANT_LIB_EXT1_ _ shall be defined identically for all inclusions of any headers from subclause K.3. If __STDC_WANT_LIB_EXT1_ _ is defined differently for any such inclusion, the implementation shall issue a diagnostic as if a preprocessor error directive were used.

I wonder how to implement this. I went ahead and naïvely wrote this (to be included in each affected header):

#ifndef __STDC_WANT_LIB_EXT1__
  #ifdef __STDC_WANT_LIB_EXT1_PREVIOUS__
    #error __STDC_WANT_LIB_EXT1__ undefined when it was defined previously.
  #endif
#else
  #ifdef __STDC_WANT_LIB_EXT1_PREVIOUS__
    #if __STDC_WANT_LIB_EXT1__ != __STDC_WANT_LIB_EXT1_PREVIOUS__
      #error __STDC_WANT_LIB_EXT1__ defined to different value from previous include.
    #endif
  #else
    #define __STDC_WANT_LIB_EXT1_PREVIOUS__ __STDC_WANT_LIB_EXT1__
  #endif
#endif

This (of course) does not work for a variety of reasons:

...but if taken as pseudocode it showcases the logic behind it.

I am not that well-versed with more intricate preprocessor business like this, as you are usually told to stay away from macro magic. There has to be a way to implement the quoted requirement; it just doesn't "click" for me.

Any ideas?


To complete the [mcve], put the above code into header.h, and this in testme.c:

#define __STDC_WANT_LIB_EXT1__ 0
#include "header.h"
#define __STDC_WANT_LIB_EXT1__ 1
#include "header.h"

int main() {}

This should trigger the "different value" error message.

Upvotes: 4

Views: 216

Answers (1)

DevSolar
DevSolar

Reputation: 70263

@HWalters did set me on the right track:

#ifndef __STDC_WANT_LIB_EXT1__
  #ifdef __STDC_WANT_LIB_EXT1_PREVIOUS__
    #if __STDC_WANT_LIB_EXT1_PREVIOUS__ != -1
      #error __STDC_WANT_LIB_EXT1__ undefined when it was defined earlier.
    #endif
  #else
    #define __STDC_WANT_LIB_EXT1_PREVIOUS__ -1
  #endif
#else
  #ifdef __STDC_WANT_LIB_EXT1_PREVIOUS__
    #if __STDC_WANT_LIB_EXT1__ != __STDC_WANT_LIB_EXT1_PREVIOUS__
      #error __STDC_WANT_LIB_EXT1__ redefined from previous value.
    #endif
  #else
    #if __STDC_WANT_LIB_EXT1__ == 0
      #define __STDC_WANT_LIB_EXT1_PREVIOUS__ 0
    #elif __STDC_WANT_LIB_EXT1__ == 1
      #define __STDC_WANT_LIB_EXT1_PREVIOUS__ 1
    #else
      /* Values other than 0,1 reserved for future use */
      #define __STDC_WANT_LIB_EXT1_PREVIOUS__ -2
    #endif
  #endif
#endif

The "thinko" was this line:

#define __STDC_WANT_LIB_EXT1_PREVIOUS__ __STDC_WANT_LIB_EXT1__

Defining the "previous" to an actual value instand of another token makes it work.

The solution is not perfect, though -- all "other" values except 0,1,undefined get lumped together into a single "previous" value (-2), while the letter of the standard says that any redefinition should issue a diagnostic.

Upvotes: 4

Related Questions