Cara mudah memetakan enum c ++ ke string

119

Saya memiliki banyak jenis enum di beberapa file header perpustakaan yang saya gunakan, dan saya ingin memiliki cara untuk mengonversi nilai enum menjadi string pengguna - dan sebaliknya.

RTTI tidak akan melakukannya untuk saya, karena 'string pengguna' harus sedikit lebih mudah dibaca daripada enumerasi.

Solusi brute force akan menjadi sekumpulan fungsi seperti ini, tapi saya merasa itu agak terlalu mirip C.

enum MyEnum {VAL1, VAL2,VAL3};

String getStringFromEnum(MyEnum e)
{
  switch e
  {
  case VAL1: return "Value 1";
  case VAL2: return "Value 2";
  case VAL1: return "Value 3";
  default: throw Exception("Bad MyEnum");
  }
}

Saya memiliki firasat bahwa ada solusi elegan menggunakan templat, tetapi saya belum bisa sepenuhnya memahaminya.

PEMBARUAN: Terima kasih atas saran - Saya seharusnya menjelaskan bahwa enum didefinisikan di tajuk perpustakaan pihak ketiga, jadi saya tidak ingin mengubah definisi mereka.

Perasaan saya sekarang adalah menghindari template dan melakukan sesuatu seperti ini:

char * MyGetValue(int v, char *tmp); // implementation is trivial

#define ENUM_MAP(type, strings) char * getStringValue(const type &T) \
 { \
 return MyGetValue((int)T, strings); \
 }

; enum eee {AA,BB,CC}; - exists in library header file 
; enum fff {DD,GG,HH}; 

ENUM_MAP(eee,"AA|BB|CC")
ENUM_MAP(fff,"DD|GG|HH")

// To use...

    eee e;
    fff f;
    std::cout<< getStringValue(e);
    std::cout<< getStringValue(f);
Roddy
sumber
1
Kemungkinan duplikat dari Apakah ada cara sederhana untuk mengubah C ++ enum menjadi string?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Jawaban:

60

Jika Anda ingin nama enum itu sendiri sebagai string, lihat posting ini . Jika tidak, a std::map<MyEnum, char const*>akan bekerja dengan baik. (Tidak ada gunanya menyalin literal string Anda ke std :: string di peta)

Untuk tambahan gula sintaksis, berikut cara menulis kelas map_init. Tujuannya adalah untuk memungkinkan

std::map<MyEnum, const char*> MyMap;
map_init(MyMap)
    (eValue1, "A")
    (eValue2, "B")
    (eValue3, "C")
;

Fungsi template <typename T> map_init(T&)mengembalikan a map_init_helper<T>. map_init_helper<T>menyimpan T &, dan mendefinisikan hal sepele map_init_helper& operator()(typename T::key_type const&, typename T::value_type const&). (Kembali *thisdari operator()memungkinkan perangkaian operator(), seperti operator<<pada std::ostreams)

template<typename T> struct map_init_helper
{
    T& data;
    map_init_helper(T& d) : data(d) {}
    map_init_helper& operator() (typename T::key_type const& key, typename T::mapped_type const& value)
    {
        data[key] = value;
        return *this;
    }
};

template<typename T> map_init_helper<T> map_init(T& item)
{
    return map_init_helper<T>(item);
}

Karena fungsi dan kelas helper memiliki template, Anda dapat menggunakannya untuk peta apa pun, atau struktur seperti peta. Yaitu dapat juga menambahkan entri kestd::unordered_map

Jika Anda tidak suka menulis pembantu ini, boost :: assign menawarkan fungsionalitas yang sama di luar kotak.

