Domanda

Mi chiedo sull'uso pratico di #undef in C.Sto lavorando a K&R, e sono per il preprocessore.La maggior parte di questo materiale ho più o meno capito, ma qualcosa a pagina 90 (seconda edizione), tirato fuori a me:

I nomi potrebbero essere definito con #undef, di solito per garantire che una routine in realtà una funzione, non una macro:

#undef getchar

int getchar(void) { ... }

Questa è una pratica comune per difendere contro qualcuno #define-ing una macro con lo stesso nome della funzione?O si tratta di molto più di un campione che non si verificano nella realtà?(Ad ESEMPIO, nessuno nel suo diritto, di male, né folle mente dovrebbe essere la riscrittura getchar(), per cui non dovrebbe venire.) Con la propria nomi di funzione, si sente il bisogno di fare questo?Cambia se si sta sviluppando una libreria per altri usi?

È stato utile?

Soluzione

Cosa fa

Se leggi Plauger s La Libreria C Standard (1992), vedrete che la <stdio.h> intestazione è autorizzato a fornire getchar() e getc() come funzione di tipo macro (con un permesso speciale per getc() per valutare il suo puntatore del file argomento più di una volta!).Tuttavia, anche se fornisce macro, l'implementazione è anche obbligato a provid effettive funzioni che fanno lo stesso lavoro, principalmente in modo che è possibile accedere a un puntatore a funzione chiamata getchar() o getc() e passare ad altre funzioni.

Che è, facendo:

#include <stdio.h>
#undef getchar

extern int some_function(int (*)(void));

int core_function(void)
{
   int c = some_function(getchar);
   return(c);
}

Come scritto, il core_function() è piuttosto insignificante, ma illustra il punto.Si può fare la stessa cosa con la isxxxx() macro in <ctype.h> troppo, per esempio.

Normalmente, non si vuole fare che - di solito non si desidera rimuovere la definizione della macro.Ma, quando si bisogno reale funzione, è possibile entrare in possesso di esso.Le persone che forniscono librerie in grado di emulare le funzionalità della libreria C standard di buon effetto.

Raramente necessari

Nota anche che uno dei motivi che raramente bisogno di utilizzare l'esplicita #undef perché si può richiamare la funzione invece di macro scrivendo:

int c = (getchar)();

