Frage

Ich frage mich, über den praktischen Einsatz von #undef in C. Ich arbeite durch K & R, und bin an den Präprozessor auf. Die meisten davon war Material, das ich (mehr oder weniger) zu verstehen, sondern etwas, auf Seite 90 (zweite Ausgabe) fest an mich:

  

Namen mit #undef nicht definiert werden können,   in der Regel, um sicherzustellen, dass eine Routine   wirklich eine Funktion, kein Makro:

     

#undef getchar

     

int getchar(void) { ... }

Ist dies eine gängige Praxis zu verteidigen gegen jemand ein Makro mit dem gleichen Namen wie die Funktion #define-ing? Oder ist das wirklich mehr von einer Probe, die in der Realität nicht auftreten würde? (EG, niemand in seinen Rechten, falsch noch verrückt Geist getchar() Umschreiben werden soll, damit es nicht kommen sollte, auf.) Mit dem eigenen Funktionsnamen, fühlen Sie die Notwendigkeit, dies zu tun? Wird diese Änderung, wenn Sie eine Bibliothek sind zu entwickeln, für andere zu benutzen?

War es hilfreich?

Lösung

Was sie tut,

Wenn Sie Plauger Die Standard-C-Bibliothek (1992) lesen, sehen Sie, dass die <stdio.h> Header getchar() und getc() als Funktionsmakros zur Verfügung zu stellen erlaubt (mit Sondergenehmigung für getc() sein Dateizeigerargument mehr zu bewerten als einmal!). Doch selbst wenn es Makros bietet, ist die Umsetzung auch verpflichtet, die tatsächlichen Funktionen anbietest, die die gleiche Arbeit tun, in erster Linie, so dass Sie einen Funktionszeiger zugreifen können getchar() oder getc() und passieren, dass zu anderen Funktionen aufgerufen.

Das heißt, indem Sie:

#include <stdio.h>
#undef getchar

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

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

Wie schon geschrieben, die core_function() ziemlich sinnlos ist, aber es zeigt den Punkt. Sie können mit dem isxxxx() Makros in <ctype.h> das gleiche tun auch, zum Beispiel.

Normalerweise wollen Sie nicht, das zu tun - Sie normalerweise nicht die Makrodefinition entfernen möchten. Aber, wenn Sie die reale Funktion benötigen, können Sie halten, es zu bekommen. Personen, die Bibliotheken bieten die Funktionalität der Standard-C-Bibliothek, um eine gute Wirkung emulieren kann.

Nur selten benötigt

Beachten Sie auch, dass einer der Gründe, benötigen Sie selten die explizite #undef zu verwenden ist, weil Sie die Funktion anstelle des Makros durch Schreiben aufrufen kann:

int c = (getchar)();

Da die Token nach getchar kein ( ist, ist es nicht ein Aufruf der funktionsähnlichen Makros, also ist es ein Verweis auf die Funktion sein muss. In ähnlicher Weise über das erste Beispiel würde kompilieren und korrekt auch ohne #undef ausgeführt werden.

Wenn Sie eine eigene Funktion mit einem Makro-Überschreibung implementieren, können Sie dies für eine gute Wirkung verwenden, obwohl es etwas verwirrend sein könnte, es sei denn, erläutert.

/* 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);
}

Die Funktionsdefinition Zeile aufrufen nicht das Makro, da die Token nach function nicht ( ist. Die return Linie wird das Makro aufrufen.

Andere Tipps

Makros werden häufig verwendet, Masse von Code zu generieren. Es ist oft eine ziemlich lokalisierte Nutzung und es ist sicher keine Helfer Makros am Ende des jeweiligen Kopfes um #undef Namenskonflikte, so dass nur der tatsächlich erzeugten Code importiert wird an anderer Stelle zu vermeiden und die Makros verwendet, um den Code zu generieren, dies nicht tun.

/ Edit: Als Beispiel habe ich verwendet, um diesen structs für mich zu generieren. Der folgende Text ist ein Auszug aus einem aktuellen Projekt:

#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

Da Präprozessor #defines alle in einem globalen Namensraum sind, ist es einfach für Namespace-Konflikte, führen vor allem, wenn Bibliotheken von Drittanbietern verwenden. Zum Beispiel, wenn Sie eine Funktion namens OpenFile schaffen wollte, könnte es nicht richtig kompilieren, da die Header-Datei <windows.h> das Token OpenFile definiert, um zur Karte entweder OpenFileA oder OpenFileW (je nachdem, ob UNICODE definiert ist oder nicht). Die richtige Lösung ist #undef OpenFile vor Ihrer Funktion zu definieren.

Ich kann es nur verwenden, wenn ein Makro in einer #included-Datei wird mit einem meiner Funktionen zu stören (zum Beispiel es hat den gleichen Namen). Dann #undef ich das Makro so kann ich meine eigene Funktion.

Obwohl ich glaube, Jonathan Leffler hat Ihnen die richtige Antwort. Hier ist ein sehr seltener Fall, wo ich eine #undef verwenden. Normalerweise sollte ein Makro viele Funktionen wiederverwendbar innen sein; das ist, warum Sie es an der Spitze einer Datei oder in einer Header-Datei definiert werden. Aber manchmal muss man einige sich wiederholende Code innerhalb einer Funktion, die mit einem Makro verkürzt werden kann.


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;
}

, um den Leser zu zeigen, dass dieser Makro in der Funktion nur dann sinnvoll ist, wird es am Ende nicht definiert. Ich möchte nicht, dass jemand ermutigen, solche hackish Makros zu verwenden. Aber wenn Sie zu haben, #undef sie am Ende.

  

Ist dies eine gängige Praxis, gegen jemanden zu verteidigen # define-ing ein Makro mit dem gleichen Namen wie Ihre Funktion? Oder ist das wirklich mehr von einer Probe, die in der Realität nicht auftreten würde? (EG, niemand in seinen Rechten, falsch noch verrückt Geist Umschreiben getchar werden soll (), so sollte es nicht kommen.)

Ein wenig von beidem. Guter Code wird nicht Gebrauch von #undef erfordern, aber es gibt viele schlechten Code da draußen Sie haben zu arbeiten. #undef kann von unschätzbarem Wert sein, wenn jemand einen Trick wie #define bool int zieht.

Zusätzlich zu den Problemen mit Makros zur Festsetzung des globalen Namensraum verschmutzen, eine weitere Verwendung von #undef ist die Situation, in der ein Makro erforderlich sein könnte, ein anderes Verhalten an verschiedenen Orten zu haben. Dies ist kein wirklich gemeinsames Szenario, aber ein Paar, das in den Sinn kommen, sind:

  • das assert Makro kann hat es Definition in der Mitte einer Übersetzungseinheit für den Fall geändert, wo Sie vielleicht das Debuggen auf einem Teil des Codes auszuführen, andere aber nicht. Zusätzlich zu müssen assert selbst #undef'ed wird, dies zu tun, muss der NDEBUG Makro das gewünschte Verhalten von assert

  • neu konfiguriert werden neu definiert werden
  • Ich habe eine Technik, die verwendet gesehen, um sicherzustellen, dass Globals genau einmal definiert ist, ein Makro mithilfe der Variablen als extern zu erklären, aber das Makro würde für den einzelnen Fall zu nichts neu definiert werden, wenn die Kopf- / Erklärungen sind verwendet, um die Variablen zu definieren.

So etwas (ich sage nicht, dass dies unbedingt eine gute Technik, nur ein Ich habe in der Wildnis gesehen):

/* 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"

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

Wenn ein Makro def'ed werden kann, muss eine Anlage undef sein.

ein Speichertracker Ich benutze definiert ihren eigenen neuen / Löschen von Makros Datei / Zeileninformationen zu verfolgen. Dieses Makro bricht den SC ++ L.

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

In Bezug auf Ihre spezifische Frage: Namensraum oft emul ist; ated in C durch Bibliothek prefixing Funktionen mit einer Kennung

.

Blind Makros undefing wird Verwirrung hinzuzufügen, Wartbarkeit zu reduzieren und kann die Dinge brechen, die auf dem ursprünglichen Verhalten verlassen. Wenn Sie gezwungen wurden, zumindest Verwendung Push / Pop das ursprüngliche Verhalten überall sonst zu bewahren.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top