MSalters
sumber
Anda benar untuk merujuk pada pertanyaan lain. Orang-orang harus melihat di "pertanyaan terkait" sebelum memposting ...
xtofl
2
@xtofl: "Pertanyaan terkait" yang ditampilkan di sini sama sekali berbeda dengan pertanyaan terkait yang tercantum saat saya memposting pertanyaan!
Roddy
@ MSalters, std :: map adalah cara yang berguna untuk menangani implementasi, tetapi saya sedang mencari beberapa cara untuk mengurangi kode boilerplate yang mungkin diperlukan.
Roddy
@MSalters, alangkah baiknya dapat menerima beberapa argumen untuk operator []. tapi sayangnya, seseorang tidak bisa melakukan itu. x [a, b] bernilai x [b]. ekspresi (a, b) menggunakan operator koma. sehingga setara dengan ["A"] ["B"] ["C"] di kode Anda. Anda dapat mengubahnya menjadi [eValue1] ["A"] [eValu ..
Johannes Schaub - litb
operator pemanggil fungsi juga akan menjadi kandidat yang baik: map_init (MyMap) (eValue1, "A") (eValue2, "B") .... maka itu setara dengan boost :: assign: insert (MyMap) (eValue1, "A") (eValue2, "B") ... ( boost.org/doc/libs/1_35_0/libs/assign/doc/index.html )
Johannes Schaub - litb
31

Solusi MSalters adalah solusi yang bagus tetapi pada dasarnya diimplementasikan kembali boost::assign::map_list_of. Jika Anda memiliki dorongan, Anda dapat menggunakannya secara langsung:

#include <boost/assign/list_of.hpp>
#include <boost/unordered_map.hpp>
#include <iostream>

using boost::assign::map_list_of;

enum eee { AA,BB,CC };

const boost::unordered_map<eee,const char*> eeeToString = map_list_of
    (AA, "AA")
    (BB, "BB")
    (CC, "CC");

int main()
{
    std::cout << " enum AA = " << eeeToString.at(AA) << std::endl;
    return 0;
}
Alastair
sumber
Bagaimana Anda menggunakan ini di mana eeeToString adalah anggota data kelas? Saya mendapatkan "Kesalahan: inisialisasi anggota data tidak diizinkan"
Pengguna
@ Pengguna: Anggota data kelas diinisialisasi dalam konstruktor, biasanya dalam daftar penginisialisasi.
MSalters
Apakah ada cara untuk membuat ini berfungsi untuk semua enum. Saya memiliki beberapa deklarasi enum dan tidak ingin peta hanya berfungsi untuk tipe eeedalam kasus Anda.
Justin Liang
Saya mencoba menggunakan template tapi kemudian mendapat and error: error: template declaration of 'const boost::unordered::unordered_map<T, const char*> enumToString'.
Justin Liang
4
Sebenarnya jawaban ini sebagian besar sudah usang dengan C ++ 11.
Alastair
19

Buat satu formulir secara otomatis dari yang lain.

Sumber:

enum {
  VALUE1, /* value 1 */
  VALUE2, /* value 2 */
};

Dihasilkan:

const char* enum2str[] = {
  "value 1", /* VALUE1 */
  "value 2", /* VALUE2 */
};

Jika nilai enum besar maka formulir yang dihasilkan dapat menggunakan unordered_map <> atau template seperti yang disarankan oleh Constantin.

Sumber:

enum State{
  state0 = 0, /* state 0 */
  state1 = 1, /* state 1 */
  state2 = 2, /* state 2 */
  state3 = 4, /* state 3 */

  state16 = 0x10000, /* state 16 */
};

Dihasilkan:

template <State n> struct enum2str { static const char * const value; };
template <State n> const char * const enum2str<n>::value = "error";

template <> struct enum2str<state0> { static const char * const value; };
const char * const enum2str<state0>::value = "state 0";

Contoh:

#include <iostream>

int main()
{
  std::cout << enum2str<state16>::value << std::endl;
  return 0;
}
jfs
sumber
Meskipun tercepat, ini tidak semudah @MSalters.
kenny
2
Jika Anda memiliki sedikit perl / python untuk membaca daftar string dari file teks dan menghasilkan file .h dengan karakter statis pada waktu kompilasi. = "Menulis program untuk menulis program"
Martin Beckett
@mgb: perl / python bukan satu-satunya opsi yang dapat dilakukan oleh hampir semua mesin templat dalam bahasa apa pun (dalam hal ini salah satunya menghasilkan kedua formulir dari templat).
jfs
@tokopedia Ya, poin pentingnya adalah membuat tabel data statis pada waktu kompilasi secara otomatis. Saya mungkin lebih suka hanya menghasilkan array statis bodoh.
Martin Beckett
Akankah ini berfungsi jika State tidak diketahui pada waktu kompilasi? Saya cukup yakin itu tidak akan terjadi - secara teori kompilator harus membuat contoh template enum2str dengan semua kemungkinan nilai enum, yang saya cukup yakin gcc (setidaknya) tidak akan melakukannya.
Alastair
11

Saya ingat pernah menjawab ini di tempat lain di StackOverflow. Ulangi di sini. Pada dasarnya ini adalah solusi berdasarkan makro variadic, dan cukup mudah digunakan:

#define AWESOME_MAKE_ENUM(name, ...) enum class name { __VA_ARGS__, __COUNT}; \
inline std::ostream& operator<<(std::ostream& os, name value) { \
std::string enumName = #name; \
std::string str = #__VA_ARGS__; \
int len = str.length(); \
std::vector<std::string> strings; \
std::ostringstream temp; \
for(int i = 0; i < len; i ++) { \
if(isspace(str[i])) continue; \
        else if(str[i] == ',') { \
        strings.push_back(temp.str()); \
        temp.str(std::string());\
        } \
        else temp<< str[i]; \
} \
strings.push_back(temp.str()); \
os << enumName << "::" << strings[static_cast<int>(value)]; \
return os;} 

Untuk menggunakannya dalam kode Anda, cukup lakukan:

AWESOME_MAKE_ENUM(Animal,
    DOG,
    CAT,
    HORSE
);
auto dog = Animal::DOG;
std::cout<<dog;
Debdatta Basu
sumber
1
Ubah saja deklarasi kelas enum menjadi enum untuk bekerja pada pra c ++ 11.
Debdatta Basu
1
Anda benar itu berhasil (otomatis juga hanya c ++ 11). Solusi bagus! Akan sempurna jika Anda juga dapat menetapkan nilai untuk beberapa enum
jamk
Saya rasa saya melihat di boost sesuatu seperti itu
Sergei
10

Saya menyarankan perpaduan penggunaan X-macro adalah solusi terbaik dan fungsi template berikut:

Untuk meminjam marcinkoziukmyopenidcom dan diperpanjang

enum Colours {
#   define X(a) a,
#   include "colours.def"
#   undef X
    ColoursCount
};

char const* const colours_str[] = {
#   define X(a) #a,
#   include "colours.def"
#   undef X
    0
};

template <class T> T str2enum( const char* );
template <class T> const char* enum2str( T );

#define STR2ENUM(TYPE,ARRAY) \
template <> \
TYPE str2enum<TYPE>( const char* str ) \
    { \
    for( int i = 0; i < (sizeof(ARRAY)/sizeof(ARRAY[0])); i++ ) \
        if( !strcmp( ARRAY[i], str ) ) \
            return TYPE(i); \
    return TYPE(0); \
    }

#define ENUM2STR(TYPE,ARRAY) \
template <> \
const char* enum2str<TYPE>( TYPE v ) \
    { \
    return ARRAY[v]; \
    }

#define ENUMANDSTR(TYPE,ARRAY)\
    STR2ENUM(TYPE,ARRAY) \
    ENUM2STR(TYPE,ARRAY)

ENUMANDSTR(Colours,colours_str)

colour.def

X(Red)
X(Green)
X(Blue)
X(Cyan)
X(Yellow)
X(Magenta)
David Allan Finch
sumber
Apakah ada cara untuk membuat definisi array string enum generik? (Saya tidak tahu bagaimana menangani X-Macro di dalam makro dan saya tidak menangani templatnya dengan mudah)
Jonathan
5

Saya menggunakan solusi ini yang saya reproduksi di bawah ini:

#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;
}
Juan Gonzalez Burgos
sumber
1
Ini adalah X Macro dasar, dan saya terkejut bahwa ini adalah jawaban pertama yang menyarankannya! +1
Balapan Lightness di Orbit
4