Perché il token dopo getchar non è un (, non è un'invocazione della funzione di tipo macro, quindi deve essere un riferimento alla funzione.Analogamente, il primo esempio di cui sopra, compilato ed eseguito correttamente anche senza il #undef.

Se si implementa la propria funzione con una macro ignorare, è possibile utilizzare questo per un buon effetto, anche se potrebbe essere un po ' di confusione, a meno che, ha spiegato.

/* function.h */
…
extern int function(int c);
extern int other_function(int c, FILE *fp);
#define function(c) other_function(c, stdout);
…
/* function.c */

…

/* Provide function despite macro override */
int (function)(int c)
{
    return function(c, stdout);
}

La definizione della funzione di linea non richiamare la macro perché il token dopo function non è (.Il return linea non richiamare la macro.

Altri suggerimenti

Le macro sono spesso utilizzati per generare massa di codice.Spesso è piuttosto localizzata di utilizzo ed è sicuro #undef ogni macro di supporto al fine di intestazione particolare al fine di evitare scontri in modo che solo l'attuale codice generato viene importato altrove e le macro utilizzate per generare il codice non.

/Edit:Come un esempio, io ho usato questo per generare le strutture per me.Il seguente è un estratto da un vero e proprio progetto:

#define MYLIB_MAKE_PC_PROVIDER(name) \
    struct PcApi##name { \
        many members …
    };

MYLIB_MAKE_PC_PROVIDER(SA)
MYLIB_MAKE_PC_PROVIDER(SSA)
MYLIB_MAKE_PC_PROVIDER(AF)

#undef MYLIB_MAKE_PC_PROVIDER

Perché il preprocessore #defines sono tutti in uno spazio dei nomi globale, è facile per conflitti di namespace per il risultato, soprattutto quando si utilizza librerie di terze parti.Per esempio, se si voleva creare una funzione denominata OpenFile, potrebbe non compilare correttamente, perché il file di intestazione <windows.h> definisce il token OpenFile per la mappa a OpenFileA o OpenFileW (a seconda se UNICODE è definita o no).La soluzione corretta è #undef OpenFile prima di definire la funzione.

Io uso solo quando una macro in un #included file interferisce con una delle mie funzioni (ad esempio, ha lo stesso nome).Poi Ho #undef la macro in modo che posso usare la mia funzione.

Anche se credo che Jonathan Leffler ha dato la risposta giusta.Qui è un caso molto raro, in cui io uso un #undef.Normalmente la macro deve essere riutilizzabili all'interno di molte funzioni;ecco perché si definisce all'inizio di un file o di un file di intestazione.Ma a volte hai qualche codice ripetitivo all'interno di una funzione che può essere ridotto con una macro.


int foo(int x, int y)
{
#define OUT_OF_RANGE(v, vlower, vupper) \
    if (v < vlower) {v = vlower; goto EXIT;} \
    else if (v > vupper) {v = vupper; goto EXIT;}

    /* do some calcs */
    x += (x + y)/2;
    OUT_OF_RANGE(x, 0, 100);
    y += (x - y)/2;
    OUT_OF_RANGE(y, -10, 50);

    /* do some more calcs and range checks*/
    ...

EXIT:
    /* undefine OUT_OF_RANGE, because we don't need it anymore */
#undef OUT_OF_RANGE
    ...
    return x;
}

Per mostrare al lettore che questa macro è utile solo all'interno della funzione, non è definito alla fine.Non voglio incoraggiare nessuno a utilizzare tali hackish macro.Ma se è necessario, #undef alla fine.

Questa è una pratica comune per difendere contro qualcuno #define-ing una macro con lo stesso nome della funzione?O si tratta di molto più di un campione che non si verificano nella realtà?(Ad ESEMPIO, nessuno nel suo diritto, di male, né folle mente dovrebbe essere la riscrittura getchar(), quindi non dovrebbe essere alto.)

Un po ' di entrambi.Buon codice non richiedono l'uso di #undef, ma c'è un sacco di cattivo codice, ci si deve lavorare con. #undef può rivelarsi prezioso quando qualcuno tira un trucco come #define bool int.

Oltre a risolvere i problemi con le macro inquinanti spazio dei nomi globale, un altro uso di #undef è la situazione in cui una macro potrebbe essere richiesto di avere un comportamento diverso in luoghi diversi.Questo non è davvero un scenario comune, ma un paio che mi vengono in mente sono:

  • il assert macro può avere la definizione è stata modificata nel mezzo di una unità di compilazione, per il caso in cui si potrebbe desiderare di eseguire il debug su una porzione di codice, ma non altri.Oltre a assert debba essere #undef'ed per fare questo, il NDEBUG macro deve essere ridefinito per riconfigurare il comportamento desiderato di assert

  • Ho visto una tecnica utilizzata per garantire che le variabili globali sono definiti una sola volta, utilizzando una macro per dichiarare le variabili come extern, ma la macro dovrebbe essere ridefinito a nulla per il singolo caso in cui l'intestazione e/o dichiarazioni vengono utilizzati per definire le variabili.

Qualcosa di simile (non sto dicendo che questo è necessariamente una buona tecnica, solo che ho visto in natura):

/* globals.h */
/* ------------------------------------------------------ */
#undef GLOBAL
#ifdef DEFINE_GLOBALS
#define GLOBAL
#else
#define GLOBAL extern
#endif

GLOBAL int g_x;
GLOBAL char* g_name;
/* ------------------------------------------------------ */



/* globals.c */
/* ------------------------------------------------------ */
#include "some_master_header_that_happens_to_include_globals.h"

/* define the globals here (and only here) using globals.h */
#define DEFINE_GLOBALS
#include "globals.h"

/* ------------------------------------------------------ */

Se una macro può essere def autenticata, ci deve essere un impianto di undef.

una memoria tracker io uso definisce il suo nuovo/eliminare le macro per tenere traccia dei file/line informazioni.questa macro pause SC++L.

#pragma push_macro( "new" )
#undef new
#include <vector>
#pragma pop_macro( "new" )

Per quanto riguarda la tua questione più specifica:gli spazi dei nomi sono spesso emul;ate in C, anteponendo le funzioni di libreria con un identificatore.

Ciecamente undefing macro sta per aggiungere confusione, ridurre la manutenzione, e può rompere le cose che si basano sul comportamento originale.Se si è stati costretti, almeno usare il push/pop per conservare l'originale del comportamento di tutto il resto.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top