Ist es empfehlenswert, #ifdef während der Entwicklung zu verwenden, um zwischen verschiedenen Verhaltenstypen zu wechseln? Zum Beispiel möchte ich das Verhalten von vorhandenem Code ändern, ich habe verschiedene Ideen, wie ich das Verhalten ändern kann, und es ist erforderlich, zwischen verschiedenen Implementierungen zu wechseln, um verschiedene Ansätze zu testen und zu vergleichen. Normalerweise sind Änderungen im Code komplex und wirken sich auf verschiedene Methoden in verschiedenen Dateien aus.
Normalerweise füge ich mehrere Bezeichner ein und mache so etwas
void foo()
{
doSomething1();
#ifdef APPROACH1
foo_approach1();
#endif
doSomething2();
#ifdef APPROACH2
foo_approach2();
#endif
}
void bar()
{
doSomething3();
#ifndef APPROACH3
doSomething4();
#endif
doSomething5();
#ifdef APPROACH2
bar_approach2();
#endif
}
int main()
{
foo();
bar();
return 0;
}
Dies ermöglicht es, schnell zwischen verschiedenen Ansätzen zu wechseln und alles in nur einer Kopie des Quellcodes zu erledigen. Ist es ein guter Ansatz für die Entwicklung oder gibt es eine bessere Praxis?
quelle
#ifdef
Blöcken auswerten, wenn der Block deaktiviert ist. Wir sind auf Fälle gestoßen, in denen Code leicht veralten und nicht kompiliert werden kann, wenn Sie nicht routinemäßig alle Pfade erstellen.#ifdefs
weniger umständlich zu machen .Antworten:
Ich würde es vorziehen, Versionskontrollzweige für diesen Anwendungsfall zu verwenden. Auf diese Weise können Sie zwischen den Implementierungen unterscheiden, für jede einen eigenen Verlauf verwalten und, wenn Sie Ihre Entscheidung getroffen haben und eine der Versionen entfernen müssen, diesen Zweig einfach verwerfen, anstatt eine fehleranfällige Bearbeitung durchzuführen.
quelle
git
ist besonders geschickt in so etwas. Vielleicht nicht so sehr der Fallsvn
,hg
oder andere, aber es immer noch getan werden kann.git branch
!Wenn Sie einen Hammer halten, sieht alles aus wie ein Nagel. Es ist verlockend, wenn Sie wissen, wie es
#ifdef
funktioniert, wenn Sie es als eine Art Mittel verwenden, um ein benutzerdefiniertes Verhalten in Ihrem Programm zu erzielen. Ich weiß, weil ich den gleichen Fehler gemacht habe.Ich habe ein in MFC C ++ geschriebenes Legacy-Programm geerbt, mit dem bereits
#ifdef
plattformspezifische Werte definiert wurden. Dies bedeutete, dass ich mein Programm kompilieren konnte, um es auf einer 32-Bit-Plattform oder einer 64-Bit-Plattform zu verwenden, indem ich bestimmte Makrowerte definierte (oder in einigen Fällen nicht definierte).Das Problem trat dann auf, dass ich benutzerdefiniertes Verhalten für einen Client schreiben musste. Ich hätte eine Verzweigung erstellen und eine separate Codebasis für den Client erstellen können, aber das hätte die Wartung zur Hölle gemacht. Ich hätte auch Konfigurationswerte definieren können, die vom Programm beim Start gelesen werden sollen, und diese Werte zum Bestimmen des Verhaltens verwenden müssen, aber dann müsste ich benutzerdefinierte Setups erstellen, um den Konfigurationsdateien für jeden Client die richtigen Konfigurationswerte hinzuzufügen.
Ich war versucht und gab nach. Ich schrieb
#ifdef
Abschnitte in meinen Code, um die verschiedenen Verhaltensweisen zu unterscheiden. Machen Sie keinen Fehler, es war zunächst nichts übertrieben. Es wurden sehr geringfügige Verhaltensänderungen vorgenommen, die es mir ermöglichten, Versionen des Programms an unsere Kunden weiterzugeben, und ich brauche nicht mehr als eine Version der Codebasis.Im Laufe der Zeit wurde dies sowieso zur Wartungshölle, da sich das Programm nicht mehr durchgehend konsistent verhielt. Wenn ich eine Version des Programms testen wollte, musste ich unbedingt wissen, wer der Client war. Obwohl ich versucht habe, den Code auf eine oder zwei Header-Dateien zu reduzieren, war er sehr unübersichtlich, und aufgrund des schnellen
#ifdef
Lösungsansatzes verbreiteten sich solche Lösungen im gesamten Programm wie ein bösartiger Krebs.Ich habe seitdem meine Lektion gelernt und du solltest es auch. Verwenden Sie es, wenn Sie es unbedingt müssen, und verwenden Sie es ausschließlich für Plattformänderungen. Der beste Weg, um auf Verhaltensunterschiede zwischen Programmen (und damit Clients) zu reagieren, besteht darin, nur die beim Start geladene Konfiguration zu ändern. Das Programm bleibt konsistent und es ist sowohl leichter zu lesen als auch zu debuggen.
quelle
Vorübergehend ist nichts falsch an dem, was Sie tun (etwa vor dem Einchecken): Es ist eine großartige Möglichkeit, verschiedene Kombinationen von Techniken zu testen oder einen Codeabschnitt zu ignorieren (obwohl dies von Problemen an und für sich spricht).
Aber ein Wort der Warnung: Halten Sie keine #ifdef-Zweige da, es ist wenig frustrierender, als meine Zeit damit zu verschwenden, dasselbe auf vier verschiedene Arten zu lesen , nur um herauszufinden, welche ich lesen sollte .
Das Lesen eines #ifdef ist mühsam, da Sie sich tatsächlich daran erinnern müssen, es zu überspringen! Machen Sie es sich nicht schwerer, als es unbedingt sein muss.
Verwenden Sie #ifdefs so sparsam wie möglich. In der Regel können Sie dies in Ihrer Entwicklungsumgebung für permanente Unterschiede wie Debug- / Release-Builds oder für verschiedene Architekturen tun .
Ich habe Bibliotheksfunktionen geschrieben, die von eingeschlossenen Bibliotheksversionen abhängig waren, für die #ifdef-Teilungen erforderlich waren. Manchmal mag es der einzige oder der einfachste Weg sein, aber selbst dann sollten Sie sich darüber aufregen, sie zu behalten.
quelle
Die Verwendung von #ifdefs macht das Lesen von Code sehr schwierig.
Also, nein, benutze nicht so #ifdefs.
Es kann Unmengen von Argumenten geben, warum man ifdefs nicht verwendet, für mich ist dies genug.
Kann eine Menge Dinge tun, die es tun kann:
Alles abhängig davon, welche Ansätze definiert sind oder nicht. Was es tut, ist auf den ersten Blick absolut nicht klar.
quelle