Seltene Fälle, in denen MACROs verwendet werden müssen

Lesezeit: 7 Minuten

Das Debuggen von Makros kann viel Zeit in Anspruch nehmen. Wir sind viel besser dran, sie zu vermeiden, außer in den sehr seltenen Fällen, in denen weder Konstanten, Funktionen noch Templates tun können, was wir wollen.

Was sind die seltenen Fälle?

  • Ich missbrauche sie beim Schreiben von HPC-Code, da sie viel flexibler sind als alles andere. Aber das ist nur eine kleine Nische…

    – Mystisch

    14. Dezember 2011 um 18:34 Uhr


  • Woher stammt das Zitat?

    – Taylor Price

    14. Dezember 2011 um 18:38 Uhr

  • @TaylorPrice: Es sieht aus wie ein Kommentar, den ich hier geschrieben habe.

    – Mike Seymour

    14. Dezember 2011 um 18:39 Uhr

  • @TaylorPrice Aus meiner vorherigen Frage hat mich jemand darauf hingewiesen. Falls Sie neugierig waren, hier ist der Link zur Frage: stackoverflow.com/questions/8508989/…

    – Terry Li

    14. Dezember 2011 um 18:40 Uhr

  • Der Fall, in dem jemand eine Waffe auf Ihren Kopf richtet und sagt: “Wenn Sie kein Makro verwenden, erschieße ich Sie”.

    – DJ Clayworth

    14. Dezember 2011 um 21:50 Uhr

Benutzer-Avatar
Xeo

Wenn Sie tatsächlich wollen textlicher Ersatz, dort verwenden Sie Makros. Schauen Sie sich an Boost.Präprozessorist es eine großartige Möglichkeit, variadische Vorlagen in C++03 zu simulieren, ohne sich zu oft zu wiederholen.

Mit anderen Worten, wenn Sie möchten den Programmcode selbst manipulierenverwenden Sie Makros.

Eine weitere nützliche Anwendung ist assertdie als no-op when definiert ist NDEBUG ist nicht definiert (normalerweise Kompilierung im Freigabemodus).

Das bringt uns zum nächsten Punkt, der eine Spezialisierung des ersten ist: Unterschiedlicher Code mit unterschiedlichen Kompilierungsmodi oder zwischen verschiedenen Compilern. Wenn Sie Cross-Compiler-Unterstützung wünschen, kommen Sie ohne Makros nicht davon. Schauen Sie sich Boost im Allgemeinen an, es benötigt ständig Makros aufgrund verschiedener Mängel in verschiedenen Compilern, die es unterstützen muss.

Ein weiterer wichtiger Punkt ist, wann Sie brauchen Call-Site-Informationen ohne den Benutzer Ihres Codes nerven zu wollen. Sie haben keine Möglichkeit, das automatisch mit nur einer Funktion zu erhalten.

#define NEEDS_INFO() \
  has_info(__FILE__, __LINE__, __func__)

Mit einer entsprechenden Erklärung von has_info (und C++11/C99 __func__ oder ähnliches).

  • @Mystcial: Da dies mein Hauptpunkt war, habe ich es etwas mehr betont. 🙂

    – Xeo

    15. Dezember 2011 um 2:30 Uhr

  • @Xeo Ich nehme an, es gibt nichts, was static_assert nicht kann, was das Makro-Assert kann?

    – Trevor Hickey

    17. Dezember 2013 um 15:13 Uhr

  • @Trevor: static_assert kann nicht bedingt definiert werden, etwas oder nichts zu tun – und es kann insbesondere zur Laufzeit nichts tun.

    – Xeo

    17. Dezember 2013 um 16:33 Uhr

Benutzer-Avatar
Kerrek SB

Diese Frage scheint keine eindeutige, geschlossene Antwort zu haben, daher gebe ich nur ein paar Beispiele.

Angenommen, Sie möchten Informationen zu einem bestimmten Typ drucken. Typnamen sind im kompilierten Code nicht vorhanden, sodass sie möglicherweise nicht von der Sprache selbst ausgedrückt werden können (mit Ausnahme von C++-Erweiterungen). Hier muss der Präprozessor eingreifen:

#define PRINT_TYPE_INFO(type) do { printf("sizeof(" #type ") = %zu\n", sizeof(type)); } while (false)