Jika Anda ingin mendapatkan representasi string dari MyEnum variabel , maka template tidak akan memotongnya. Template dapat dikhususkan pada nilai integral yang diketahui pada waktu kompilasi.

Namun, jika itu yang Anda inginkan, cobalah:

#include <iostream>

enum MyEnum { VAL1, VAL2 };

template<MyEnum n> struct StrMyEnum {
    static char const* name() { return "Unknown"; }
};

#define STRENUM(val, str) \
  template<> struct StrMyEnum<val> { \
    static char const* name() { return str; }};

STRENUM(VAL1, "Value 1");
STRENUM(VAL2, "Value 2");

int main() {
  std::cout << StrMyEnum<VAL2>::name();
}

Ini bertele-tele, tetapi akan menangkap kesalahan seperti yang Anda buat yang dimaksud - Anda case VAL1digandakan.

Constantin
sumber
Sebenarnya nama metode () tidak diperlukan. Lihat jawaban saya.
jfs
3

Saya telah menghabiskan lebih banyak waktu untuk meneliti topik ini yang ingin saya akui. Untungnya ada solusi sumber terbuka yang bagus di alam liar.

Ini adalah dua pendekatan hebat, meskipun belum cukup dikenal (belum),

bijaksana_enum

  • Library enum smart mandiri untuk C ++ 11/14/17. Ini mendukung semua fungsionalitas standar yang Anda harapkan dari kelas enum cerdas di C ++.
  • Batasan: membutuhkan setidaknya C ++ 11.

Enum yang Lebih Baik

  • Library enum waktu kompilasi reflektif dengan sintaks yang bersih, dalam satu file header, dan tanpa dependensi.
  • Batasan: berdasarkan makro, tidak dapat digunakan di dalam kelas.
jose.angel.jimenez
sumber
2

Saya akan tergoda untuk memiliki peta m - dan menyematkannya ke enum.

setup dengan m [MyEnum.VAL1] = "Nilai 1";

dan semuanya selesai.

Richard Harrison
sumber
2

Saya memerlukan fungsi ini beberapa kali untuk men-debug / menganalisis kode dari orang lain. Untuk ini, saya telah menulis skrip Perl yang menghasilkan kelas dengan beberapa toStringmetode kelebihan beban . Setiap toStringmetode mengambil Enumsebuah argumen dan mengembalikan const char*.

Tentu saja, skrip tidak mengurai C ++ untuk enum itu sendiri, tetapi menggunakan ctag untuk membuat tabel simbol.

Skrip Perl ada di sini: http://heinitz-it.de/download/enum2string/enum2string.pl.html

Valentin Heinitz
sumber
2

Jawaban Anda menginspirasi saya untuk menulis beberapa makro sendiri. Persyaratan saya adalah sebagai berikut:

  1. hanya menulis setiap nilai enum satu kali, jadi tidak ada daftar ganda yang harus dipertahankan

  2. jangan menyimpan nilai enum di file terpisah yang #included, jadi saya bisa menuliskannya di mana pun saya mau

  3. jangan mengganti enum itu sendiri, saya masih ingin memiliki tipe enum yang ditentukan, tetapi selain itu saya ingin dapat memetakan setiap nama enum ke string yang sesuai (untuk tidak memengaruhi kode lama)

  4. pencarian harus cepat, jadi sebaiknya tidak ada switch-case, untuk enum besar itu

Kode ini membuat enum klasik dengan beberapa nilai. Selain itu, ia membuat sebagai std :: map yang memetakan setiap nilai enum ke namanya (yaitu peta [E_SUNDAY] = "E_SUNDAY", dll.)

Oke, ini kodenya sekarang:

EnumUtilsImpl.h :

map<int, string> & operator , (map<int, string> & dest, 
                               const pair<int, string> & keyValue) {
    dest[keyValue.first] = keyValue.second; 
    return dest;
}

#define ADD_TO_MAP(name, value) pair<int, string>(name, #name)

EnumUtils.h // ini adalah file yang ingin Anda sertakan kapan pun Anda perlu melakukan hal ini, Anda akan menggunakan makro darinya:

#include "EnumUtilsImpl.h"
#define ADD_TO_ENUM(name, value) \
    name value

#define MAKE_ENUM_MAP_GLOBAL(values, mapName) \
    int __makeMap##mapName() {mapName, values(ADD_TO_MAP); return 0;}  \
    int __makeMapTmp##mapName = __makeMap##mapName();

#define MAKE_ENUM_MAP(values, mapName) \
    mapName, values(ADD_TO_MAP);

MyProjectCodeFile.h // ini adalah contoh cara menggunakannya untuk membuat enum kustom:

#include "EnumUtils.h*

#define MyEnumValues(ADD) \
    ADD(val1, ), \
    ADD(val2, ), \
    ADD(val3, = 100), \
    ADD(val4, )

enum MyEnum {
    MyEnumValues(ADD_TO_ENUM)
};

map<int, string> MyEnumStrings;
// this is how you initialize it outside any function
MAKE_ENUM_MAP_GLOBAL(MyEnumValues, MyEnumStrings); 

void MyInitializationMethod()
{ 
    // or you can initialize it inside one of your functions/methods
    MAKE_ENUM_MAP(MyEnumValues, MyEnumStrings); 
}

Bersulang.

muqker
sumber
2

Berikut ini adalah upaya untuk mendapatkan << dan >> operator streaming pada enum secara otomatis dengan hanya perintah makro satu baris ...

Definisi:

#include <string>
#include <iostream>
#include <stdexcept>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <vector>

#define MAKE_STRING(str, ...) #str, MAKE_STRING1_(__VA_ARGS__)
#define MAKE_STRING1_(str, ...) #str, MAKE_STRING2_(__VA_ARGS__)
#define MAKE_STRING2_(str, ...) #str, MAKE_STRING3_(__VA_ARGS__)
#define MAKE_STRING3_(str, ...) #str, MAKE_STRING4_(__VA_ARGS__)
#define MAKE_STRING4_(str, ...) #str, MAKE_STRING5_(__VA_ARGS__)
#define MAKE_STRING5_(str, ...) #str, MAKE_STRING6_(__VA_ARGS__)
#define MAKE_STRING6_(str, ...) #str, MAKE_STRING7_(__VA_ARGS__)
#define MAKE_STRING7_(str, ...) #str, MAKE_STRING8_(__VA_ARGS__)
#define MAKE_STRING8_(str, ...) #str, MAKE_STRING9_(__VA_ARGS__)
#define MAKE_STRING9_(str, ...) #str, MAKE_STRING10_(__VA_ARGS__)
#define MAKE_STRING10_(str) #str

