Wie kann man printf dazu bringen, 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 wie
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 die schönste Art ist, es zu tun. Ist es überhaupt möglich?
Es gibt wirklich keine schöne Art, dies zu tun. Richten Sie einfach ein Array von Zeichenfolgen ein, die von der Aufzählung indiziert werden.
Wenn Sie viel ausgeben, können Sie einen Operator<< definieren, der einen Aufzählungsparameter akzeptiert und die Suche für Sie übernimmt.
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:
printf("My OS is %s", enumMap[myOS].c_str());
std::cout << enumMap[myOS] ;
Ich habe die Lösungen von James, Howard und Éder kombiniert und eine allgemeinere Implementierung erstellt:
- Int-Wert und benutzerdefinierte Zeichenfolgendarstellung können optional für jedes Enum-Element definiert werden
- “enum-Klasse” wird verwendet
Der vollständige Code ist unten geschrieben (verwenden Sie “DEFINE_ENUM_CLASS_WITH_ToString_METHOD” zum Definieren einer Aufzählung) (Online-Demo).
#include <boost/preprocessor.hpp>
#include <iostream>
// ADD_PARENTHESES_FOR_EACH_TUPLE_IN_SEQ implementation is taken from:
// http://lists.boost.org/boost-users/2012/09/76055.php
//
// This macro do the following:
// input:
// (Element1, "Element 1 string repr", 2) (Element2) (Element3, "Element 3 string repr")
// output:
// ((Element1, "Element 1 string repr", 2)) ((Element2)) ((Element3, "Element 3 string repr"))
#define HELPER1(...) ((__VA_ARGS__)) HELPER2
#define HELPER2(...) ((__VA_ARGS__)) HELPER1
#define HELPER1_END
#define HELPER2_END
#define ADD_PARENTHESES_FOR_EACH_TUPLE_IN_SEQ(sequence) BOOST_PP_CAT(HELPER1 sequence,_END)
// CREATE_ENUM_ELEMENT_IMPL works in the following way:
// if (elementTuple.GetSize() == 4) {
// GENERATE: elementTuple.GetElement(0) = elementTuple.GetElement(2)),
// } else {
// GENERATE: elementTuple.GetElement(0),
// }
// Example 1:
// CREATE_ENUM_ELEMENT_IMPL((Element1, "Element 1 string repr", 2, _))
// generates:
// Element1 = 2,
//
// Example 2:
// CREATE_ENUM_ELEMENT_IMPL((Element2, _))
// generates:
// Element1,
#define CREATE_ENUM_ELEMENT_IMPL(elementTuple) \
BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_TUPLE_SIZE(elementTuple), 4), \
BOOST_PP_TUPLE_ELEM(0, elementTuple) = BOOST_PP_TUPLE_ELEM(2, elementTuple), \
BOOST_PP_TUPLE_ELEM(0, elementTuple) \
),
// we have to add a dummy element at the end of a tuple in order to make
// BOOST_PP_TUPLE_ELEM macro work in case an initial tuple has only one element.
// if we have a tuple (Element1), BOOST_PP_TUPLE_ELEM(2, (Element1)) macro won't compile.
// It requires that a tuple with only one element looked like (Element1,).
// Unfortunately I couldn't find a way to make this transformation, so
// I just use BOOST_PP_TUPLE_PUSH_BACK macro to add a dummy element at the end
// of a tuple, in this case the initial tuple will look like (Element1, _) what
// makes it compatible with BOOST_PP_TUPLE_ELEM macro
#define CREATE_ENUM_ELEMENT(r, data, elementTuple) \
CREATE_ENUM_ELEMENT_IMPL(BOOST_PP_TUPLE_PUSH_BACK(elementTuple, _))
#define DEFINE_CASE_HAVING_ONLY_ENUM_ELEMENT_NAME(enumName, element) \
case enumName::element : return BOOST_PP_STRINGIZE(element);
#define DEFINE_CASE_HAVING_STRING_REPRESENTATION_FOR_ENUM_ELEMENT(enumName, element, stringRepresentation) \
case enumName::element : return stringRepresentation;
// GENERATE_CASE_FOR_SWITCH macro generates case for switch operator.
// Algorithm of working is the following
// if (elementTuple.GetSize() == 1) {
// DEFINE_CASE_HAVING_ONLY_ENUM_ELEMENT_NAME(enumName, elementTuple.GetElement(0))
// } else {
// DEFINE_CASE_HAVING_STRING_REPRESENTATION_FOR_ENUM_ELEMENT(enumName, elementTuple.GetElement(0), elementTuple.GetElement(1))
// }
//
// Example 1:
// GENERATE_CASE_FOR_SWITCH(_, EnumName, (Element1, "Element 1 string repr", 2))
// generates:
// case EnumName::Element1 : return "Element 1 string repr";
//
// Example 2:
// GENERATE_CASE_FOR_SWITCH(_, EnumName, (Element2))
// generates:
// case EnumName::Element2 : return "Element2";
#define GENERATE_CASE_FOR_SWITCH(r, enumName, elementTuple) \
BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_TUPLE_SIZE(elementTuple), 1), \
DEFINE_CASE_HAVING_ONLY_ENUM_ELEMENT_NAME(enumName, BOOST_PP_TUPLE_ELEM(0, elementTuple)), \
DEFINE_CASE_HAVING_STRING_REPRESENTATION_FOR_ENUM_ELEMENT(enumName, BOOST_PP_TUPLE_ELEM(0, elementTuple), BOOST_PP_TUPLE_ELEM(1, elementTuple)) \
)
// DEFINE_ENUM_CLASS_WITH_ToString_METHOD final macro witch do the job
#define DEFINE_ENUM_CLASS_WITH_ToString_METHOD(enumName, enumElements) \
enum class enumName { \
BOOST_PP_SEQ_FOR_EACH( \
CREATE_ENUM_ELEMENT, \
0, \
ADD_PARENTHESES_FOR_EACH_TUPLE_IN_SEQ(enumElements) \
) \
}; \
inline const char* ToString(const enumName element) { \
switch (element) { \
BOOST_PP_SEQ_FOR_EACH( \
GENERATE_CASE_FOR_SWITCH, \
enumName, \
ADD_PARENTHESES_FOR_EACH_TUPLE_IN_SEQ(enumElements) \
) \
default: return "[Unknown " BOOST_PP_STRINGIZE(enumName) "]"; \
} \
}
DEFINE_ENUM_CLASS_WITH_ToString_METHOD(Elements,
(Element1)
(Element2, "string representation for Element2 ")
(Element3, "Element3 string representation", 1000)
(Element4, "Element 4 string repr")
(Element5, "Element5", 1005)
(Element6, "Element6 ")
(Element7)
)
// Generates the following:
// enum class Elements {
// Element1, Element2, Element3 = 1000, Element4, Element5 = 1005, Element6,
// };
// inline const char* ToString(const Elements element) {
// switch (element) {
// case Elements::Element1: return "Element1";
// case Elements::Element2: return "string representation for Element2 ";
// case Elements::Element3: return "Element3 string representation";
// case Elements::Element4: return "Element 4 string repr";
// case Elements::Element5: return "Element5";
// case Elements::Element6: return "Element6 ";
// case Elements::Element7: return "Element7";
// default: return "[Unknown " "Elements" "]";
// }
// }
int main() {
std::cout << ToString(Elements::Element1) << std::endl;
std::cout << ToString(Elements::Element2) << std::endl;
std::cout << ToString(Elements::Element3) << std::endl;
std::cout << ToString(Elements::Element4) << std::endl;
std::cout << ToString(Elements::Element5) << std::endl;
std::cout << ToString(Elements::Element6) << std::endl;
std::cout << ToString(Elements::Element7) << std::endl;
return 0;
}
Das Problem mit C-Enumerationen ist, dass es kein eigener Typ ist, wie es in C++ der Fall ist. Eine Aufzählung in C ist eine Möglichkeit, Bezeichner ganzzahligen Werten zuzuordnen. Nur das. Aus diesem Grund ist ein Enum-Wert 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. Zum Beispiel:
char * OS_type_label[] = {
"Linux",
"Apple",
"Windows"
};
Treffen Sie Ihre Wahl: hier, hier, hier. Es ist ein Durcheinander, sie sind alle Halbduplikate.
– rubenvb
11. Februar 2014 um 10:25 Uhr
Ein anderer, dieser ist vollständiger.
– bit2shift
27. März 2016 um 0:21 Uhr
Mögliches Duplikat von stackoverflow.com/questions/207976/… für C++ und stackoverflow.com/questions/9907160/… für C
– Ciro Santilli Путлер Капут 六四事
11. Dezember 2016 um 15:01 Uhr
So viele Antworten, aber keine “Enumerationsklasse”
– Vivic
23. Juni 2017 um 13:21 Uhr