4

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:

  • Does not catch the case when __STDC_WANT_LIB_EXT1__ is not defined for the first include, but defined for the second (which should also be caught with an #error)
  • The #define does not take the value of __STDC_WANT_LIB_EXT1__ (prefixing # would take the symbol as string, going through symbol2value(...) would take the 1 as string).
  • ...

...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.

14
  • Is there a reason why you don't want to activate it via a compiler option -D__STDC_WANT_LIB_EXT1__=1 ? Commented May 6, 2019 at 5:57
  • @user6556709: That would make it a bit difficult to (erroneously) redefine it for the second include, which was the point of the example code. Commented May 6, 2019 at 6:01
  • 2
    @user6556709: GCC gives a warning for an #undef as well. Could we please focus on the topic of the question instead of discussing code style of the example? Commented May 6, 2019 at 6:07
  • 2
    Pretty sure this is a logic problem, and someone else will attack it (and I have to be to bed soon), so I'll quickly leave this partial... don't define your PREVIOUS macro in terms of the current value; define it as literally 0, 1, or some token (-1 we'll say to represent undefined). Macros aren't variables; they store replacement lists, not what that replacement list would expand to, and especially here you want your storage to be literally what you found (so that next time the EXT is defined, you don't simply think you're equal to it). Commented May 6, 2019 at 7:46
  • 1
    To be clear, I mean #define PREV CURRENT is a trap; that CURRENT is 1 when the definition is made is of no consequence... once CURRENT is defined as 0 for the second include and this macro is used as a value, it will expand to 0. You want #if CURRENT==1/#define PREV 1; this way, if CURRENT is redefined to 0 and you compare to PREV, it will truly be not equal. Commented May 6, 2019 at 7:55

1 Answer 1

4

@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.

Sign up to request clarification or add additional context in comments.

1 Comment

I leave the question open in case the "perfect" solution is still "out there".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.