PRINT_TYPE_INFO(int);
PRINT_TYPE_INFO(double);

Ebenso sind Funktionsnamen selbst nicht variabel. Wenn Sie also viele ähnliche Namen generieren müssen, hilft der Präprozessor:

#define DECLARE_SYM(name) fhandle libfoo_##name = dlsym("foo_" #name, lib);

DECLARE_SYM(init);   // looks up "foo_init()", declares "libfoo_init" pointer
DECLARE_SYM(free);
DECLARE_SYM(get);
DECLARE_SYM(set);

Meine bevorzugte Verwendung ist das Versenden von CUDA-Funktionsaufrufen und das Überprüfen ihres Rückgabewerts:

#define CUDACALL(F, ARGS...) do { e = F(ARGS); if (e != cudaSuccess) throw cudaException(#F, e); } while (false)

CUDACALL(cudaMemcpy, data, dp, s, cudaMemcpyDeviceToHost);
CUDACALL(cudaFree, dp);

  • +1 für die CUDACALL Makro. Ich mache dasselbe mit Win32-API-Funktionsaufrufen.

    – Andre Caron

    14. Dezember 2011 um 18:46 Uhr

  • Ist das e eine globale Variable?

    – Umarmung

    15. Dezember 2011 um 11:45 Uhr

  • @missingno: Ja. Ich habe einige Umgebungscodes übersprungen. Ich schätze, du könntest es lokal machen? Mein cudaException -Klasse erzeugt übrigens eine ausführliche Fehlermeldung, indem sie die Fehlerformatierungsfunktion von CUDA verwendet.

    – Kerrek SB

    15. Dezember 2011 um 11:51 Uhr


Da dies eine offene Frage ist, ein Trick, den ich oft benutze und praktisch finde.

Wenn Sie eine Wrapper-Funktion über eine freie Funktion schreiben möchten, wie sagen wir mallocohne jede Instanz in Ihrem Code zu ändern, in der die Funktion aufgerufen wird, reicht ein einfaches Makro aus:

#define malloc(X) my_malloc( X, __FILE__, __LINE__, __FUNCTION__)

void* my_malloc(size_t size, const char *file, int line, const char *func)
{

    void *p = malloc(size);
    printf ("Allocated = %s, %i, %s, %p[%li]\n", file, line, func, p, size);

    /*Link List functionality goes in here*/

    return p;
}

Sie können diesen Trick oft verwenden, um zu Debugging-Zwecken Ihren eigenen Speicherleckdetektor usw. zu schreiben.

Obwohl das Beispiel für ist malloc Es kann wirklich für jede freistehende Funktion wiederverwendet werden.

Benutzer-Avatar
Paul Rubel

Ein Beispiel ist Token einfügen wenn Sie einen Wert sowohl als Bezeichner als auch als Wert verwenden möchten. Aus dem msdn-Link:

#define paster( n ) printf_s( "token" #n " = %d", token##n )
int token9 = 9;

paster( 9 ); // => printf_s( "token9 = %d", token9 );

Es gibt auch Fälle in der C++-FAQ obwohl es Alternativen geben kann, ist die Makrolösung der beste Weg, Dinge zu tun. Ein Beispiel ist Zeiger auf Elementfunktionen wo das richtige Makro

 #define CALL_MEMBER_FN(object,ptrToMember)  ((object).*(ptrToMember)) 

macht es viel einfacher, den Anruf zu tätigen, anstatt sich mit all den verschiedenen Haaren herumzuschlagen, wenn man versucht, es ohne das Makro zu tun.

int ans = CALL_MEMBER_FN(fred,p)('x', 3.14);

Ehrlich gesagt nehme ich einfach ihr Wort und mache es so, aber anscheinend wird es schlimmer, wenn die Anrufe komplizierter werden.

Hier ist ein Beispiel für jemand, der versucht, es alleine zu schaffen

Benutzer-Avatar
zehn vier

Wenn Sie den Aufruf selbst benötigen, um optional von einer Funktion zurückzukehren.

#define MYMACRO(x) if(x) { return; }
void fn()
{
    MYMACRO(a);
    MYMACRO(b);
    MYMACRO(c);
}

Dies wird normalerweise für kleine Bits von sich wiederholendem Code verwendet.

Benutzer-Avatar
Basile Starynkevitch

