Variadic UNUSED-Funktion/Makro

Lesezeit: 10 Minuten

Benutzeravatar von dag
dag

Eine bekannte und portable Möglichkeit, C-Compiler-Warnungen über nicht verwendete Variablen zu unterdrücken, ist (siehe Warnungen zu nicht verwendeten Parametern in C-Code):

#define UNUSED(x) (void)(x)

Ich suche nach einer Möglichkeit, dies zu verallgemeinern, um mehrere Eingaben (unterschiedlicher Art) zu verwenden:

void foo(int a, long b, void* c){

   /* Want this: */
   ALL_UNUSED(a, b, c);

   /* instead of: */
   UNUSED(a);
   UNUSED(b);
   UNUSED(c);
}

Eine Möglichkeit, die den Trick zu machen scheint, ist die Verwendung einer variadischen Funktion

static inline void ALL_UNUSED(int dummy, ...) {}

Ich vermute jedoch, dass diese Lösung in Fachkreisen zu beanstanden ist.

Gibt es eine standardkonforme und portable (also nicht zu verwendende __attribute__((unused))) Möglichkeit, eine variadische UNUSED()-Funktion/Makro zu erstellen? Danke vielmals!

BEARBEITEN

Es scheint keinen sauberen Weg zu geben, das zu tun, worum ich im Zusammenhang mit C99 oder dem C-Präprozessor gebeten habe. So ist das Leben.

In seiner Antwort unten zeigt @Dabo eine ziemlich interessante Möglichkeit, das zu tun, worum ich gebeten habe, indem ich eine Reihe von Makros verwende. Das ist ordentlich und informativ (zumindest für mich), also akzeptiere ich diese Antwort. Das heißt, ich würde es nicht in einem großen Projekt einsetzen, weil es Tränen genug ist, um den Nutzen zu überwiegen, den es (in meinen Augen) bringt. Aber die Leute werden hier zu anderen Schlüssen kommen.

Wie unten angemerkt, ist der Ansatz, eine leere variadische Funktion zu verwenden, ebenfalls nicht perfekt. Obwohl es ein ziemlich eleganter Einzeiler ist, wird es Warnungen über unititialisierte Variablen provozieren (falls dies der Fall ist). Außerdem müssen Sie Ihrem Compiler vertrauen, dass er ihn vollständig wegoptimiert, was ich grundsätzlich ablehne, aber dass alle Compiler, mit denen ich es versucht habe, dies tatsächlich tun.

Ein relevanter Fall ist das Stubben von Funktionen nach einer frühen High-Level-Schnittstellendesignphase. Dann sind Ihre nicht verwendeten Variablen alle Funktionsargumente und per Definition initialisiert, und der folgende Ansatz funktioniert gut

static inline void UNUSED(int dummy, ...) {}

void foo(int a, long b, void* c){
    UNUSED(a, b, b); /* No warnings */
}

  • Was ist mit der Verwendung #define UNUSED(...) (void)(__VA_ARGS__).

    – Mach dir keine Sorgen, Kind

    23. April 2014 um 5:59 Uhr


  • Leider nein – probiert. Der Compiler warnt vor einem unbenutzten Ausdrucksergebnis oder einem “Wert als Anweisung”. Was erwarten Sie semantisch (void) (a, b, c); meinen?

    – Tag

    23. April 2014 um 6:07 Uhr

  • Danke für den Hinweis. Versuche es mal anders…

    – Mach dir keine Sorgen, Kind

    23. April 2014 um 6:10 Uhr

  • Beachten Sie, dass gcc die Option hat -Wno-unused-variable was scheint zu tun, was du fragst? Obwohl sich die Frage vielleicht eher um Varadics und Makros dreht …

    – spinus

    23. April 2014 um 7:20 Uhr


  • #define UNUSED(x) (void)(x) eigentlich bewertet ‘x’ … #define UNUSED(x) (void)(sizeof(x)) ist auch keine perfekte Lösung, da C99 berühmte VLAs (Variable Length Arrays) eingeführt hat, die auch ausgewertet werden, wenn sie an die übergeben werden sizeof()

    Benutzer10133158

    12. September 2018 um 10:52 Uhr


Benutzeravatar von Dabo
Dabo

Basierend auf diesen beiden Beiträgen Variadic-Makro zum Zählen der Anzahl der Argumenteund Überladen von Makros ich habe folgendes gemacht

