struct foo {
int id;
int route;
int backup_route;
int current_route;
}
und eine Funktion namens update(), die verwendet wird, um Änderungen darin anzufordern.
update(42, dont_care, dont_care, new_route);
das ist wirklich lang und wenn ich der Struktur etwas hinzufüge, muss ich JEDEM Aufruf von update( … ) ein ‘dont_care’ hinzufügen.
Ich denke darüber nach, ihm stattdessen eine Struktur zu übergeben, aber das vorherige Ausfüllen der Struktur mit ‘dont_care’ ist noch mühsamer, als es nur im Funktionsaufruf zu buchstabieren. Kann ich die Struktur irgendwo mit Standardwerten von dont care erstellen und einfach die Felder festlegen, die mir wichtig sind, nachdem ich sie als lokale Variable deklariert habe?
Was ist der eleganteste Weg, um nur die Informationen, die ich ausdrücken möchte, an die Update-Funktion zu übergeben?
und ich möchte, dass alles andere standardmäßig auf -1 gesetzt wird (der Geheimcode für „egal“)
hlovdal
Während Makros und/oder Funktionen (wie bereits vorgeschlagen) funktionieren (und möglicherweise andere positive Effekte haben (z. B. Debug-Hooks)), sind sie komplexer als nötig. Die einfachste und möglicherweise eleganteste Lösung besteht darin, einfach eine Konstante zu definieren, die Sie zur Variableninitialisierung verwenden:
const struct foo FOO_DONT_CARE = { // or maybe FOO_DEFAULT or something
dont_care, dont_care, dont_care, dont_care
};
...
struct foo bar = FOO_DONT_CARE;
bar.id = 42;
bar.current_route = new_route;
update(&bar);
Dieser Code hat praktisch keinen mentalen Overhead, um die Indirektion zu verstehen, und es ist sehr klar, welche Felder in bar Sie legen explizit fest, während Sie (sicher) diejenigen ignorieren, die Sie nicht festlegen.
Ein weiterer Vorteil dieses Ansatzes besteht darin, dass er nicht auf C99-Funktionen angewiesen ist, um zu funktionieren.
– D. Shawley
15. April 2009 um 18:28 Uhr
Als ich zu diesem wechselte, “fielen” 500 Zeilen aus dem Projekt heraus. Ich wünschte, ich könnte hier zweimal abstimmen!
– Arthur Ulfeldt
12. Juni 2009 um 17:53 Uhr
PTHREAD_MUTEX_INITIALIZER nutzt diese auch.
– yingted
2. März 2012 um 4:50 Uhr
Ich habe unten eine Antwort hinzugefügt, die sich auf X-Makros verlässt, um flexibel in Bezug auf die Anzahl der in der Struktur vorhandenen Elemente zu sein.
– Antonio
12. April 2017 um 16:02 Uhr
jpalecek
Sie können Ihren geheimen Spezialwert auf 0 ändern und die standardmäßige Strukturelement-Semantik von C ausnutzen
Funktioniert FOO_INIT? Der Compiler sollte sich beschweren, denke ich, wenn Sie dasselbe Member zweimal initialisieren.
– Jonathan Leffler
15. April 2009 um 15:17 Uhr
Ich habe es mit gcc versucht und es hat sich nicht beschwert. Auch im Standard habe ich nichts dagegen gefunden, tatsächlich gibt es ein Beispiel, wo das Überschreiben ausdrücklich erwähnt wird.
– jpalecek
15. April 2009 um 19:40 Uhr
C99: open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf: 6.7.8.19: Die Initialisierung soll in der Reihenfolge der Initialisiererliste erfolgen, wobei jeder Initialisierer, der für ein bestimmtes Unterobjekt vorgesehen ist, jeden zuvor aufgelisteten Initialisierer für dasselbe Unterobjekt überschreibt;
– alterdky
14. Dezember 2012 um 18:08 Uhr
Wenn dies wahr ist, könnten Sie nicht DEFAULT_FOO definieren, das alle Initialisierer enthält, und dann struct foo bar = { DEFAULT_FOO, .id=10 } ausführen; ?
– John
23. Dezember 2013 um 18:56 Uhr
Matt J
<stdarg.h> ermöglicht es Ihnen, variadische Funktionen zu definieren (die eine unbestimmte Anzahl von Argumenten akzeptieren, wie z printf()). Ich würde eine Funktion definieren, die eine beliebige Anzahl von Argumentpaaren benötigt, eines, das die zu aktualisierende Eigenschaft angibt, und eines, das den Wert angibt. Benutze ein enum oder eine Zeichenfolge, um den Namen der Eigenschaft anzugeben.
Hallo @Matt, wie man abbildet enum Variablen zu struct aufstellen? Hardcodieren? Dann ist diese Funktion nur auf bestimmte anwendbar struct.
– misssprite
15. Dezember 2014 um 8:20 Uhr
Vielleicht sollten Sie stattdessen eine Präprozessor-Makrodefinition verwenden:
Wenn Ihre Instanz von (struct foo) global ist, brauchen Sie den Parameter dafür natürlich nicht. Aber ich gehe davon aus, dass Sie wahrscheinlich mehr als eine Instanz haben. Die Verwendung des ({ … })-Blocks ist ein GNU-Ismus, der für GCC gilt; Es ist eine schöne (sichere) Möglichkeit, Linien als Block zusammenzuhalten. Wenn Sie später mehr zu den Makros hinzufügen müssen, wie z. B. Bereichsvalidierungsprüfung, müssen Sie sich keine Gedanken darüber machen, Dinge wie if/else-Anweisungen und so weiter zu beschädigen.
Das würde ich tun, basierend auf den von Ihnen angegebenen Anforderungen. Situationen wie diese sind einer der Gründe, warum ich angefangen habe, Python häufig zu verwenden; Der Umgang mit Standardparametern und dergleichen wird viel einfacher als jemals zuvor mit C. (Ich denke, das ist ein Python-Plug-in, sorry 😉
Ein von Gobject verwendetes Muster ist eine variadische Funktion und aufgezählte Werte für jede Eigenschaft. Die Schnittstelle sieht in etwa so aus:
update (ID, 1,
BACKUP_ROUTE, 4,
-1); /* -1 terminates the parameter list */
Das Schreiben einer varargs-Funktion ist einfach – siehe http://www.eskimo.com/~scs/cclass/int/sx11b.html. Passen Sie einfach Schlüssel -> Wert-Paare an und legen Sie die entsprechenden Strukturattribute fest.
Quinmare
Da es so aussieht braucht man diese Struktur nur für die update() Funktion, verwenden Sie dafür überhaupt keine Struktur, es verschleiert nur Ihre Absicht hinter diesem Konstrukt. Sie sollten vielleicht überdenken, warum Sie diese Felder ändern und aktualisieren, und separate Funktionen oder Makros für diese “kleinen” Änderungen definieren.
Oder schreiben Sie noch besser eine Funktion für jeden Änderungsfall. Wie Sie bereits bemerkt haben, ändern Sie nicht alle Eigenschaften gleichzeitig, also machen Sie es möglich, jeweils nur eine Eigenschaft zu ändern. Dies verbessert nicht nur die Lesbarkeit, sondern hilft Ihnen auch bei der Handhabung der unterschiedlichen Fälle, zB müssen Sie nicht nach allen “dont_care” suchen, weil Sie wissen, dass nur die aktuelle Route geändert wird.