Come altri hanno già detto, non c'è una buona soluzione generale a questo. I nomi dei tipi non sono visibili al preprocessore, quindi non è possibile utilizzare #ifdef
per verificare la loro esistenza.
Ci sono un certo numero di soluzioni parziali, tuttavia, e variano a seconda di dove provengono i requisiti per un determinato tipo.
Esistono diverse versioni dello standard ISO C, rilasciate nel 1990, 1999 e 2011.Ogni nuovo standard (in teoria) sostituisce e sostituisce il precedente, e ognuno definisce alcuni nuovi tipi. Ad esempio le intestazioni standard 1999 C aggiunte <stdbool.h>
e <stdint.h>
e tipi bool
, int32_t
, ecc. Se si desidera utilizzare il tipo bool
, ma si desidera comunque che il codice sia portatile per implementazioni che non supportano C99, è possibile eseguire un'operazione come:
#if defined(__STDC__) && __STDC_VERSION__ >= 199901L
#include <stdbool.h>
#else
typedef enum { false, true } bool;
#endif
Il enum
tipo non si comporta esattamente come quella di C99 built-in bool
tipo, quindi è necessario essere un po 'attenti a come lo si utilizza.
Il tipo uintptr_t
, definito in <stdint.h>
è facoltativo. È un tipo senza segno che può contenere un valore di puntatore convertito void*
senza perdita di informazioni; un'implementazione che non ha un tipo non firmato (ad esempio, perché i puntatori sono più grandi di qualsiasi tipo intero) non la fornirà. Non è possibile verificare direttamente per il tipo in sé, ma è possibile verificare per le macro che danno i suoi limiti:
#include <stdint.h>
#ifdef UINTMAX_MAX
/* uintmax_t exists */
#else
/* uintmax_t doesn't exist */
#endif
Potrebbe essere necessario avvolgere questo in un test per __STDC__
e __STDC_VERSION__
se non si può assumere C99 o meglio.
Il tipo long long
è un tipo predefinito (non parte della libreria), aggiunto in C99. Anche in questo caso, non è possibile verificare direttamente, ma è possibile verificare per le macro che definiscono i suoi confini:
#include <limits.h>
#ifdef LLONG_MAX
/* long long exists */
#else
/* long long *probably* doesn't exist */
#endif
Infine, ci sono cose che non si possono fare direttamente in C, ma che si può fare come parte del processo di creazione del tuo programma. Ad esempio, POSIX definisce un tipo pid_t
nell'intestazione specifica POSIX <unistd.h>
(è il tipo di identificativo di processo restituito dalla funzione getpid()
). Non si può condizionale includere un'intestazione - ma è possibile scrivere un piccolo programma che non riuscirà a compilare se l'intestazione non esiste:
#include <unistd.h>
pid_t dummy;
Come parte del processo di generazione, provare a compilare questo file. Se riesce, aggiungi una riga come
#define HAVE_PID_T
in un'intestazione di configurazione; se fallisce, aggiungere una linea come
#undef HAVE_PID_T
Nel codice sorgente, è possibile scrivere qualcosa come:
#include "config.h"
#ifdef HAVE_PID_T
#include <unistd.h>
/* pid_t exists */
#else
/* pid_t doesn't exist */
#endif
GNU Autoconf fornisce un modo per automatizzare questo tipo di test, ma è stato criticato per essere troppo complesso e ingombrante
Tutto ciò presuppone che, una volta determinato se esiste un tipo, è possibile fare qualcosa di utile con tali informazioni. Per alcuni tipi, come bool
, è possibile implementare un'alternativa quasi equivalente. Per pid_t
, d'altra parte, probabilmente non c'è un buon ripiego, a meno che non siate semplicemente #ifdef
a estrarre tutto il codice che tratta i processi. Se il tuo programma non funziona su un sistema che non ha pid_t
e getpid()
, potrebbe essere meglio scrivere semplicemente codice che presume che esistano. Se si tenta di compilare il codice su un sistema che non li fornisce, non verrà immediatamente compilato e potrebbe essere la cosa migliore che si possa fare.
Nel tuo caso particolare. ptrdiff_t dovrebbe sempre essere definito se si include stddef.h –
Essere in grado di testare il codice per l'esistenza e le proprietà di particolari definizioni è chiamato "reflection". I linguaggi di scripting tendono a supportare una piena riflessione, in parte perché è relativamente facile per i linguaggi di scripting. .NET e JVM supportano anche la riflessione. Per le lingue native, tuttavia, il supporto della riflessione in fase di esecuzione richiederebbe un sacco di codice eseguibile aggiuntivo. Alcune riflessioni in fase di compilazione possono essere supportate senza tale overhead, ma C non ha praticamente alcun supporto per la riflessione anche in fase di compilazione. – Steve314
Usa uno strumento come cmake o autotools – szx