#define UNUSED1(x) (void)(x)
#define UNUSED2(x,y) (void)(x),(void)(y)
#define UNUSED3(x,y,z) (void)(x),(void)(y),(void)(z)
#define UNUSED4(a,x,y,z) (void)(a),(void)(x),(void)(y),(void)(z)
#define UNUSED5(a,b,x,y,z) (void)(a),(void)(b),(void)(x),(void)(y),(void)(z)

#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5, N,...) N
#define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(__VA_ARGS__, 5, 4, 3, 2, 1)

#define ALL_UNUSED_IMPL_(nargs) UNUSED ## nargs
#define ALL_UNUSED_IMPL(nargs) ALL_UNUSED_IMPL_(nargs)
#define ALL_UNUSED(...) ALL_UNUSED_IMPL( VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__ )

was kann wie folgt verwendet werden

 int main()
 {
    int a,b,c;
    long f,d;

    ALL_UNUSED(a,b,c,f,d);

    return 0;
  }

Eclipse-Makroerweiterung ergibt:

  (void)(a),(void)(b),(void)(c),(void)(f),(void)(d)

zusammengestellt mit gcc -Wall ohne Warnungen

BEARBEITEN:

#define UNUSED1(z) (void)(z)
#define UNUSED2(y,z) UNUSED1(y),UNUSED1(z)
#define UNUSED3(x,y,z) UNUSED1(x),UNUSED2(y,z)
#define UNUSED4(b,x,y,z) UNUSED2(b,x),UNUSED2(y,z)
#define UNUSED5(a,b,x,y,z) UNUSED2(a,b),UNUSED3(x,y,z)

EDIT2

Wie für inline Methode, die Sie gepostet haben, ein Schnelltest

int a=0;
long f,d;

ALL_UNUSEDINLINE(a,f,&d);

gibt ‘f’ is used uninitialized in this function [-Wuninitialized] Warnung. Hier also zumindest ein Anwendungsfall, der die Allgemeingültigkeit dieses Ansatzes bricht

  • Ganz nett! Ich werde damit herumspielen und sehen, wie es sich anfühlt. Die manuelle Aufzählung scheint unvermeidlich, macht aber kaum jemanden zum Verlieben in cpp. Vielen Dank!

    – Tag

    23. April 2014 um 8:38 Uhr

  • Darf ich fragen, was Sie von dem Void-Variadic-Funktionsansatz halten, den ich in dem Beitrag erwähne (der etwas kürzer ist als Ihre beeindruckende Makrolösung)? Erscheint es Ihnen falsch und/oder fehlerhaft?

    – Tag

    23. April 2014 um 8:42 Uhr

  • @dag siehe mein edit2 bzgl inlineich nehme an, es beantwortet Ihre Frage.

    – Dabu

    23. April 2014 um 11:12 Uhr

  • Danke für den Hinweis! Der (nicht angegebene) Anwendungsfall, den ich im Sinn hatte, bestand eigentlich nur darin, Warnungen von nicht verwendeten Funktionsargumenten zu unterdrücken.

    – Tag

    25. April 2014 um 4:12 Uhr

Benutzeravatar von kugel
Kugel