Ich bin mir nicht sicher, ob das Debuggen von Makros viel Zeit in Anspruch nimmt. Ich würde glauben, dass ich das Debuggen von Makros (sogar 100-Zeilen-Monster-Makros) einfach finde, weil man die Möglichkeit hat, sich die Erweiterung anzuschauen (mittels gcc -C -E zum Beispiel) – was mit zB C++ Templates weniger möglich ist.

C-Makros sind in mehreren Fällen nützlich:

  • Sie möchten eine Liste von Dingen auf verschiedene Arten verarbeiten
  • Sie möchten einen “lvalue”-Ausdruck definieren
  • Sie brauchen Effizienz
  • Sie müssen den Speicherort des Makros durch haben __LINE__)
  • Sie benötigen eindeutige Kennungen
  • usw usw

Schauen Sie sich die vielen Verwendungen von an #define-d Makros in großen kostenlosen Software (wie Gtk, Gcc, Qt, …)

Was ich sehr bedauere, ist, dass die C-Makrosprache so begrenzt ist…. Stellen Sie sich vor, die C-Makrosprache wäre so mächtig wie Guile!!! (Dann könnten Sie so komplexe Dinge schreiben wie flex oder bison als Makros).

Schauen Sie sich die Leistungsfähigkeit von Common Lisp-Makros an!

Benutzer-Avatar
Marlon

Wenn Sie C verwenden, müssen Sie Makros verwenden, um Vorlagen zu simulieren.

Aus http://www.flipcode.com/archives/Faking_Templates_In_C.shtml

#define CREATE_VECTOR_TYPE_H(type) \
typedef struct _##type##_Vector{ \
  type *pArray; \
  type illegal; \
  int size; \
  int len; \
} type##_Vector; \
void type##_InitVector(type##_Vector *pV, type illegal); \
void type##_InitVectorEx(type##_Vector *pV, int size, type illegal); \
void type##_ClearVector(type##_Vector *pV); \
void type##_DeleteAll(type##_Vector *pV); \
void type##_EraseVector(type##_Vector *pV); \
int type##_AddElem(type##_Vector *pV, type Data); \
type type##_SetElemAt(type##_Vector *pV, int pos, type data); \
type type##_GetElemAt(type##_Vector *pV, int pos);

#define CREATE_VECTOR_TYPE_C(type) \
void type##_InitVector(type##_Vector *pV, type illegal) \
{ \
  type##_InitVectorEx(pV, DEF_SIZE, illegal); \
} \
void type##_InitVectorEx(type##_Vector *pV, int size, type illegal) \
{ \
  pV-len = 0; \
  pV-illegal = illegal; \
  pV-pArray = malloc(sizeof(type) * size); \
  pV-size = size; \
} \
void type##_ClearVector(type##_Vector *pV) \
{ \
  memset(pV-pArray, 0, sizeof(type) * pV-size); \
  pV-len = 0; \
} \
void type##_EraseVector(type##_Vector *pV) \
{ \
  if(pV-pArray != NULL) \
    free(pV-pArray); \
  pV-len = 0; \
  pV-size = 0; \
  pV-pArray = NULL; \
} \
int type##_AddElem(type##_Vector *pV, type Data) \
{ \
  type *pTmp; \
  if(pV-len = pV-size) \
  { \
    pTmp = malloc(sizeof(type) * pV-size * 2); \
    if(pTmp == NULL) \
      return -1; \
    memcpy(pTmp, pV-pArray, sizeof(type) * pV-size); \
    free(pV-pArray); \
    pV-pArray = pTmp; \
    pV-size *= 2; \
  } \
  pV-pArray[pV-len] = Data; \
  return pV-len++; \
} \
type type##_SetElemAt(type##_Vector *pV, int pos, type data) \
{ \
  type old = pV-illegal; \
  if(pos = 0 && pos <= pV-len) \
  { \
    old = pV-pArray[pos]; \
    pV-pArray[pos] = data; \
  } \
  return old; \
} \
type type##_GetElemAt(type##_Vector *pV, int pos) \
{ \
  if(pos = 0 && pos <= pV-len) \
    return pV-pArray[pos]; \
  return pV-illegal; \
} 

1362010cookie-checkSeltene Fälle, in denen MACROs verwendet werden müssen

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy