Как я могу проверить, что определенный тип уже определен в компиляторе C?

Если у компилятора есть определенный тип (например, ptrdiff_t) как внедренный тип, я не хочу его снова вводить. Я знаю, что ниже код работает неправильно, что я ожидал.

#ifndef ptrdiff_t 
    typedef long int ptrdiff_t;
#endif

Как я могу проверить, что определенный тип уже определен в компиляторе C?

Ответ 1

В своем вопросе вы немного путаете две разные вещи:

Существуют встроенные типы, такие как int, float и т.д. Это стандартные типы, и они определены во всех компиляторах. Такие типы, как __int64 были введены и стандартизированы позже. Это означает, что они определены во всех последних компиляторах, но только в некоторых старых компиляторах. Вам не нужно ничего делать, чтобы использовать их. В то же время вы не можете выяснить в своем коде, определены они или нет. Это можно выяснить только из документации по компилятору. Вы можете написать:

#ifdef MSVC
       .... Microsoft specific code
#else
       .... Code for other compiler.
#endif

Этот подход позволяет вам создавать что-то вроде compiler independent environment.

Помимо встроенных типов, есть типы, которые приходят из заголовков. Некоторые заголовки имеют такие конструкции, как:

#ifndef ptrdiff_t_DEFINED
    #define ptrdiff_t_DEFINED
    typedef long int ptrdiff_t;
#endif

Обратите внимание, что определения макропроцессора остаются в стороне от определения типа. Вы не можете проверить, определен ли тип или нет, но вы можете легко проверить, определено ли определение макроса.

Какие заголовки включены в ваш код вы сами выбираете. Это означает, что эти определения не являются in the compiler itself. Они находятся в наборе определений текущей единицы перевода. Для компилятора они мало чем отличаются от других определений типов, которые вы пишете в своем собственном коде.

Некоторые заголовки компилятора или системы не имеют "защитных функций", как в примере выше. В этом случае единственное, что вы можете сделать, это отследить, из каких заголовков они приходят, и включить/не включать эти заголовки, возможно, используя ваши собственные охранники #ifdef вокруг операторов #include.

Ответ 2

Нет никакого способа сделать это вообще. В некоторых случаях может быть макрос, который определяется одновременно с типом, который вы можете использовать.

В вашем конкретном примере вы можете #include <stddef.h>, который всегда должен определять ptrdiff_t.

Ответ 3

Как уже говорили другие, нет хорошего общего решения для этого. Имена типов не видны препроцессору, поэтому вы не можете использовать #ifdef для проверки их существования.

Однако существует ряд частичных решений, и они различаются в зависимости от того, откуда появились требования для данного типа.

Существует несколько версий стандарта ISO C, выпущенных в 1990, 1999 и 2011 годах. Каждый новый стандарт (в теории) заменяет и заменяет предыдущий, и каждый определяет некоторые новые типы. Например, в стандарт 1999 C добавлены заголовки <stdbool.h> и <stdint.h>, а также типы bool, int32_t и т.д. Если вы хотите использовать тип bool, но при этом хотите, чтобы ваш код был переносимым для реализаций, которые не поддерживает C99, вы можете сделать что-то вроде:

#if defined(__STDC__) && __STDC_VERSION__ >= 199901L
#include <stdbool.h>
#else
typedef enum { false, true } bool;
#endif

Тип enum не ведет себя точно так же, как встроенный тип C99 bool, поэтому вам нужно быть немного осторожнее в его использовании.

Тип uintptr_t, определенный в <stdint.h>, является необязательным. Это тип без знака, который может содержать преобразованное значение указателя void* без потери информации; реализация, у которой нет такого типа без знака (скажем, потому что указатели больше, чем у любого целочисленного типа), не предоставит его. Вы не можете напрямую проверить сам тип, но вы можете проверить макросы, которые дают его границы:

#include <stdint.h>

#ifdef UINTMAX_MAX
/* uintmax_t exists */
#else
/* uintmax_t does not exist */
#endif

Возможно, вам понадобится обернуть это в тесте для __STDC__ и __STDC_VERSION__, если вы не можете принять C99 или выше.

Тип long long является предопределенным типом (не является частью библиотеки), добавленным в C99. Опять же, вы не можете проверить это напрямую, но вы можете проверить макросы, которые определяют его границы:

#include <limits.h>

#ifdef LLONG_MAX
/* long long exists */
#else
/* long long *probably* does not exist */
#endif

Наконец, есть вещи, которые вы не можете сделать непосредственно в C, но которые вы можете сделать как часть процесса сборки вашей программы. Например, POSIX определяет тип pid_t в заголовке, специфичном для POSIX <unistd.h> (это тип идентификатора процесса, возвращаемого функцией getpid()). Вы не можете условно включить заголовок - но вы можете написать небольшую программу, которая не будет компилироваться, если заголовок не существует:

#include <unistd.h>
pid_t dummy;

В рамках процесса сборки попробуйте скомпилировать этот файл. Если это удастся, добавьте строку вроде

#define HAVE_PID_T

к заголовку конфигурации; в случае неудачи добавьте строку вроде

#undef HAVE_PID_T

В своем исходном коде вы можете написать что-то вроде:

#include "config.h"
#ifdef HAVE_PID_T
#include <unistd.h>
/* pid_t exists */
#else
/* pid_t does not exist */
#endif

GNU Autoconf предоставляет способ автоматизации такого рода испытаний, но его критикуют за то, что он слишком сложный и громоздкий.

Все это предполагает, что, как только вы определили, существует ли тип, вы можете сделать что-то полезное с этой информацией. Для некоторых типов, таких как bool, вы можете реализовать почти эквивалентную альтернативу. Для pid_t, с другой стороны, вероятно, не будет хорошего отступления, если вы просто #ifdef не удалите весь код, который имеет дело с процессами. Если ваша программа просто не будет работать в системе, в которой нет pid_t и getpid(), лучше всего написать код, который предполагает, что они существуют. Если вы попытаетесь скомпилировать свой код в системе, которая его не предоставляет, он сразу же не сможет скомпилироваться, и это может быть лучшим решением, которое вы можете сделать.

Ответ 4

Поскольку переопределение зарезервированной конструкции языка C обычно приводит к ошибке времени компиляции, ее вообще невозможно проверить. Если вам интересно (для академического/учебного процесса), вы можете написать базовый прогон компилятора, чтобы проверить исключения/ошибки в вашей программе на C, чтобы определить, зарезервирован ли тип или нет.