Ich habe die großartige Lösung von Dabo (https://stackoverflow.com/a/23238813/5126486) genommen und ein wenig verbessert, damit es einfacher ist, sie auf mehr als 5 zu erweitern:

#define UNUSED0()
#define UNUSED1(a)                  (void)(a)
#define UNUSED2(a,b)                (void)(a),UNUSED1(b)
#define UNUSED3(a,b,c)              (void)(a),UNUSED2(b,c)
#define UNUSED4(a,b,c,d)            (void)(a),UNUSED3(b,c,d)
#define UNUSED5(a,b,c,d,e)          (void)(a),UNUSED4(b,c,d,e)

#define VA_NUM_ARGS_IMPL(_0,_1,_2,_3,_4,_5, N,...) N
#define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(100, ##__VA_ARGS__, 5, 4, 3, 2, 1, 0 )

#define ALL_UNUSED_IMPL_(nargs) UNUSED ## nargs
#define ALL_UNUSED_IMPL(nargs) ALL_UNUSED_IMPL_(nargs)
#define ALL_UNUSED(...) ALL_UNUSED_IMPL( VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__ )

  • Verarbeitet keine 0 varadischen Argumente … ist aber eine einfache Lösung. Ich werde bearbeiten …

    – Michael

    11. November 2019 um 21:18 Uhr

Benutzeravatar von Gian Lorenzo Meocci
Gian Lorenzo Meocci

Was denkst du darüber:

#define UNUSED(...) [__VA_ARGS__](){};

Beispiel:

void f(int a, char* b, long d)
{
    UNUSED(a, b, d);
}

Sollte zu einer Lambdas-Definition erweitert werden:

[a,b,d](){}; //optimized by compiler (I hope!)

===== Getestet mit http://gcc.godbolt.org ===== Ich habe es mit diesem Code versucht:

#define UNUSED(...) [__VA_ARGS__](){};

int square(int num, float a) {
  UNUSED(a);
  return num * num;
}

Die resultierende Ausgabe (kompiliert mit -O0 -Wall) ist:

square(int, float):
    pushq   %rbp
    movq    %rsp, %rbp
    movl    %edi, -4(%rbp)
    movss   %xmm0, -8(%rbp)
    movl    -4(%rbp), %eax
    imull   -4(%rbp), %eax
    popq    %rbp
    ret

BEARBEITEN:

Wenn Sie C ++ 11 verwenden können, könnte dies eine bessere Lösung sein:

template <typename ...Args>
void UNUSED(Args&& ...args)
{
    (void)(sizeof...(args));
}

  • Diese Frage bezieht sich auf C, nicht auf C++11.

    Benutzer824425

    21. September 2016 um 19:11 Uhr

  • Führt zu “Warnung: Ausdrucksergebnis nicht verwendet”, aber dies kann leicht behoben werden

    – Simon Warta

    16. April 2017 um 20:05 Uhr

  • +1 Kommentar für user824425 und -1 Antwort für Gian Lorenzo Meocci. Die Frage bezieht sich auf C. Wenn es C++ wäre, können Sie die Argumentnamen einfach weglassen (aber die Typen belassen).

    – Herr Stobbe

    20. April 2019 um 7:00 Uhr

  • Das Lambda erfordert, dass die Objekte kopierbar sind.

    – HolyBlackCat

    22. April 2021 um 5:41 Uhr

Benutzeravatar von Matt Eding
Matt Eding

Hier ist ein sehr einfacher Ansatz, der keine besonderen Tricks erfordert und auch keinen Laufzeit-Overhead verursacht.

Ursprüngliche Antwort (nur C++)

#define UNUSED(...) (void)sizeof(__VA_ARGS__)

https://godbolt.org/z/Gz8MfvaTz

Aktualisierte Antwort (C und C++)

Ich hatte vergessen, den Compiler von C++ auf C umzustellen, und daher die überarbeitete Antwort.

int main(int argc, char ** argv)
{
    /* different types */
    UNUSED(argc, argv);

    /* many variables */
    int w, x, y, z;
    UNUSED(w, x, y, z);

    /* single variable */
    void * ptr;
    UNUSED(ptr);
}

MSVC

/O2 /W4
Keine Warnungen von C- und C++-Compilern.

Klirren

-O3 -Wand -Wextra
Keine Warnungen von C- und C++-Compilern.

GCC

-O3 -Wand -Wextra
Keine Warnungen nur vom C++-Compiler. Beim C-Compiler wird nur der letzte Parameter ohne Warnung ignoriert. Also im schlimmsten Fall diese Version von UNUSED ist dem traditionellen ebenbürtig #define UNUSED(x) ((void)x).

Das Folgende verursacht keine Warnungen mit -Wall -Wextra auf GCC und Clang, funktioniert aber nur für C11 und höher:

#define UNUSED(...) ((void)sizeof((_Bool[]){__VA_ARGS__}))

  • Das hängt von den Typen der übergebenen Parameter ab, nicht wahr? Sie haben möglicherweise nicht die gleichen und kompatiblen Typen, die als Initialisierer für das Array zulässig sind, was zu einer weiteren Warnung oder sogar zu Fehlern führt, z. Warum würde dies auch C11 erfordern?

    – stefankt

    23. September um 22:19 Uhr

  • Ich bekomme diesen Fehler nicht mit String-Literalen, aber ja, das funktioniert nur für einige Typen, da Struct- und Union-Typen nicht konvertierbar sind _Bool, also müssen Sie die Adresse von Variablen mit diesen Typen nehmen. Das Makro erfordert C11, da zusammengesetzte Literale ((_Bool[]){__VA_ARGS__}) in C11 hinzugefügt wurden, wenn Ihr Projekt C11 (oder höher) nicht verwendet, müssen Sie auf eine weniger minimale Lösung zurückgreifen. Ich mag diese Lösung, weil alle meine Projekte C11 sind und weil unbenutzte Strukturen und Vereinigungen (keine Zeiger) sehr selten sind.

    – yyny

    28. September um 15:26 Uhr

  • Zusammengesetzte Literale wurden in C99 hinzugefügt.

    – stefankt

    29. September um 16:08 Uhr

vjalles Benutzeravatar
vjalle

Eine etwas verfeinerte Version der Lösung in der Frage, die jede Art von Argumenten akzeptiert:

static inline void UNUSED2(int dummy, ...)
{
    (void)dummy;
}
#define UNUSED(...) UNUSED2(0, __VA_ARGS__)

Scheint mit mehreren Compilern zu arbeiten, nicht nur mit GCC.

  • Das hängt von den Typen der übergebenen Parameter ab, nicht wahr? Sie haben möglicherweise nicht die gleichen und kompatiblen Typen, die als Initialisierer für das Array zulässig sind, was zu einer weiteren Warnung oder sogar zu Fehlern führt, z. Warum würde dies auch C11 erfordern?

    – stefankt

    23. September um 22:19 Uhr

  • Ich bekomme diesen Fehler nicht mit String-Literalen, aber ja, das funktioniert nur für einige Typen, da Struct- und Union-Typen nicht konvertierbar sind _Bool, also müssen Sie die Adresse von Variablen mit diesen Typen nehmen. Das Makro erfordert C11, da zusammengesetzte Literale ((_Bool[]){__VA_ARGS__}) in C11 hinzugefügt wurden, wenn Ihr Projekt C11 (oder höher) nicht verwendet, müssen Sie auf eine weniger minimale Lösung zurückgreifen. Ich mag diese Lösung, weil alle meine Projekte C11 sind und weil unbenutzte Strukturen und Vereinigungen (keine Zeiger) sehr selten sind.

    – yyny

    28. September um 15:26 Uhr

  • Zusammengesetzte Literale wurden in C99 hinzugefügt.

    – stefankt

    29. September um 16:08 Uhr

Sie können die Kompilierzeit verwenden __VA_ARGS__ Makro.

#define UNUSED(...) (void)(__VA_ARGS__)

AKTUALISIEREN: Nach vielen Versuchen bin ich zu einer optimierten Lösung gekommen:

#define UNUSED(...)  __VA_ARGS__

int main()
{
    int e, x;
    char **a, **b, *c, d[45];
    x = x, UNUSED(a, b, c, d, e), x; 

    return 0;
}

ANMERKUNGEN:

  1. Es eliminiert Warnungen nicht vollständig, aber reduziert sie nur 3 Gleiche Art von Warnungen:
    warning: value computed is not used

  2. Der erste und der letzte x Zuweisung gleicher Datentypen sicherstellen.

  3. Ich werde sagen, es ist optimiert, weil es für eine beliebige Anzahl von unbenutzten Variablen gibt 3 Warnungen (ich kann mich irren, bitte testen Sie es selbst und melden Sie mich, wenn Sie mehr erhalten) und die Menge an Code (MACRO-Manipulationen), die erforderlich ist, um dies zu erreichen, ist geringer.

  4. Ich arbeite noch daran, werde posten, wenn ich zu einer besseren Lösung komme.

  • Danke @MadHatter, aber ich denke, sowohl Clang als auch GCC geben immer noch Warnungen damit aus. GCC “Warnung: linker Operand des Komma-Ausdrucks hat keine Wirkung [-Wunused-value]”, Clang: “Warnung: Ausdrucksergebnis ungenutzt [-Wunused-value]”

    – Tag

    23. April 2014 um 6:12 Uhr

  • Ja!! Ich habe es selbst mit zusammengestellt gcc -Wall und bekam dieselbe Warnung. Probiere noch was…

    – Mach dir keine Sorgen, Kind

    23. April 2014 um 6:17 Uhr

1432290cookie-checkVariadic UNUSED-Funktion/Makro

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

Privacy policy