Часто мне хотелось бы связать условные выражения препроцессора с использованием __has_include (определенного в C++17 и C23 и поддерживаемого в более ранних версиях как расширение многими компиляторами). (GCC 4.9.2 и выше, а Clang минимум 3.0.0)). Простой пример выглядит примерно так:
Код: Выделить всё
#if defined(__has_include) && __has_include("header.h")
#include "header.h"
#else
#include "backup-header.h"
#endif
В документации GCC предлагается что-то вроде следующего:
Код: Выделить всё
#if defined __has_include
# if __has_include ()
# include
# endif
#endif
Проблема в том, что становится сложно связать ветку #else. Если бы мы хотели адаптировать его к нашему примеру, нам нужно было бы определить дополнительный макрос для отслеживания успешности __has_include, что делает код намного более подробным:
Код: Выделить всё
#ifdef __has_include
#if __has_include("header.h")
#include "header.h"
#define HAS_INCLUDE_HEADER_H
#endif
#endif
#ifndef HAS_INCLUDE_HEADER_H
#include "backup-header.h"
#endif
#undef HAS_INCLUDE_HEADER_H
Код: Выделить всё
// This part can be done once in a common header
#ifdef __has_include
#define MY_HAS_INCLUDE(Arg) __has_include(Arg)
#else
#define MY_HAS_INCLUDE(Arg) 0
#endif
#if MY_HAS_INCLUDE("header.h")
#include "header.h"
#else
#include "backup-header.h"
#endif
Выражение, управляющее условным включением, должно быть целочисленным постоянным выражением, за исключением того, что идентификаторы (включая те, которые лексически идентичны ключевым словам) интерпретируются, как описано ниже, и оно может содержать ноль или более определенных макровыражений и/или имеет-включаемые-выражения и/или имеет-атрибутные-выражения как выражения унарного оператора.
...
Директивы #ifdef, #ifndef, #elifdef и #elifndef, а также определенный оператор условного включения должны обрабатывать __has_include и __has_cpp_attribute так, как если бы они были именами определенных макросов. Идентификаторы __has_include и __has_cpp_attribute не должны появляться ни в каком контексте, не упомянутом в этом подразделе.
https://timsong-cpp.github.io/cppwp/n4950/cpp.cond
(C23 имеет по существу идентичный пункт)
Насколько я понимаю, это говоря, что "
Код: Выделить всё
__has_includeКод: Выделить всё
#elifТеперь к сути моего вопроса. Я продумывал пару обходных путей и задавался вопросом, соответствуют ли какие-либо из них стандартам и будут ли они работать.
(A) Определение MY_HAS_INCLUDE как макроса без аргументов вместо одного это требует аргумента, который заглушает предупреждение Clang, но я сомневаюсь, что это действительно допустимо, поскольку мы все еще пишем фразу __has_include вне условия #if. Есть ли шанс, что это соответствует стандарту?
Код: Выделить всё
// This part can be done once in a common header
#ifdef __has_include
#define MY_HAS_INCLUDE __has_include
#else
#define HAS_INCLUDE_STUB(Arg) 0
#define MY_HAS_INCLUDE HAS_INCLUDE_STUB
#endif
#if MY_HAS_INCLUDE("header.h")
#include "header.h"
#else
#include "backup-header.h"
#endif
Код: Выделить всё
// This part can be done once in a common header
#ifdef __has_include
#define IF_HAS_INCLUDE_SUPPORTED(Arg) Arg
#else
#define IF_HAS_INCLUDE_SUPPORTED(Arg) 0
#endif
#if IF_HAS_INCLUDE_SUPPORTED(__has_include("header.h"))
#include "header.h"
#else
#include "backup-header.h"
#endif
Код: Выделить всё
// This part can be done once in a common header
#ifdef __has_include
#define HAS_INCLUDE_TAKE_EMPTY_PAREN()
#define PRE_HAS_INCLUDE
#define POST_HAS_INCLUDE HAS_INCLUDE_TAKE_EMPTY_PAREN (
#else
#define HAS_INCLUDE_CONSUMER(Arg) 0
#define PRE_HAS_INCLUDE HAS_INCLUDE_CONSUMER (
#define POST_HAS_INCLUDE
#endif
#if PRE_HAS_INCLUDE __has_include("header.h") POST_HAS_INCLUDE )
#include "header.h"
#else
#include "backup-header.h"
#endif
Код: Выделить всё
// This part can be done once in a common header
#ifdef __has_include
#define IF_HAS_INCLUDE_SUPPORTED
#else
#define IF_HAS_INCLUDE_SUPPORTED(Arg) (0)
#endif
#if IF_HAS_INCLUDE_SUPPORTED(__has_include("header.h"))
#include "header.h"
#else
#include "backup-header.h"
#endif
Код: Выделить всё
// This part can be done once in a common header
#ifndef __has_include
#define __has_include(Arg) 0
#endif
#if __has_include("header.h")
#include "header.h"
#else
#include "backup-header.h"
#endif
Подробнее здесь: https://stackoverflow.com/questions/798 ... d-is-it-va
Мобильная версия