#define MAKE_ENUM(name, ...) MAKE_ENUM_(, name, __VA_ARGS__)
#define MAKE_CLASS_ENUM(name, ...) MAKE_ENUM_(friend, name, __VA_ARGS__)

#define MAKE_ENUM_(attribute, name, ...) name { __VA_ARGS__ }; \
    attribute std::istream& operator>>(std::istream& is, name& e) { \
        const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
        std::string str; \
        std::istream& r = is >> str; \
        const size_t len = sizeof(name##Str)/sizeof(name##Str[0]); \
        const std::vector<std::string> enumStr(name##Str, name##Str + len); \
        const std::vector<std::string>::const_iterator it = std::find(enumStr.begin(), enumStr.end(), str); \
        if (it != enumStr.end())\
            e = name(it - enumStr.begin()); \
        else \
            throw std::runtime_error("Value \"" + str + "\" is not part of enum "#name); \
        return r; \
    }; \
    attribute std::ostream& operator<<(std::ostream& os, const name& e) { \
        const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
        return (os << name##Str[e]); \
    }

Pemakaian:

// Declare global enum
enum MAKE_ENUM(Test3, Item13, Item23, Item33, Itdsdgem43);

class Essai {
public:
    // Declare enum inside class
    enum MAKE_CLASS_ENUM(Test, Item1, Item2, Item3, Itdsdgem4);

};

int main() {
    std::cout << Essai::Item1 << std::endl;

    Essai::Test ddd = Essai::Item1;
    std::cout << ddd << std::endl;

    std::istringstream strm("Item2");
    strm >> ddd;

    std::cout << (int) ddd << std::endl;
    std::cout << ddd << std::endl;
}

Tidak yakin tentang batasan skema ini ... komentar dipersilakan!

OlivierB
sumber
1

di header:

enum EFooOptions
 {
FooOptionsA = 0, EFooOptionsMin = 0,
FooOptionsB,
FooOptionsC,
FooOptionsD 
EFooOptionsMax
};
extern const wchar* FOO_OPTIONS[EFooOptionsMax];

di file .cpp:

const wchar* FOO_OPTIONS[] = {
    L"One",
    L"Two",
    L"Three",
    L"Four"
};

Peringatan: Jangan menangani indeks array yang buruk. :) Tetapi Anda dapat dengan mudah menambahkan fungsi untuk memverifikasi enum sebelum mendapatkan string dari array.

moogs
sumber
Memang solusi yang sangat non-KERING-TITIK.
xtofl
sekarang setelah Anda menyebutkan KERING. file .h dan .cpp yang dihasilkan secara otomatis dari beberapa file masukan lainnya. Saya ingin melihat solusi yang lebih baik (yang tidak menggunakan kerumitan yang tidak perlu)
moogs
1

Saya hanya ingin menunjukkan kemungkinan solusi elegan ini menggunakan makro. Ini tidak menyelesaikan masalah, tetapi menurut saya ini adalah cara yang baik untuk meninjau kembali masalah tersebut.

#define MY_LIST(X) X(value1), X(value2), X(value3)

enum eMyEnum
    {
    MY_LIST(PLAIN)
    };

const char *szMyEnum[] =
    {
    MY_LIST(STRINGY)
    };


int main(int argc, char *argv[])
{

std::cout << szMyEnum[value1] << value1 <<" " <<  szMyEnum[value2] << value2 << std::endl;

return 0;
}

---- EDIT ----

Setelah beberapa penelitian internet dan beberapa percobaan sendiri, saya sampai pada solusi berikut:

//this is the enum definition
#define COLOR_LIST(X) \
  X( RED    ,=21)      \
  X( GREEN  )      \
  X( BLUE   )      \
  X( PURPLE , =242)      \
  X( ORANGE )      \
  X( YELLOW )

//these are the macros
#define enumfunc(enums,value) enums,
#define enumfunc2(enums,value) enums value,
#define ENUM2SWITCHCASE(enums) case(enums): return #enums;

#define AUTOENUM(enumname,listname) enum enumname{listname(enumfunc2)};
#define ENUM2STRTABLE(funname,listname) char* funname(int val) {switch(val) {listname(ENUM2SWITCHCASE) default: return "undef";}}
#define ENUM2STRUCTINFO(spacename,listname) namespace spacename { int values[] = {listname(enumfunc)};int N = sizeof(values)/sizeof(int);ENUM2STRTABLE(enum2str,listname)};

//here the enum and the string enum map table are generated
AUTOENUM(testenum,COLOR_LIST)
ENUM2STRTABLE(testfunenum,COLOR_LIST)
ENUM2STRUCTINFO(colorinfo,COLOR_LIST)//colorinfo structur {int values[]; int N; char * enum2str(int);}

//debug macros
#define str(a) #a
#define xstr(a) str(a)


int main( int argc, char** argv )
{
testenum x = YELLOW;
std::cout << testfunenum(GREEN) << "   " << testfunenum(PURPLE) << PURPLE << "  " << testfunenum(x);

for (int i=0;i< colorinfo::N;i++)
std::cout << std::endl << colorinfo::values[i] <<  "  "<< colorinfo::enum2str(colorinfo::values[i]);

  return EXIT_SUCCESS;
}

Saya hanya ingin mempostingnya mungkin seseorang dapat menemukan solusi ini berguna. Tidak perlu kelas template, tidak perlu c ++ 11 dan tidak perlu boost sehingga ini juga bisa digunakan untuk C.

---- EDIT2 ----

tabel informasi dapat menghasilkan beberapa masalah jika menggunakan lebih dari 2 enum (masalah penyusun). Solusi berikut berhasil:

#define ENUM2STRUCTINFO(spacename,listname) namespace spacename { int spacename##_##values[] = {listname(enumfunc)};int spacename##_##N = sizeof(spacename##_##values)/sizeof(int);ENUM2STRTABLE(spacename##_##enum2str,listname)};
jamk
sumber
1
typedef enum {
    ERR_CODE_OK = 0,
    ERR_CODE_SNAP,

    ERR_CODE_NUM
} ERR_CODE;

const char* g_err_msg[ERR_CODE_NUM] = {
    /* ERR_CODE_OK   */ "OK",
    /* ERR_CODE_SNAP */ "Oh, snap!",
};

Di atas adalah solusi sederhana saya. Salah satu keuntungannya adalah 'NUM' yang mengontrol ukuran array pesan, juga mencegah akses di luar batas (jika Anda menggunakannya dengan bijak).

Anda juga dapat menentukan fungsi untuk mendapatkan string:

const char* get_err_msg(ERR_CODE code) {
    return g_err_msg[code];
}

Selanjutnya solusi saya, saya kemudian menemukan yang berikut ini cukup menarik. Ini biasanya memecahkan masalah sinkronisasi di atas.

Slide di sini: http://www.slideshare.net/arunksaha/touchless-enum-tostring-28684724

Kode di sini: https://github.com/arunksaha/enum_to_string

Madwyn
sumber
1

Saya tahu saya terlambat ke pesta, tetapi untuk semua orang yang datang mengunjungi halaman ini, Anda dapat mencoba ini, ini lebih mudah daripada semua yang ada di sana dan lebih masuk akal:

namespace texs {
    typedef std::string Type;
    Type apple = "apple";
    Type wood = "wood";
}
uIM7AI9S
sumber
Apakah Anda menyarankan menggunakan string dan tidak menggunakan enum sama sekali? Itu tidak benar-benar menyelesaikan masalah.
Roddy
0

Saya baru-baru ini mengalami masalah yang sama dengan pustaka vendor (Fincad). Untungnya, vendor menyediakan doucumentation xml untuk semua enum. Saya akhirnya menghasilkan peta untuk setiap jenis enum dan menyediakan fungsi pencarian untuk setiap enum. Teknik ini juga memungkinkan Anda mencegat pencarian di luar rentang enum.

Saya yakin swig dapat melakukan hal serupa untuk Anda, tetapi saya dengan senang hati memberikan utilitas pembuatan kode yang ditulis dalam ruby.

Berikut ini contoh kodenya:

std::map<std::string, switches::FCSW2::type> init_FCSW2_map() {
        std::map<std::string, switches::FCSW2::type> ans;
        ans["Act365Fixed"] = FCSW2::Act365Fixed;
        ans["actual/365 (fixed)"] = FCSW2::Act365Fixed;
        ans["Act360"] = FCSW2::Act360;
        ans["actual/360"] = FCSW2::Act360;
        ans["Act365Act"] = FCSW2::Act365Act;
        ans["actual/365 (actual)"] = FCSW2::Act365Act;
        ans["ISDA30360"] = FCSW2::ISDA30360;
        ans["30/360 (ISDA)"] = FCSW2::ISDA30360;
        ans["ISMA30E360"] = FCSW2::ISMA30E360;
        ans["30E/360 (30/360 ISMA)"] = FCSW2::ISMA30E360;
        return ans;
}
switches::FCSW2::type FCSW2_lookup(const char* fincad_switch) {
        static std::map<std::string, switches::FCSW2::type> switch_map = init_FCSW2_map();
        std::map<std::string, switches::FCSW2::type>::iterator it = switch_map.find(fincad_switch);
        if(it != switch_map.end()) {
                return it->second;
        } else {
                throw FCSwitchLookupError("Bad Match: FCSW2");
        }
}

Sepertinya Anda ingin pergi ke arah lain (enum ke string, bukan string ke enum), tapi ini harus mudah untuk dibalik.

-Sedikit


sumber
1
a) Apakah ada orang lain yang menganggap ini sama sekali tidak terbaca? Beberapa typedef dan penggunaan deklarasi akan sangat meningkatkan keterbacaan. b) deklarasi statis lokal tidak aman untuk thread. c) gunakan string const & alih-alih char *, d) bagaimana dengan menyertakan nilai yang tidak dapat ditemukan dalam pengecualian yang dilempar?
Alastair
0

