C/C++ enums: Erkennen, wenn mehrere Elemente anzeigen, um denselben Wert
Gibt es einen compile-Zeit-Weg zu erkennen /verhindern, dass doppelte Werte in einer C/C++ Aufzählung?
Der Haken ist, dass sind mehrere Elemente vorhanden, die initialisiert werden, um explizite Werte.
Hintergrund:
Habe ich geerbt, einige C-code wie den folgenden:
#define BASE1_VAL (5)
#define BASE2_VAL (7)
typedef enum
{
MsgFoo1A = BASE1_VAL, //5
MsgFoo1B, //6
MsgFoo1C, //7
MsgFoo1D, //8
MsgFoo1E, //9
MsgFoo2A = BASE2_VAL, //Uh oh! 7 again...
MsgFoo2B //Uh oh! 8 again...
} FOO;
Das problem ist, dass, wie der code wächst & Entwickler fügen Sie weitere Nachrichten an die MsgFoo1x
Gruppe, die schließlich es overruns BASE2_VAL
.
Dieser code wird schließlich migriert werden C++, so dass, wenn es ein C++-Lösung (template-Magie?), das ist OK-aber eine Lösung, die mit C und C++ ist besser.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Gibt es ein paar Möglichkeiten, um dies zu überprüfen compile-Zeit, aber Sie könnten nicht immer für Sie arbeiten. Starten Sie durch einfügen von "marker" enum-Wert direkt vor MsgFoo2A.
Nun müssen wir einen Weg finden, um sicherzustellen, dass
MARKER_1_DONT_USE < BASE2_VAL
zur compile-Zeit. Es gibt zwei gängige techiques.Negative Größe arrays
Es ist ein Fehler deklarieren Sie ein array mit einer negativen Größe. Das sieht ein wenig hässlich, aber es funktioniert.
Fast alle compiler, die jemals geschrieben wurden, wird ein Fehler generiert, wenn MARKER_1_DONT_USE größer ist als BASE_2_VAL. GCC spuckt:
Statische assertions
Wenn Ihr compiler unterstützt C11, können Sie
_Static_assert
. Unterstützung für C11 ist nicht allgegenwärtig, aber der compiler kann unterstützen_Static_assert
sowieso, vor allem, da die entsprechende Funktion in C++ wird allgemein unterstützt.GCC spuckt folgende Meldung:
Ich nicht sehen "hübsch" in Ihren Anforderungen, so reiche ich diese Lösung implementiert, mit dem Boost Preprocessor library.
Als eine up-front-disclaimer, ich habe nicht verwendet Steigern.Präprozessor eine ganze Menge und ich habe nur getestet, mit der Testfälle dargestellt, die hier, so könnte es sein Fehler, und es kann ein einfacher, sauberer Weg, dies zu tun. Ich begrüße Kommentare, Korrekturen, Anregungen, Beleidigungen, etc.
Hier gehen wir:
Dann verwenden wir es so:
Enumerator Wert ist optional; dieser code erzeugt eine Aufzählung entspricht:
Erzeugt auch einen sanity-check-Funktion enthält eine switch-Anweisung wie beschrieben in Ben Voigt ' s Antwort. Wenn wir die änderung der enumeration Deklaration, so dass wir nicht eindeutige enumerator-Werte, z.B.,
wird es nicht kompilieren (Visual C++ meldet das erwartet Fehler C2196: case Wert '1' ist bereits verwendet).
Dank auch an Matthieu M., deren Antwort auf eine andere Frage hat mich interessiert, die Boost Preprocessor library.
detail
namespace, um es zu halten aus dem Weg. EDIT: <3.namespace
wäre besser so, es funktioniert für beide Sprachen. Öhm... ich werde darüber nachdenken, dass in den morgen.detail_
würde gut genug funktioniert. Man könnte sogar__cplusplus
zu wählen, so dass die gleiche header funktioniert auf beiden. (Generierung von verschiedenen Quellen offensichtlich.)toString
Methode ? Statt einer reinen compiler-guard konnten wir, nachdem alle verwenden die Methode für die eigentliche Arbeit 🙂=
zu definieren Initialwert.=
ist, dass Sie nicht verwenden können, die aufgezählten Werte für etwas anderes danach. In diesem Fall zum Beispiel, Sie konnten nicht dieswitch
trick.Glaube ich nicht, es ist ein Weg, um dies zu erkennen, die mit der Sprache selbst, wenn man bedenkt es gibt denkbare Fälle, wo Sie wollen würde zwei enumeration-Werte identisch sein. Sie können jedoch immer sicherzustellen, dass alle explizit set-items sind am Anfang der Liste:
So lange, wie Ihnen Werte zugewiesen werden, an der Spitze, keine Kollision möglich ist, es sei denn, aus irgendeinem Grund die Makros zu erweitern, um Werte, die gleich sind.
In der Regel dieses problem zu überwinden, indem eine Feste Anzahl von bits für jeden MsgFooX Gruppe, und dafür sorgen, dass jede Gruppe nicht überlaufen-es ist vorgegebenen Anzahl von bits. Die "Anzahl der bits" Lösung ist gut, denn es ermöglicht eine bitweise test zu bestimmen, bis zu welcher Nachricht der Gruppe etwas gehört. Aber es gibt keine built-in-Sprache-Funktion, um dies zu tun, denn es gibt legitime Fälle für ein enum mit zwei von der gleichen Wert:
Ich kenne nichts, das automatisch überprüfen Sie alle enum-Mitglieder, aber wenn Sie wollen, um zu überprüfen, dass künftige änderungen der Initialisierungen (oder die Makros, auf die Sie sich stützen) nicht zu Kollisionen:
verursacht einen compiler-Fehler, wenn eine der integralen Werte wiederverwendet wird, und die meisten Compiler auch sagen, was mit dem Wert (dem numerischen Wert) ein problem war.
Könnten Sie roll eine robustere Lösung zu definieren, enums mit Boost.Präprozessor - ob seine lohnt sich, die Zeit ist eine andere Sache.
Wenn Sie sich bewegen, um C++ sowieso, vielleicht ist die (vorgeschlagene) zu Steigern.Enum passt Sie (verfügbar über die Boost-Vault).
Ein weiterer Ansatz ist die Verwendung von so etwas wie gccxml (oder mehr bequem pygccxml) zu identifizieren, die Kandidaten für die manuelle Inspektion.
Während wir nicht voll auf die Reflexion, Sie können dieses problem lösen, wenn Sie können Wiedereinstellen der enumeration-Werte.
Irgendwo in diesem wird erklärt:
anderswo, wir bauen diese Maschinen:
Sobald Sie haben, dass Maschinen (erfordert C++11), können wir Folgendes tun:
und zur compile-Zeit werden wir sicherstellen, dass keine zwei Elemente gleich sind.
Dies erfordert O(n) Rekursionstiefe O(n^2) Arbeit durch den compiler zur compile-Zeit, also für extrem große Enumerationen, die diese Probleme verursachen könnte. Ein O(lg(n)) und Tiefe O(n lg(n)) arbeiten mit einem viel größeren Konstanten Faktor getan werden kann durch Sortieren der Liste mit Elementen, die den ersten, aber das ist viel, viel mehr Arbeit.
Mit der enum-Reflexion-code vorgeschlagen für C++1y-C++17, das wird machbar sein, ohne Wiedereinstellen Elemente.
Ich nicht ganz wie die Antworten hier schon geschrieben, aber Sie Gaben mir einige Ideen. Die entscheidende Technik ist darauf zu verlassen, Ben Voight Antwort der switch-Anweisung verwenden. Wenn mehrere Fälle in einer switch-teilen sich die gleiche Zahl, erhalten Sie einen Kompilierungsfehler.
Meisten sinnvoll für mich und wahrscheinlich die original-poster, dies erfordert nicht alle C++ features.
Dinge zu bereinigen, die ich verwendet, aaronps Antwort auf Wie kann ich vermeiden, mich zu wiederholen, wenn Sie eine C++ - enum und einer abhängigen Daten Struktur?
Zunächst definieren Sie diese in ein paar header irgendwo:
Nun, Wann immer Sie brauchen, um eine Aufzählung:
Schließlich, diese Zeilen sind erforderlich, um tatsächlich machen die Aufzählung:
DEFINE_ENUM
macht der enum-Datentyp selbst.CHECK_ENUM
macht eine test-Funktion, die schaltet alle enum-Werte. Der compiler Absturz beim kompilierenCHECK_ENUM
wenn Sie Duplikate.