Убедитесь, что определение препроцессора не меняет значение - PullRequest
4 голосов
/ 06 мая 2019

При реализации Приложения K стандарта C (Интерфейсы проверки границ) существует следующее требование:

Расширения, указанные в этом приложении, могут быть "запрошены" для объявления путем определения от __STDC_WANT_LIB_EXT1__ до 1, а для запроса не должны быть объявлены путем определения того, что 0.

Тогда есть этот абзац:

В блоке преобразования предварительной обработки __STDC_WANT_LIB_EXT1_ _ должно быть идентично определено для всех включений любых заголовков из подпункта K.3. Если __STDC_WANT_LIB_EXT1_ _ определяется по-разному для любого такого включения, реализация должна выдать диагностику, как если бы использовалась директива об ошибке препроцессора.

Интересно, как это реализовать. Я пошел дальше и наивно написал это (будет включено в каждый затронутый заголовок):

#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

Это (конечно) не работает по разным причинам:

  • Не учитывается случай, когда __STDC_WANT_LIB_EXT1__ не определен для первого включения, но определен для второго (который также должен быть пойман с помощью #error)
  • #define не принимает значение из __STDC_WANT_LIB_EXT1__ (префикс # будет принимать символ в виде строки, при проходе по symbol2value (...) будет приниматься 1 в качестве строки ).
  • ...

... но если взять псевдокод, он демонстрирует логику, стоящую за ним.

Я не так хорошо разбираюсь в более сложных делах препроцессоров, как это, так как обычно вам говорят держаться подальше от макромагиков. Должен быть способ выполнить указанное требование; это просто не "щелкает" для меня.

Есть идеи?


Чтобы завершить [mcve], поместите приведенный выше код в header.h, и это в testme.c:

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

int main() {}

Это должно вызвать сообщение об ошибке «другое значение».

1 Ответ

4 голосов
/ 06 мая 2019

@ HWalters сделал меня на правильном пути:

#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

"thinko" было этой строкой:

#define __STDC_WANT_LIB_EXT1_PREVIOUS__ __STDC_WANT_LIB_EXT1__

Определение «предыдущего» фактического значения вместо другого токена заставляет его работать.

Решение не идеальное - все «другие» значения, кроме 0,1, неопределенные объединяются в одно «предыдущее» значение (-2), в то время как буква стандарта говорит, что любое переопределение должно выдать диагностику.

...