Inilah yang saya coba lakukan:
typedef enum { ONE, TWO, THREE } Numbers;
Saya mencoba menulis fungsi yang akan melakukan kasus sakelar yang mirip dengan berikut ini:
char num_str[10];
int process_numbers_str(Numbers num) {
switch(num) {
case ONE:
case TWO:
case THREE:
{
strcpy(num_str, num); //some way to get the symbolic constant name in here?
} break;
default:
return 0; //no match
return 1;
}
Alih-alih mendefinisikan di setiap kasus, apakah ada cara untuk mengaturnya menggunakan variabel enum seperti yang saya coba lakukan di atas?
c
enums
c-preprocessor
zxcv
sumber
sumber
Teknik dari Membuat sesuatu baik pengenal C dan string? bisa digunakan disini.
Seperti biasa dengan hal-hal preprocessor seperti itu, menulis dan memahami bagian preprocessor bisa jadi sulit, dan termasuk meneruskan makro ke makro lain dan melibatkan penggunaan operator # dan ##, tetapi menggunakannya sangat mudah. Saya menemukan gaya ini sangat berguna untuk enum panjang, di mana mempertahankan daftar yang sama dua kali bisa sangat merepotkan.
Kode pabrik - hanya diketik sekali, biasanya disembunyikan di tajuk:
enumFactory.h:
// expansion macro for enum value definition #define ENUM_VALUE(name,assign) name assign, // expansion macro for enum to string conversion #define ENUM_CASE(name,assign) case name: return #name; // expansion macro for string to enum conversion #define ENUM_STRCMP(name,assign) if (!strcmp(str,#name)) return name; /// declare the access function and define enum values #define DECLARE_ENUM(EnumType,ENUM_DEF) \ enum EnumType { \ ENUM_DEF(ENUM_VALUE) \ }; \ const char *GetString(EnumType dummy); \ EnumType Get##EnumType##Value(const char *string); \ /// define the access function names #define DEFINE_ENUM(EnumType,ENUM_DEF) \ const char *GetString(EnumType value) \ { \ switch(value) \ { \ ENUM_DEF(ENUM_CASE) \ default: return ""; /* handle input error */ \ } \ } \ EnumType Get##EnumType##Value(const char *str) \ { \ ENUM_DEF(ENUM_STRCMP) \ return (EnumType)0; /* handle input error */ \ } \
Pabrik digunakan
someEnum.h:
#include "enumFactory.h" #define SOME_ENUM(XX) \ XX(FirstValue,) \ XX(SecondValue,) \ XX(SomeOtherValue,=50) \ XX(OneMoreValue,=100) \ DECLARE_ENUM(SomeEnum,SOME_ENUM)
someEnum.cpp:
#include "someEnum.h" DEFINE_ENUM(SomeEnum,SOME_ENUM)
Teknik ini dapat dengan mudah diperpanjang sehingga makro XX menerima lebih banyak argumen, dan Anda juga dapat menyiapkan lebih banyak makro untuk menggantikan XX untuk kebutuhan yang berbeda, serupa dengan tiga yang telah saya berikan dalam contoh ini.
Perbandingan dengan X-Macro menggunakan #include / #define / #undef
Meskipun ini mirip dengan X-Macro yang telah disebutkan orang lain, saya pikir solusi ini lebih elegan karena tidak memerlukan #undefing apa pun, yang memungkinkan Anda untuk menyembunyikan lebih banyak hal rumit yang ada di pabrik file header - file header adalah sesuatu yang tidak Anda sentuh sama sekali saat Anda perlu menentukan enum baru, oleh karena itu definisi enum baru jauh lebih pendek dan lebih rapi.
sumber
SOME_ENUM(XX)
persis X-makro (tepatnya, "bentuk pengguna" yang melewatiXX
fungsi daripada menggunakan#def
#undef
) dan kemudian pada gilirannya seluruh X-MACRO kemudian diteruskan ke DEFINE_ENUM yang menggunakan itu. Tidak mengambil apa pun dari solusi - ini bekerja dengan baik. Hanya untuk memperjelas bahwa ini adalah penggunaan makro X.XX
over reing#define
adalah bahwa pola sebelumnya dapat digunakan dalam ekspansi makro. Perhatikan bahwa satu-satunya solusi lain sesingkat yang satu ini semuanya memerlukan pembuatan dan banyak penyertaan file terpisah untuk menentukan enum baru.#define DEFINE_ENUM(EnumType) ...
, menggantiENUM_DEF(...)
denganEnumType(...)
, dan meminta pengguna mengatakannya#define SomeEnum(XX) ...
. Praprosesor C secara kontekstual akan berkembangSomeEnum
menjadi pemanggilan makro saat diikuti oleh tanda kurung dan sebaliknya menjadi token biasa. (Tentu saja, ini menyebabkan masalah jika pengguna lebih suka menggunakanSomeEnum(2)
untuk mentransmisikan ke jenis enum daripada(SomeEnum)2
ataustatic_cast<SomeEnum>(2)
.)#define
dan#undef
. Apakah Anda tidak setuju bahwa "formulir pengguna" (disarankan misalnya, di bagian bawah artikel ini ) adalah jenis x-makro? Saya pasti selalu menyebutnya x-makro juga dan dalam basis kode C yang saya kunjungi akhir-akhir ini, ini adalah bentuk yang paling umum (itu jelas pengamatan yang bias). Saya mungkin telah salah mengurai OP.// Define your enumeration like this (in say numbers.h); ENUM_BEGIN( Numbers ) ENUM(ONE), ENUM(TWO), ENUM(FOUR) ENUM_END( Numbers ) // The macros are defined in a more fundamental .h file (say defs.h); #define ENUM_BEGIN(typ) enum typ { #define ENUM(nam) nam #define ENUM_END(typ) }; // Now in one and only one .c file, redefine the ENUM macros and reinclude // the numbers.h file to build a string table #undef ENUM_BEGIN #undef ENUM #undef ENUM_END #define ENUM_BEGIN(typ) const char * typ ## _name_table [] = { #define ENUM(nam) #nam #define ENUM_END(typ) }; #undef NUMBERS_H_INCLUDED // whatever you need to do to enable reinclusion #include "numbers.h" // Now you can do exactly what you want to do, with no retyping, and for any // number of enumerated types defined with the ENUM macro family // Your code follows; char num_str[10]; int process_numbers_str(Numbers num) { switch(num) { case ONE: case TWO: case THREE: { strcpy(num_str, Numbers_name_table[num]); // eg TWO -> "TWO" } break; default: return 0; //no match return 1; } // Sweet no ? After being frustrated by this for years, I finally came up // with this solution for my most recent project and plan to reuse the idea // forever
sumber
#define ENUM_END(typ) }; extern const char * typ ## _name_table[];
dalamdefs.h
file - ini akan mendeklarasikan tabel nama Anda di file yang Anda gunakan. (Namun, tidak dapat menemukan cara yang baik untuk menyatakan ukuran tabel.) Selain itu, secara pribadi saya akan meninggalkan titik koma terakhir, tetapi manfaatnya masih bisa diperdebatkan.typ
mengantre#define ENUM_END(typ) };
?Pasti ada cara untuk melakukan ini - gunakan makro X () . Makro ini menggunakan praprosesor C untuk membuat enum, array, dan blok kode dari daftar data sumber. Anda hanya perlu menambahkan item baru ke #define yang berisi makro X (). Pernyataan switch akan meluas secara otomatis.
Contoh Anda dapat ditulis sebagai berikut:
// Source data -- Enum, String #define X_NUMBERS \ X(ONE, "one") \ X(TWO, "two") \ X(THREE, "three") ... // Use preprocessor to create the Enum typedef enum { #define X(Enum, String) Enum, X_NUMBERS #undef X } Numbers; ... // Use Preprocessor to expand data into switch statement cases switch(num) { #define X(Enum, String) \ case Enum: strcpy(num_str, String); break; X_NUMBERS #undef X default: return 0; break; } return 1;
Ada cara yang lebih efisien (yaitu menggunakan X Macro untuk membuat larik string dan indeks enum), tetapi ini adalah demo yang paling sederhana.
sumber
Saya tahu Anda memiliki beberapa jawaban yang bagus dan solid, tetapi tahukah Anda tentang operator # dalam C preprocessor?
Ini memungkinkan Anda melakukan ini:
#define MACROSTR(k) #k typedef enum { kZero, kOne, kTwo, kThree } kConst; static char *kConstStr[] = { MACROSTR(kZero), MACROSTR(kOne), MACROSTR(kTwo), MACROSTR(kThree) }; static void kConstPrinter(kConst k) { printf("%s", kConstStr[k]); }
sumber
char const *kConstStr[]
C atau C ++ tidak menyediakan fungsionalitas ini, meskipun saya sering membutuhkannya.
Kode berikut berfungsi, meskipun paling cocok untuk enum non-sparse.
typedef enum { ONE, TWO, THREE } Numbers; char *strNumbers[] = {"one","two","three"}; printf ("Value for TWO is %s\n",strNumbers[TWO]);
Dengan non-jarang, maksud saya bukan dari bentuk
typedef enum { ONE, FOUR_THOUSAND = 4000 } Numbers;
karena ada celah besar di dalamnya.
Keuntungan dari metode ini adalah ia menempatkan definisi enum dan string di dekat satu sama lain; memiliki pernyataan switch dalam suatu fungsi mempelopori mereka. Ini berarti Anda cenderung tidak mengubah yang satu tanpa yang lain.
sumber
CIUMAN. Anda akan melakukan segala macam hal switch / case lain dengan enum Anda jadi mengapa pencetakan harus berbeda? Melupakan tas dalam rutinitas pencetakan Anda bukanlah masalah besar ketika Anda mempertimbangkan ada sekitar 100 tempat lain di mana Anda dapat melupakan tas. Cukup kompilasi -Wall, yang akan memperingatkan kecocokan kasus yang tidak lengkap. Jangan gunakan "default" karena itu akan membuat peralihan menjadi lengkap dan Anda tidak akan mendapatkan peringatan. Sebaliknya, biarkan saklar keluar dan tangani kasus default seperti ...
const char *myenum_str(myenum e) { switch(e) { case ONE: return "one"; case TWO: return "two"; } return "invalid"; }
sumber
Coba Ubah enum C ++ menjadi string . The komentar memiliki perbaikan yang memecahkan masalah ketika item enum memiliki nilai sewenang-wenang.
sumber
Penggunaan boost :: preprocessor memungkinkan solusi elegan seperti berikut:
Langkah 1: Sertakan file header:
#include "EnumUtilities.h"
Langkah 2: Deklarasikan objek enumerasi dengan sintaks berikut:
Langkah 3: Gunakan data Anda:
Mendapatkan jumlah elemen:
td::cout << "Number of Elements: " << TestDataCount << std::endl;
Mendapatkan string terkait:
std::cout << "Value of " << TestData2String(x) << " is " << x << std::endl; std::cout << "Value of " << TestData2String(y) << " is " << y << std::endl; std::cout << "Value of " << TestData2String(z) << " is " << z << std::endl;
Mendapatkan nilai enum dari string terkait:
std::cout << "Value of x is " << TestData2Enum("x") << std::endl; std::cout << "Value of y is " << TestData2Enum("y") << std::endl; std::cout << "Value of z is " << TestData2Enum("z") << std::endl;
Ini terlihat bersih dan kompak, tanpa file tambahan untuk disertakan. Kode yang saya tulis dalam EnumUtilities.h adalah sebagai berikut:
#include <boost/preprocessor/seq/for_each.hpp> #include <string> #define REALLY_MAKE_STRING(x) #x #define MAKE_STRING(x) REALLY_MAKE_STRING(x) #define MACRO1(r, data, elem) elem, #define MACRO1_STRING(r, data, elem) case elem: return REALLY_MAKE_STRING(elem); #define MACRO1_ENUM(r, data, elem) if (REALLY_MAKE_STRING(elem) == eStrEl) return elem; #define MakeEnum(eName, SEQ) \ enum eName { BOOST_PP_SEQ_FOR_EACH(MACRO1, , SEQ) \ last_##eName##_enum}; \ const int eName##Count = BOOST_PP_SEQ_SIZE(SEQ); \ static std::string eName##2String(const enum eName eel) \ { \ switch (eel) \ { \ BOOST_PP_SEQ_FOR_EACH(MACRO1_STRING, , SEQ) \ default: return "Unknown enumerator value."; \ }; \ }; \ static enum eName eName##2Enum(const std::string eStrEl) \ { \ BOOST_PP_SEQ_FOR_EACH(MACRO1_ENUM, , SEQ) \ return (enum eName)0; \ };
Ada beberapa batasan, yaitu salah satunya boost :: preprocessor. Dalam kasus ini, daftar konstanta tidak boleh lebih dari 64 elemen.
Mengikuti logika yang sama, Anda juga bisa berpikir untuk membuat enum renggang:
#define EnumName(Tuple) BOOST_PP_TUPLE_ELEM(2, 0, Tuple) #define EnumValue(Tuple) BOOST_PP_TUPLE_ELEM(2, 1, Tuple) #define MACRO2(r, data, elem) EnumName(elem) EnumValue(elem), #define MACRO2_STRING(r, data, elem) case EnumName(elem): return BOOST_PP_STRINGIZE(EnumName(elem)); #define MakeEnumEx(eName, SEQ) \ enum eName { \ BOOST_PP_SEQ_FOR_EACH(MACRO2, _, SEQ) \ last_##eName##_enum }; \ const int eName##Count = BOOST_PP_SEQ_SIZE(SEQ); \ static std::string eName##2String(const enum eName eel) \ { \ switch (eel) \ { \ BOOST_PP_SEQ_FOR_EACH(MACRO2_STRING, _, SEQ) \ default: return "Unknown enumerator value."; \ }; \ };
Dalam hal ini, sintaksnya adalah:
MakeEnumEx(TestEnum, ((x,)) ((y,=1000)) ((z,)) );
Penggunaannya mirip seperti di atas (tanpa fungsi eName ## 2Enum, yang dapat Anda coba ekstrapolasi dari sintaks sebelumnya).
Saya mengujinya di mac dan linux, tetapi perlu diketahui bahwa boost :: preprocessor mungkin tidak sepenuhnya portabel.
sumber
Dengan menggabungkan beberapa teknik di sini, saya mendapatkan bentuk yang paling sederhana:
#define MACROSTR(k) #k #define X_NUMBERS \ X(kZero ) \ X(kOne ) \ X(kTwo ) \ X(kThree ) \ X(kFour ) \ X(kMax ) enum { #define X(Enum) Enum, X_NUMBERS #undef X } kConst; static char *kConstStr[] = { #define X(String) MACROSTR(String), X_NUMBERS #undef X }; int main(void) { int k; printf("Hello World!\n\n"); for (k = 0; k < kMax; k++) { printf("%s\n", kConstStr[k]); } return 0; }
sumber
Jika Anda menggunakan gcc, Anda dapat menggunakan:
const char * enum_to_string_map[]={ [enum1]='string1', [enum2]='string2'};
Kemudian panggil saja misalnya
sumber
Lihat ide-ide di Mu Dynamics Research Labs - Arsip Blog . Saya menemukan ini awal tahun ini - saya lupa konteks persis di mana saya menemukannya - dan telah menyesuaikannya ke dalam kode ini. Kita bisa memperdebatkan manfaat menambahkan E di depan; ini berlaku untuk masalah spesifik yang ditangani, tetapi bukan bagian dari solusi umum. Saya menyimpannya di folder 'sketsa' saya - tempat saya menyimpan potongan kode yang menarik jika saya menginginkannya nanti. Saya malu untuk mengatakan bahwa saya tidak mencatat dari mana ide ini berasal pada saat itu.
Header: paste1.h
/* @(#)File: $RCSfile: paste1.h,v $ @(#)Version: $Revision: 1.1 $ @(#)Last changed: $Date: 2008/05/17 21:38:05 $ @(#)Purpose: Automated Token Pasting */ #ifndef JLSS_ID_PASTE_H #define JLSS_ID_PASTE_H /* * Common case when someone just includes this file. In this case, * they just get the various E* tokens as good old enums. */ #if !defined(ETYPE) #define ETYPE(val, desc) E##val, #define ETYPE_ENUM enum { #endif /* ETYPE */ ETYPE(PERM, "Operation not permitted") ETYPE(NOENT, "No such file or directory") ETYPE(SRCH, "No such process") ETYPE(INTR, "Interrupted system call") ETYPE(IO, "I/O error") ETYPE(NXIO, "No such device or address") ETYPE(2BIG, "Arg list too long") /* * Close up the enum block in the common case of someone including * this file. */ #if defined(ETYPE_ENUM) #undef ETYPE_ENUM #undef ETYPE ETYPE_MAX }; #endif /* ETYPE_ENUM */ #endif /* JLSS_ID_PASTE_H */
Contoh sumber:
/* @(#)File: $RCSfile: paste1.c,v $ @(#)Version: $Revision: 1.2 $ @(#)Last changed: $Date: 2008/06/24 01:03:38 $ @(#)Purpose: Automated Token Pasting */ #include "paste1.h" static const char *sys_errlist_internal[] = { #undef JLSS_ID_PASTE_H #define ETYPE(val, desc) desc, #include "paste1.h" 0 #undef ETYPE }; static const char *xerror(int err) { if (err >= ETYPE_MAX || err <= 0) return "Unknown error"; return sys_errlist_internal[err]; } static const char*errlist_mnemonics[] = { #undef JLSS_ID_PASTE_H #define ETYPE(val, desc) [E ## val] = "E" #val, #include "paste1.h" #undef ETYPE }; #include <stdio.h> int main(void) { int i; for (i = 0; i < ETYPE_MAX; i++) { printf("%d: %-6s: %s\n", i, errlist_mnemonics[i], xerror(i)); } return(0); }
Tidak selalu merupakan penggunaan pra-prosesor C terbersih di dunia - tetapi itu mencegah penulisan materi beberapa kali.
sumber
Membuat sesuatu menjadi pengenal C dan string
sumber
Jika indeks enum berbasis 0, Anda dapat meletakkan nama-nama dalam array char *, dan mengindeksnya dengan nilai enum.
sumber
#define stringify( name ) # name enum MyEnum { ENUMVAL1 }; ...stuff... stringify(EnumName::ENUMVAL1); // Returns MyEnum::ENUMVAL1
Pembahasan lebih lanjut tentang metode ini
Trik direktif preprocessor untuk pendatang baru
sumber
Saya telah menciptakan sebuah kelas sederhana templated
streamable_enum
yang menggunakan streaming operator<<
dan>>
dan didasarkan padastd::map<Enum, std::string>
:#ifndef STREAMABLE_ENUM_HPP #define STREAMABLE_ENUM_HPP #include <iostream> #include <string> #include <map> template <typename E> class streamable_enum { public: typedef typename std::map<E, std::string> tostr_map_t; typedef typename std::map<std::string, E> fromstr_map_t; streamable_enum() {} streamable_enum(E val) : Val_(val) {} operator E() { return Val_; } bool operator==(const streamable_enum<E>& e) { return this->Val_ == e.Val_; } bool operator==(const E& e) { return this->Val_ == e; } static const tostr_map_t& to_string_map() { static tostr_map_t to_str_(get_enum_strings<E>()); return to_str_; } static const fromstr_map_t& from_string_map() { static fromstr_map_t from_str_(reverse_map(to_string_map())); return from_str_; } private: E Val_; static fromstr_map_t reverse_map(const tostr_map_t& eToS) { fromstr_map_t sToE; for (auto pr : eToS) { sToE.emplace(pr.second, pr.first); } return sToE; } }; template <typename E> streamable_enum<E> stream_enum(E e) { return streamable_enum<E>(e); } template <typename E> typename streamable_enum<E>::tostr_map_t get_enum_strings() { // \todo throw an appropriate exception or display compile error/warning return {}; } template <typename E> std::ostream& operator<<(std::ostream& os, streamable_enum<E> e) { auto& mp = streamable_enum<E>::to_string_map(); auto res = mp.find(e); if (res != mp.end()) { os << res->second; } else { os.setstate(std::ios_base::failbit); } return os; } template <typename E> std::istream& operator>>(std::istream& is, streamable_enum<E>& e) { std::string str; is >> str; if (str.empty()) { is.setstate(std::ios_base::failbit); } auto& mp = streamable_enum<E>::from_string_map(); auto res = mp.find(str); if (res != mp.end()) { e = res->second; } else { is.setstate(std::ios_base::failbit); } return is; } #endif
Pemakaian:
#include "streamable_enum.hpp" using std::cout; using std::cin; using std::endl; enum Animal { CAT, DOG, TIGER, RABBIT }; template <> streamable_enum<Animal>::tostr_map_t get_enum_strings<Animal>() { return { { CAT, "Cat"}, { DOG, "Dog" }, { TIGER, "Tiger" }, { RABBIT, "Rabbit" } }; } int main(int argc, char* argv []) { cout << "What animal do you want to buy? Our offering:" << endl; for (auto pr : streamable_enum<Animal>::to_string_map()) { // Use from_string_map() and pr.first instead cout << " " << pr.second << endl; // to have them sorted in alphabetical order } streamable_enum<Animal> anim; cin >> anim; if (!cin) { cout << "We don't have such animal here." << endl; } else if (anim == Animal::TIGER) { cout << stream_enum(Animal::TIGER) << " was a joke..." << endl; } else { cout << "Here you are!" << endl; } return 0; }
sumber
Berikut adalah solusi menggunakan makro dengan fitur-fitur berikut:
hanya menulis setiap nilai enum satu kali, jadi tidak ada daftar ganda yang harus dipertahankan
jangan menyimpan nilai enum dalam file terpisah yang #included, jadi saya bisa menuliskannya di mana pun saya mau
jangan mengganti enum itu sendiri, saya masih ingin memiliki jenis enum yang ditentukan, tetapi selain itu saya ingin dapat memetakan setiap nama enum ke string yang sesuai (untuk tidak memengaruhi kode lama)
pencarian harus cepat, jadi sebaiknya tidak ada switch-case, untuk enum besar itu
https://stackoverflow.com/a/20134475/1812866
sumber
Saya pikir solusi seperti Boost.Fusion satu untuk mengadaptasi struct dan class akan bagus, mereka bahkan memilikinya di beberapa titik, untuk menggunakan enum sebagai urutan fusi.
Jadi saya membuat hanya beberapa makro kecil untuk menghasilkan kode untuk mencetak enum. Ini tidak sempurna dan tidak ada yang bisa dilihat dengan kode boilerplate yang dihasilkan Boost.Fusion, tetapi dapat digunakan seperti makro Boost Fusion. Saya ingin benar-benar menghasilkan jenis yang dibutuhkan oleh Boost.Fusion untuk berintegrasi dalam infrastruktur ini yang memungkinkan untuk mencetak nama anggota struct, tetapi ini akan terjadi nanti, untuk saat ini ini hanya makro:
#ifndef SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP #define SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP #include <swissarmyknife/detail/config.hpp> #include <string> #include <ostream> #include <boost/preprocessor/cat.hpp> #include <boost/preprocessor/stringize.hpp> #include <boost/preprocessor/seq/for_each.hpp> #define SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C( \ R, unused, ENUMERATION_ENTRY) \ case ENUMERATION_ENTRY: \ return BOOST_PP_STRINGIZE(ENUMERATION_ENTRY); \ break; /** * \brief Adapts ENUM to reflectable types. * * \param ENUM_TYPE To be adapted * \param ENUMERATION_SEQ Sequence of enum states */ #define SWISSARMYKNIFE_ADAPT_ENUM(ENUM_TYPE, ENUMERATION_SEQ) \ inline std::string to_string(const ENUM_TYPE& enum_value) { \ switch (enum_value) { \ BOOST_PP_SEQ_FOR_EACH( \ SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C, \ unused, ENUMERATION_SEQ) \ default: \ return BOOST_PP_STRINGIZE(ENUM_TYPE); \ } \ } \ \ inline std::ostream& operator<<(std::ostream& os, const ENUM_TYPE& value) { \ os << to_string(value); \ return os; \ } #endif
Jawaban lama di bawah ini sangat buruk, tolong jangan gunakan itu. :)
Jawaban lama:
Saya telah mencari cara untuk memecahkan masalah ini tanpa terlalu banyak mengubah sintaks deklarasi enums. Saya sampai pada solusi yang menggunakan preprocessor untuk mengambil string dari deklarasi enum bersenar.
Saya dapat mendefinisikan enum non-sparse seperti ini:
SMART_ENUM(State, enum State { RUNNING, SLEEPING, FAULT, UNKNOWN })
Dan saya dapat berinteraksi dengan mereka dengan berbagai cara:
// With a stringstream std::stringstream ss; ss << State::FAULT; std::string myEnumStr = ss.str(); //Directly to stdout std::cout << State::FAULT << std::endl; //to a string std::string myStr = State::to_string(State::FAULT); //from a string State::State myEnumVal = State::from_string(State::FAULT);
Berdasarkan definisi berikut:
#define SMART_ENUM(enumTypeArg, ...) \ namespace enumTypeArg { \ __VA_ARGS__; \ std::ostream& operator<<(std::ostream& os, const enumTypeArg& val) { \ os << swissarmyknife::enums::to_string(#__VA_ARGS__, val); \ return os; \ } \ \ std::string to_string(const enumTypeArg& val) { \ return swissarmyknife::enums::to_string(#__VA_ARGS__, val); \ } \ \ enumTypeArg from_string(const std::string &str) { \ return swissarmyknife::enums::from_string<enumTypeArg>(#__VA_ARGS__, str); \ } \ } \ namespace swissarmyknife { namespace enums { static inline std::string to_string(const std::string completeEnumDeclaration, size_t enumVal) throw (std::runtime_error) { size_t begin = completeEnumDeclaration.find_first_of('{'); size_t end = completeEnumDeclaration.find_last_of('}'); const std::string identifiers = completeEnumDeclaration.substr(begin + 1, end ); size_t count = 0; size_t found = 0; do { found = identifiers.find_first_of(",}", found+1); if (enumVal == count) { std::string identifiersSubset = identifiers.substr(0, found); size_t beginId = identifiersSubset.find_last_of("{,"); identifiersSubset = identifiersSubset.substr(beginId+1); boost::algorithm::trim(identifiersSubset); return identifiersSubset; } ++count; } while (found != std::string::npos); throw std::runtime_error("The enum declaration provided doesn't contains this state."); } template <typename EnumType> static inline EnumType from_string(const std::string completeEnumDeclaration, const std::string &enumStr) throw (std::runtime_error) { size_t begin = completeEnumDeclaration.find_first_of('{'); size_t end = completeEnumDeclaration.find_last_of('}'); const std::string identifiers = completeEnumDeclaration.substr(begin + 1, end ); size_t count = 0; size_t found = 0; do { found = identifiers.find_first_of(",}", found+1); std::string identifiersSubset = identifiers.substr(0, found); size_t beginId = identifiersSubset.find_last_of("{,"); identifiersSubset = identifiersSubset.substr(beginId+1); boost::algorithm::trim(identifiersSubset); if (identifiersSubset == enumStr) { return static_cast<EnumType>(count); } ++count; } while (found != std::string::npos); throw std::runtime_error("No valid enum value for the provided string"); } }}
Ketika saya akan membutuhkan dukungan untuk jarang enum dan ketika saya akan memiliki lebih banyak waktu saya akan meningkatkan to_string dan from_string implementasi dengan meningkatkan :: XPRESSIVE, tapi akan ini biaya dalam waktu kompilasi karena template penting dilakukan dan executable yang dihasilkan yaitu kemungkinan besar akan sangat besar. Tetapi ini memiliki keuntungan karena akan lebih mudah dibaca dan dipelihara daripada kode manipulasi string manual yang jelek ini.: D
Jika tidak, saya selalu menggunakan boost :: bimap untuk melakukan pemetaan seperti itu antara nilai enum dan string, tetapi harus dijaga secara manual.
sumber
Karena saya memilih untuk tidak menggunakan makro untuk semua alasan yang biasa, saya menggunakan solusi makro yang lebih terbatas yang memiliki keuntungan menjaga makro deklarasi enum tetap bebas. Kekurangannya termasuk harus menyalin dan menempel definisi makro untuk setiap enum, dan harus menambahkan pemanggilan makro secara eksplisit saat menambahkan nilai ke enum.
std::ostream& operator<<(std::ostream& os, provenance_wrapper::CaptureState cs) { #define HANDLE(x) case x: os << #x; break; switch (cs) { HANDLE(CaptureState::UNUSED) HANDLE(CaptureState::ACTIVE) HANDLE(CaptureState::CLOSED) } return os; #undef HANDLE }
sumber