10 Stimmen

Ist es eine gute Idee, varargs in einer C-API zu verwenden, um Schlüssel-Wert-Paare zu setzen?

Ich schreibe eine API, die eine Vielzahl von verschiedenen Feldern in einer Struktur aktualisiert.

Ich könnte das Hinzufügen künftiger Felder erleichtern, indem ich die Aktualisierungsfunktion variabel gestalte:

update(FIELD_NAME1, 10, FIELD_NAME2, 20);

dann später hinzufügen FIELD_NAME3 ohne die bestehenden Anrufe zu ändern:

update(FIELD_NAME1, 10, FIELD_NAME2, 20, FIELD_NAME3, 30);

Worte der Weisheit, bitte?

2voto

Patrick Schlüter Punkte 10870

Die bisher genannten Gründe für die Vermeidung von varargs sind alle gut. Lassen Sie mich noch einen weiteren hinzufügen, der noch nicht genannt wurde, da er weniger wichtig ist, aber dennoch vorkommen kann. Der vararg erzwingt, dass der Parameter auf dem Stack übergeben wird, was den Funktionsaufruf verlangsamt. Auf einigen Architekturen kann der Unterschied beträchtlich sein. Auf x86 ist er wegen des Fehlens von Registern nicht sehr wichtig, auf SPARC z. B. kann er wichtig sein. Bis zu 5 Parameter werden über Register übergeben, und wenn Ihre Funktion nur wenige Locals verwendet, wird kein Stack-Abgleich vorgenommen. Wenn Ihre Funktion eine Blattfunktion ist (d.h. keine andere Funktion aufruft), findet auch keine Fensteranpassung statt. Die Kosten des Aufrufs sind daher sehr gering. Bei einem vararg wird die normale Abfolge von Parameterübergabe auf dem Stack, Stackanpassung und Fensterverwaltung durchgeführt, sonst könnte Ihre Funktion nicht an die Parameter gelangen. Dies erhöht die Kosten des Aufrufs erheblich.

1voto

Paul Punkte 567

Viele Leute hier haben vorgeschlagen, die Anzahl der Parameter zu übergeben, aber andere merken zu Recht an, dass dies zu heimtückischen Fehlern führt, bei denen sich die Anzahl der Felder ändert, aber die Anzahl, die an die vararg-Funktion übergeben wird, nicht. Ich löse dies in einem Produkt, indem ich stattdessen eine Nullterminierung verwende:

send_info(INFO_NUMBER,
          Some_Field,       23,
          Some_other_Field, "more data",
          NULL);

Auf diese Weise ist es unwahrscheinlich, dass Programmierer beim Kopieren und Einfügen etwas falsch machen. Und was noch wichtiger ist: Ich werde es wahrscheinlich nicht vermasseln.

Zurück zum ursprünglichen Problem: Sie haben eine Funktion, die eine Struktur mit vielen Feldern aktualisieren muss, und die Struktur wird wachsen. Die übliche Methode (in den klassischen Win32- und MacOS-APIs) zur Übergabe von Daten dieser Art an eine Funktion ist die Übergabe einer anderen Struktur (kann sogar die gleiche Struktur sein wie die, die Sie aktualisieren), d.h.:

void update(UPDATESTRUCTURE *update_info);

um es zu verwenden, müssen Sie die Felder ausfüllen:

UPDATESTRUCTURE my_update = {
    UPDATESTRUCTURE_V1,
    field_1,
    field_2
};
update( &my_update );

Wenn Sie später neue Felder hinzufügen, können Sie die UPDATESTRUCTURE-Definition aktualisieren und neu kompilieren. Indem Sie die Versionsnummer angeben, können Sie älteren Code unterstützen, der die neuen Felder nicht verwendet.

Eine Variante des Themas ist es, einen Wert für Felder zu haben, die nicht aktualisiert werden sollen, wie KEEP_OLD_VALUE (idealerweise ist dies 0) oder NULL.

UPDATESTRUCTURE my_update = {
    field_1,
    NULL,
    field_3
};
update( &my_update);

Ich füge keine Version ein, weil ich die Tatsache ausnutze, dass, wenn wir die Anzahl der Felder in UPDATESTRUCTURE erhöhen, die zusätzlichen Felder auf 0 initialisiert werden, oder KEEP_OLD_VALUE.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X