Lihat apakah sintaks berikut cocok untuk Anda:

// WeekEnd enumeration
enum WeekEnd
{
    Sunday = 1,
    Saturday = 7
};

// String support for WeekEnd
Begin_Enum_String( WeekEnd )
{
    Enum_String( Sunday );
    Enum_String( Saturday );
}
End_Enum_String;

// Convert from WeekEnd to string
const std::string &str = EnumString<WeekEnd>::From( Saturday );
// str should now be "Saturday"

// Convert from string to WeekEnd
WeekEnd w;
EnumString<WeekEnd>::To( w, "Sunday" );
// w should now be Sunday

Jika ya, Anda mungkin ingin membaca artikel ini:
http://www.gamedev.net/reference/snippets/features/cppstringizing/


sumber
0

kekacauan lama yang tepat ini adalah usaha saya berdasarkan potongan-potongan dari SO. For_each harus diperluas untuk mendukung lebih dari 20 nilai enum. Mengujinya di visual studio 2019, clang dan gcc. c ++ 11

#define _enum_expand(arg) arg
#define _enum_select_for_each(_,_0, _1, _2,_3,_4, _5, _6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,N, ...) N
#define _enum_for_each_0(_call, arg0,arg1,...)
#define _enum_for_each_1(_call, arg0,arg1) _call(arg0,arg1)
#define _enum_for_each_2(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_1(_call,arg0, __VA_ARGS__))
#define _enum_for_each_3(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_2(_call,arg0, __VA_ARGS__))
#define _enum_for_each_4(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_3(_call,arg0, __VA_ARGS__))
#define _enum_for_each_5(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_4(_call,arg0, __VA_ARGS__))
#define _enum_for_each_6(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_5(_call,arg0, __VA_ARGS__))
#define _enum_for_each_7(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_6(_call,arg0, __VA_ARGS__))
#define _enum_for_each_8(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_7(_call,arg0, __VA_ARGS__))
#define _enum_for_each_9(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_8(_call,arg0, __VA_ARGS__))
#define _enum_for_each_10(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_9(_call,arg0, __VA_ARGS__))
#define _enum_for_each_11(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_10(_call,arg0, __VA_ARGS__))
#define _enum_for_each_12(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_11(_call,arg0, __VA_ARGS__))
#define _enum_for_each_13(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_12(_call,arg0, __VA_ARGS__))
#define _enum_for_each_14(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_13(_call,arg0, __VA_ARGS__))
#define _enum_for_each_15(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_14(_call,arg0, __VA_ARGS__))
#define _enum_for_each_16(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_15(_call,arg0, __VA_ARGS__))
#define _enum_for_each_17(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_16(_call,arg0, __VA_ARGS__))
#define _enum_for_each_18(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_17(_call,arg0, __VA_ARGS__))
#define _enum_for_each_19(_call, arg0,arg1, ...) _call(arg) _enum_expand(_enum_for_each_18(_call,arg0, __VA_ARGS__))
#define _enum_for_each(arg, ...) \
    _enum_expand(_enum_select_for_each(_, ##__VA_ARGS__, \
    _enum_for_each_19, _enum_for_each_18, _enum_for_each_17, _enum_for_each_16, _enum_for_each_15, \
    _enum_for_each_14, _enum_for_each_13, _enum_for_each_12, _enum_for_each_11, _enum_for_each_10, \
    _enum_for_each_9,  _enum_for_each_8,  _enum_for_each_7,  _enum_for_each_6,  _enum_for_each_5,  \
    _enum_for_each_4,  _enum_for_each_3,  _enum_for_each_2,  _enum_for_each_1,  _enum_for_each_0)(arg, ##__VA_ARGS__))

#define _enum_strip_args_1(arg0) arg0
#define _enum_strip_args_2(arg0, arg1) arg0, arg1
#define _enum_make_args(...) (__VA_ARGS__)

#define _enum_elem_arity1_1(arg) arg,
#define _enum_elem_arity1( ...) _enum_expand(_enum_elem_arity1_1 __VA_ARGS__)
#define _enum_elem_arity2_1(arg0,arg1) arg0 = arg1,
#define _enum_elem_arity2( ...) _enum_expand(_enum_elem_arity2_1 __VA_ARGS__)

#define _enum_elem_select_arity_2(_0, _1, NAME,...) NAME
#define _enum_elem_select_arity_1(...) _enum_expand(_enum_elem_select_arity_2(__VA_ARGS__, _enum_elem_arity2,_enum_elem_arity1,_))
#define _enum_elem_select_arity(enum_type,...) _enum_expand(_enum_elem_select_arity_1 __VA_ARGS__)(__VA_ARGS__)

#define _enum_str_arity1_1(enum_type,arg) { enum_type::arg,#arg },
#define _enum_str_arity1(enum_type,...) _enum_expand(_enum_str_arity1_1 _enum_make_args( enum_type, _enum_expand(_enum_strip_args_1 __VA_ARGS__)))
#define _enum_str_arity2_1(enum_type,arg,value) { enum_type::arg,#arg },
#define _enum_str_arity2(enum_type, ...) _enum_expand(_enum_str_arity2_1 _enum_make_args( enum_type, _enum_expand(_enum_strip_args_2 __VA_ARGS__)))
#define _enum_str_select_arity_2(_0, _1, NAME,...) NAME
#define _enum_str_select_arity_1(...) _enum_expand(_enum_str_select_arity_2(__VA_ARGS__, _enum_str_arity2,_enum_str_arity1,_))
#define _enum_str_select_arity(enum_type,...) _enum_expand(_enum_str_select_arity_1 __VA_ARGS__)(enum_type,__VA_ARGS__)

#define error_code_enum(enum_type,...)  enum class enum_type {              \
    _enum_expand(_enum_for_each(_enum_elem_select_arity,enum_type, ##__VA_ARGS__))};  \
    namespace _ ## enum_type ## _detail { \
        template <typename> struct _ ## enum_type ## _error_code{ \
            static const std::map<enum_type, const char*> enum_type ## _map; \
        }; \
            template <typename T> \
            const std::map<enum_type, const char*> _ ## enum_type ## _error_code<T>::enum_type ## _map = { \
                _enum_expand(_enum_for_each(_enum_str_select_arity,enum_type,  ##__VA_ARGS__)) \
        }; \
    } \
    inline const char* get_error_code_name(const enum_type& value) { \
        return _ ## enum_type ## _detail::_ ## enum_type ## _error_code<enum_type>::enum_type ## _map.find(value)->second; \
    } 

error_code_enum(myenum,
    (one, 1),
    (two)
);

yang menghasilkan kode berikut

enum class myenum { 
    one = 1,
    two,
};
namespace _myenum_detail {
    template <typename>
    struct _myenum_error_code {
        static const std::map<myenum, const char*> myenum_map;
    };
    template <typename T>
    const std::map<myenum, const char*> _myenum_error_code<T>::myenum_map = {
        { myenum::one, "one" }, 
        { myenum::two, "two" },
    };
}
inline const char* get_error_code_name(const myenum& value) { 
    return _myenum_detail::_myenum_error_code<myenum>::myenum_map.find(value)->second; 
}

Sayang sekali rintangan yang harus Anda lewati dengan preprocessor untuk melakukan ini di salah satu bahasa pemrograman yang paling banyak digunakan di dunia ...

rmawatson
sumber
0

Dengan menggunakan penginisialisasi larik yang ditentukan, larik string Anda tidak bergantung pada urutan elemen dalam enum:

enum Values {
    Val1,
    Val2
};

constexpr string_view v_name[] = {
    [Val1] = "Value 1",
    [Val2] = "Value 2"
}
Daniel
sumber