Wie mache ich printf, um die Werte von Variablen anzuzeigen, die vom Typ enum sind? Zum Beispiel:
typedef enum {Linux, Apple, Windows} OS_type;
OS_type myOS = Linux;
und was ich brauche ist so etwas
printenum(OS_type, "My OS is %s", myOS);
die eine Zeichenfolge "Linux" zeigen muss, keine Ganzzahl.
Ich nehme an, zuerst muss ich ein wertindiziertes Array von Zeichenfolgen erstellen. Aber ich weiß nicht, ob das der schönste Weg ist, es zu tun. Ist das überhaupt möglich?
c++
c
preprocessor
ansi-c
Psihodelia
quelle
quelle
Antworten:
Es gibt wirklich keinen schönen Weg, dies zu tun. Richten Sie einfach ein Array von Zeichenfolgen ein, die durch die Aufzählung indiziert werden.
Wenn Sie viel ausgeben, können Sie einen Operator << definieren, der einen Enum-Parameter verwendet und die Suche für Sie durchführt.
quelle
Die naive Lösung besteht natürlich darin, für jede Aufzählung, die die Konvertierung in einen String durchführt, eine Funktion zu schreiben:
Dies ist jedoch eine Wartungskatastrophe. Mit Hilfe der Boost.Preprocessor-Bibliothek, die sowohl mit C- als auch mit C ++ - Code verwendet werden kann, können Sie den Präprozessor problemlos nutzen und diese Funktion für Sie generieren lassen. Das Generierungsmakro lautet wie folgt:
Das erste Makro (beginnend mit
X_
) wird intern vom zweiten verwendet. Das zweite Makro generiert zuerst die Aufzählung, generiert dann eineToString
Funktion, die ein Objekt dieses Typs verwendet und den Namen des Aufzählers als Zeichenfolge zurückgibt (diese Implementierung erfordert aus offensichtlichen Gründen, dass die Aufzähler eindeutigen Werten zugeordnet werden).In C ++ könnten Sie die
ToString
Funktionoperator<<
stattdessen als Überladung implementieren , aber ich denke, es ist etwas sauberer, ein explizites "ToString
" zu benötigen , um den Wert in eine Zeichenfolgenform zu konvertieren.Als Verwendungsbeispiel
OS_type
würde Ihre Aufzählung wie folgt definiert:Während das Makro zunächst so aussieht, als wäre es eine Menge Arbeit, und die Definition von
OS_type
sieht eher fremd aus, denken Sie daran, dass Sie das Makro einmal schreiben müssen, dann können Sie es für jede Aufzählung verwenden. Sie können ihm ohne allzu große Probleme zusätzliche Funktionen hinzufügen (z. B. eine Konvertierung von Zeichenfolgen in Aufzählungszeichen), und das Wartungsproblem wird vollständig gelöst, da Sie die Namen beim Aufrufen des Makros nur einmal angeben müssen.Die Aufzählung kann dann so verwendet werden, als ob sie normal definiert wäre:
Die Code-Schnipsel in diesem Beitrag, beginnend mit der
#include <boost/preprocessor.hpp>
Zeile, können wie veröffentlicht kompiliert werden, um die Lösung zu demonstrieren.Diese spezielle Lösung ist für C ++ gedacht, da sie C ++ - spezifische Syntax (z. B. no
typedef enum
) und Funktionsüberladung verwendet. Es wäre jedoch unkompliziert, dies auch mit C zu tun.quelle
(Windows)
und(Windows, 3)
dann durchBOOST_PP_SEQ_ENUM
ein entsprechend geschriebenes ersetzenBOOST_PP_SEQ_FOR_EACH
. Ich habe kein Beispiel dafür, aber ich kann eines schreiben, wenn Sie möchten.Dies ist der Vorprozessorblock
Aufzählung Definition
Rufen Sie mit an
Von hier genommen . Wie cool ist das ? :) :)
quelle
Verwenden
std::map<OS_type, std::string>
und füllen Sie es mit enum als Schlüssel und Zeichenfolgendarstellung als Werte. Dann können Sie Folgendes tun:quelle
Das Problem mit C-Enums ist, dass es kein eigener Typ ist, wie es in C ++ ist. Eine Aufzählung in C ist eine Möglichkeit, Bezeichner Integralwerten zuzuordnen. Nur das. Aus diesem Grund ist ein Aufzählungswert mit ganzzahligen Werten austauschbar.
Wie Sie richtig erraten haben, besteht eine gute Möglichkeit darin, eine Zuordnung zwischen dem Aufzählungswert und einer Zeichenfolge zu erstellen. Beispielsweise:
quelle
enum
sind Typen in C. Integrale Aufzählungstypkonstanten sind vom Typint
und nicht vomenum
Typ, durch den sie definiert sind, ist vielleicht das, was Sie sagen wollten. Aber ich sehe überhaupt nicht, was das mit der Frage zu tun hat.Ich habe die Lösungen von James , Howard und Éder kombiniert und eine allgemeinere Implementierung erstellt:
Der vollständige Code wird unten geschrieben (verwenden Sie "DEFINE_ENUM_CLASS_WITH_ToString_METHOD" zum Definieren einer Aufzählung) ( Online-Demo ).
quelle
Dieses einfache Beispiel hat bei mir funktioniert. Hoffe das hilft.
quelle
Hast du das versucht:
Das
stringify()
Makro kann verwendet werden, um jeden Text in Ihrem Code in eine Zeichenfolge umzuwandeln, aber nur den genauen Text zwischen den Klammern. Es gibt keine variablen Dereferenzen oder Makrosubstitutionen oder andere Dinge, die getan werden.http://www.cplusplus.com/forum/general/2949/
quelle
Hier gibt es viele gute Antworten, aber ich dachte, einige Leute finden meine vielleicht nützlich. Ich mag es, weil die Schnittstelle, die Sie zum Definieren des Makros verwenden, so einfach wie möglich ist. Es ist auch praktisch, weil Sie keine zusätzlichen Bibliotheken hinzufügen müssen - alles wird mit C ++ geliefert und erfordert nicht einmal eine wirklich späte Version. Ich habe Stücke von verschiedenen Orten online gezogen, damit ich nicht alles gutschreiben kann, aber ich denke, es ist einzigartig genug, um eine neue Antwort zu rechtfertigen.
Erstellen Sie zuerst eine Header-Datei ... nennen Sie sie EnumMacros.h oder so ähnlich und fügen Sie diese ein:
Dann können Sie dies in Ihrem Hauptprogramm tun ...
Wo die Ausgabe wäre >> Der Wert von 'Apple' ist: 2 von 4
Genießen!
quelle
Angenommen, Ihre Aufzählung ist bereits definiert, können Sie ein Array von Paaren erstellen:
Jetzt können Sie eine Karte erstellen:
Jetzt können Sie die Karte verwenden. Wenn Ihre Aufzählung geändert wird, müssen Sie Paare zu Array-Paaren hinzufügen / entfernen []. Ich denke, dass es der eleganteste Weg ist, einen String aus enum in C ++ zu erhalten.
quelle
bimap
Boosts verwenden möchte, falls man Namen analysieren und in Aufzählungen umwandeln möchte (z. B. aus einer XML-Datei).Für C99 gibt es
P99_DECLARE_ENUM
in P99 , mit dem Sie einfach Folgendes deklarierenenum
können:und verwenden Sie dann
color_getname(A)
, um eine Zeichenfolge mit dem Farbnamen zu erhalten.quelle
Hier ist mein C ++ - Code:
quelle
Ein bisschen spät zur Party, aber hier ist meine C ++ 11-Lösung:
quelle
error: ‘hash’ is not a class template
->#include <functional>
Ich bevorzuge es, sowohl wiederholte Eingaben als auch schwer verständliche Makros zu minimieren und zu vermeiden, Makrodefinitionen in den allgemeinen Compilerbereich einzuführen.
Also, in der Header-Datei:
und die cpp-Implementierung ist:
Beachten Sie das #undef des Makros, sobald wir damit fertig sind.
quelle
Meine Lösung ohne Boost:
Und hier ist, wie man es benutzt
quelle
Ein weiterer Verspäteter auf der Party mit dem Präprozessor:
(Ich habe nur Zeilennummern eingegeben, damit Sie leichter darüber sprechen können.) Die Zeilen 1 bis 4 bearbeiten Sie, um die Elemente der Aufzählung zu definieren. (Ich habe es ein "Listenmakro" genannt, weil es ein Makro ist, das eine Liste von Dingen erstellt. @Lundin teilt mir mit, dass dies eine bekannte Technik namens X-Makros ist.)
Zeile 7 definiert das innere Makro, um die eigentliche Aufzählungsdeklaration in den Zeilen 8-11 auszufüllen. Zeile 12 hebt die Definition des inneren Makros auf (nur um die Compiler-Warnung auszuschalten).
Zeile 14 definiert das innere Makro, um eine Zeichenfolgenversion des Enum-Elementnamens zu erstellen. Dann erzeugen die Zeilen 15-18 ein Array, das einen Aufzählungswert in die entsprechende Zeichenfolge konvertieren kann.
Die Zeilen 21-27 generieren eine Funktion, die eine Zeichenfolge in den Aufzählungswert konvertiert, oder geben NULL zurück, wenn die Zeichenfolge mit keiner übereinstimmt.
Dies ist etwas umständlich im Umgang mit dem 0. Element. Ich habe das in der Vergangenheit tatsächlich umgangen.
Ich gebe zu, diese Technik stört Leute, die nicht glauben wollen, dass der Präprozessor selbst so programmiert werden kann, dass er Code für Sie schreibt. Ich denke, es zeigt deutlich den Unterschied zwischen Lesbarkeit und Wartbarkeit . Der Code ist schwer zu lesen, aber wenn die Aufzählung einige hundert Elemente enthält, können Sie Elemente hinzufügen, entfernen oder neu anordnen und trotzdem sicherstellen, dass der generierte Code keine Fehler enthält.
quelle
#define TEST_1 hello #define TEST_2 world
dann zu definierentypedef enum { TEST_1, TEST_2 } test_t;
und dann eine String-Nachschlagetabelle zu erstellen, die ein Stringify-Makro verwendet:const char* table[]= { STRINGIFY(TEST_1), STRINGIFY(TEST_2), };
Es gibt bereits mehrere Antworten, die auf ähnliche Lösungen hinweisen. Weitaus besser lesbar.Hier ist die Old Skool-Methode (die früher häufig in gcc verwendet wurde), bei der nur der C-Vorprozessor verwendet wird. Nützlich, wenn Sie diskrete Datenstrukturen generieren, aber die Reihenfolge zwischen ihnen konsistent halten müssen. Die Einträge in mylist.tbl können natürlich auf etwas viel Komplexeres erweitert werden.
test.cpp:
Und dann mylist.tbl:
quelle
In c ++ so:
quelle
von http://www.codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C und danach
einfügen
Funktioniert einwandfrei, wenn die Werte in der Aufzählung nicht doppelt vorhanden sind.
Beispielcode zum Konvertieren eines Enum-Werts in einen String:
Beispielcode für genau das Gegenteil:
quelle
Danke James für deinen Vorschlag. Es war sehr nützlich, also habe ich es umgekehrt implementiert, um einen Beitrag zu leisten.
quelle
Um James 'Antwort zu erweitern, möchte jemand einen Beispielcode, der die Enum-Definition mit int-Wert unterstützt. Ich habe auch diese Anforderung. Hier ist mein Weg:
Das erste ist das interne Verwendungsmakro, das von FOR_EACH verwendet wird:
Und hier ist das Definitionsmakro:
Wenn Sie es verwenden, möchten Sie vielleicht folgendermaßen schreiben:
welches erweitert wird zu:
Die Grundidee besteht darin, eine SEQ zu definieren, bei der jedes Element ein TUPLE ist, damit wir einen zusätzlichen Wert für das Enum-Member setzen können. Überprüfen Sie in der FOR_EACH-Schleife das Element TUPLE size. Wenn die Größe 2 ist, erweitern Sie den Code auf KEY = VALUE. Andernfalls behalten Sie einfach das erste Element von TUPLE bei.
Da es sich bei der Eingabe-SEQ tatsächlich um TUPLEs handelt. Wenn Sie also STRINGIZE-Funktionen definieren möchten, müssen Sie möglicherweise zuerst die Eingabe-Enumeratoren vorverarbeiten. Hier ist das Makro, um den Job auszuführen:
Das Makro
DEFINE_ENUM_WITH_STRING_CONVERSIONS_FIRST_ELEM_SEQ
behält nur das erste Element in jedem TUPLE bei und konvertiert später in SEQ. Ändern Sie jetzt James 'Code. Sie haben die volle Leistung.Meine Implementierung ist möglicherweise nicht die einfachste. Wenn Sie also keinen sauberen Code finden, verwenden Sie meinen als Referenz.
quelle
Saubere, sichere Lösung in reinem Standard C:
Ausgabe
Begründung
Bei der Lösung des Kernproblems "Enum-Konstanten mit entsprechenden Zeichenfolgen haben" stellt ein vernünftiger Programmierer die folgenden Anforderungen:
Die erste Anforderung und vielleicht auch die zweite kann mit verschiedenen chaotischen Makrolösungen wie dem berüchtigten "x-Makro" -Trick oder anderen Formen der Makromagie erfüllt werden. Das Problem bei solchen Lösungen ist, dass sie Ihnen ein völlig unlesbares Durcheinander mysteriöser Makros hinterlassen - sie erfüllen nicht die oben genannte dritte Anforderung.
Das einzige, was hier benötigt wird, ist eine String-Nachschlagetabelle, auf die wir zugreifen können, indem wir die Variable enum als Index verwenden. Eine solche Tabelle muss natürlich direkt der Aufzählung entsprechen und umgekehrt. Wenn einer von ihnen aktualisiert wird, muss auch der andere aktualisiert werden, sonst funktioniert es nicht.
Erläuterung des Codes
Angenommen, wir haben eine Aufzählung wie
Dies kann in geändert werden
Mit dem Vorteil, dass diese Makrokonstanten jetzt an anderer Stelle verwendet werden können, um beispielsweise eine String-Nachschlagetabelle zu generieren. Das Konvertieren einer Vorprozessorkonstante in eine Zeichenfolge kann mit einem "stringify" -Makro erfolgen:
Und das ist es. Mit using erhalten
hello
wir die Enum-Konstante mit dem Wert 0. Mit using erhaltentest_str[hello]
wir die Zeichenfolge "hello".Damit die Aufzählung und die Nachschlagetabelle direkt übereinstimmen, müssen wir sicherstellen, dass sie genau die gleiche Anzahl von Elementen enthalten. Wenn jemand den Code pflegen und nur die Aufzählung und nicht die Nachschlagetabelle ändern würde oder umgekehrt, funktioniert diese Methode nicht.
Die Lösung besteht darin, die Aufzählung zu haben, um Ihnen zu sagen, wie viele Elemente es enthält. Hierfür gibt es einen häufig verwendeten C-Trick. Fügen Sie einfach am Ende ein Element hinzu, das nur den Zweck erfüllt, zu bestimmen, wie viele Elemente die Aufzählung enthält:
Jetzt können wir zur Kompilierungszeit überprüfen, ob die Anzahl der Elemente in der Aufzählung so hoch ist wie die Anzahl der Elemente in der Nachschlagetabelle, vorzugsweise mit einer statischen C11-Zusicherung:
(Es gibt hässliche, aber voll funktionsfähige Möglichkeiten, statische Asserts auch in älteren Versionen des C-Standards zu erstellen, wenn jemand darauf besteht, Dinosaurier-Compiler zu verwenden. C ++ unterstützt auch statische Asserts.)
Nebenbei bemerkt können wir in C11 auch eine höhere Typensicherheit erzielen, indem wir das Stringify-Makro ändern:
(
int
weil Aufzählungskonstanten tatsächlich vom Typ sindint
, nichttest_t
)Dadurch wird verhindert, dass Code wie
STRINGIFY(random_stuff)
kompiliert wird.quelle
#define
, fügen Sie einen Verweis auf die Definition hinzu, die in der Aufzählungsdeklaration und in der Nachschlagetabelle definiert ist. Wenn Sie beim Hinzufügen dieser Zeilen Fehler machen würden, wird das Programm nicht kompiliert. Die Nummern, die ich zu den Bezeichnern hinzugefügt habe, sind keineswegs obligatorisch. Sie können auch schreiben#define APPLES hello
und#define ORANGES world
folgentypedef enum { APPES, ORANGES, TEST_N } test_t;
und so weiter.Was ich gemacht habe, ist eine Kombination aus dem, was ich hier und in ähnlichen Fragen auf dieser Seite gesehen habe. Ich habe dies für Visual Studio 2013 gemacht. Ich habe es nicht mit anderen Compilern getestet.
Zunächst definiere ich eine Reihe von Makros, die die Tricks ausführen.
Definieren Sie als Nächstes ein einzelnes Makro, das die Aufzählungsklasse und die Funktionen zum Abrufen der Zeichenfolgen erstellt.
Jetzt wird es wirklich einfach, einen Aufzählungstyp zu definieren und Zeichenfolgen dafür zu haben. Alles was Sie tun müssen ist:
Die folgenden Zeilen können zum Testen verwendet werden.
Dies wird Folgendes ausgeben:
Ich glaube, es ist sehr sauber und einfach zu bedienen. Es gibt einige Einschränkungen:
Wenn Sie das umgehen können. Ich denke, besonders wie man es benutzt, das ist schön und schlank. Vorteile:
quelle
Eine saubere Lösung für dieses Problem wäre:
Das Gute an dieser Lösung ist, dass sie einfach ist und der Aufbau der Funktion auch einfach durch Kopieren und Ersetzen erfolgen kann. Beachten Sie, dass diese Lösung möglicherweise CPU-intensiv wird, wenn Sie viele Konvertierungen durchführen und Ihre Aufzählung zu viele mögliche Werte enthält.
quelle
Ich bin etwas spät dran, aber hier ist meine Lösung mit g ++ und nur Standardbibliotheken. Ich habe versucht, die Verschmutzung durch Namespaces zu minimieren und die Notwendigkeit zu beseitigen, Enum-Namen erneut einzugeben.
Die Header-Datei "my_enum.hpp" lautet:
Anwendungsbeispiel:
Dies wird Folgendes ausgeben:
Sie müssen alles nur einmal definieren, Ihr Namespace sollte nicht verschmutzt sein und die gesamte Berechnung wird nur einmal durchgeführt (der Rest sind nur Suchvorgänge). Sie erhalten jedoch nicht die Typensicherheit von Enum-Klassen (es handelt sich immer noch um kurze Ganzzahlen). Sie können den Enums keine Werte zuweisen. Sie müssen Enums an einer Stelle definieren, an der Sie Namespaces definieren können (z. B. global).
Ich bin mir nicht sicher, wie gut die Leistung ist oder ob es eine gute Idee ist (ich habe C vor C ++ gelernt, damit mein Gehirn immer noch so funktioniert). Wenn jemand weiß, warum dies eine schlechte Idee ist, können Sie darauf hinweisen.
quelle
Es ist 2017, aber die Frage ist noch am Leben
Noch ein anderer Weg:
Ausgänge:
quelle
Dies ist eine ausgearbeitete erweiterte Enum-Version für Klassen. Sie fügt keinen anderen Enum-Wert als den angegebenen hinzu.
Verwendung: DECLARE_ENUM_SEQ (CameraMode, (3), Fly, FirstPerson, PerspectiveCorrect)
quelle
Ich brauchte dies, um in beide Richtungen zu arbeiten UND ich bettete meine Aufzählungen häufig in eine enthaltende Klasse ein, und so begann ich mit der Lösung von James McNellis ganz oben in diesen Antworten, aber ich machte diese Lösung. Beachten Sie auch, dass ich lieber eine Aufzählungsklasse als nur eine Aufzählung bevorzuge, was die Antwort etwas erschwert.
Um es in einer Klasse zu verwenden, können Sie Folgendes tun:
Und ich habe einen CppUnit-Test geschrieben, der zeigt, wie man ihn benutzt:
Sie müssen auswählen, welches Makro verwendet werden soll, entweder DEFINE_ENUMERATION oder DEFINE_ENUMERATION_INSIDE_CLASS. Sie werden sehen, dass ich Letzteres beim Definieren von ComponentStatus :: Status verwendet habe, aber Ersteres beim Definieren von Status. Der Unterschied ist einfach. Innerhalb einer Klasse stelle ich die to / from-Methoden als "statisch" voran und wenn nicht in einer Klasse, verwende ich "inline". Triviale Unterschiede, aber notwendig.
Leider glaube ich nicht, dass es einen sauberen Weg gibt, dies zu vermeiden:
Obwohl Sie nach Ihrer Klassendefinition manuell eine Inline-Methode erstellen können, die einfach mit der Klassenmethode verkettet ist, können Sie Folgendes tun:
quelle
Meine eigene Antwort, ohne Boost - mit meinem eigenen Ansatz ohne starke Definitionsmagie, und diese Lösung hat die Einschränkung, dass sie keinen bestimmten Aufzählungswert definieren kann.
Die neueste Version finden Sie auf Github hier:
https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/EnumReflect.h
quelle
Es gibt viele andere Antworten darauf, aber ich denke, ein besserer Weg ist es, C ++ 17-Funktionen zu verwenden und constexpr zu verwenden, damit die Übersetzungen zur Kompilierungszeit erstellt werden. Dies ist typsicher und wir müssen uns nicht mit Makros herumschlagen. Siehe unten:
Dies kann dann einfach verwendet werden, so dass beim Kompilieren Zeichenfolgenschlüsselfehler erkannt werden:
Der Code ist ausführlicher als einige andere Lösungen, aber wir können die Konvertierung von Enum in String und die Konvertierung von String in Enum zur Kompilierungszeit problemlos durchführen und Typfehler erkennen. Mit einigen der zukünftigen C ++ 20-Funktionen kann dies wahrscheinlich etwas vereinfacht werden.
quelle