Apakah ada cara sederhana untuk mengubah C ++ enum menjadi string?

123

Misalkan kita memiliki beberapa enum bernama:

enum MyEnum {
      FOO,
      BAR = 0x50
};

Yang saya cari di Google adalah skrip (bahasa apa saja) yang memindai semua header dalam proyek saya dan menghasilkan header dengan satu fungsi per enum.

char* enum_to_string(MyEnum t);

Dan implementasi dengan sesuatu seperti ini:

char* enum_to_string(MyEnum t){
      switch(t){
         case FOO:
            return "FOO";
         case BAR:
            return "BAR";
         default:
            return "INVALID ENUM";
      }
 }

Gotcha benar-benar dengan enum jenis huruf, dan enum gaya C tanpa nama. Apakah ada yang tahu sesuatu tentang ini?

EDIT: Solusinya tidak boleh mengubah sumber saya, kecuali untuk fungsi yang dihasilkan. Enum berada dalam API, jadi menggunakan solusi yang diusulkan sampai sekarang bukanlah pilihan.

Edu Felipe
sumber
Jawaban tentang pabrik berbasis Makro dipindahkan ke stackoverflow.com/questions/147267/… - setelah pertanyaan diperbarui, tidak lagi relevan di sini.
Suma

Jawaban:

48

Anda mungkin ingin melihat GCCXML .

Menjalankan GCCXML pada kode sampel Anda menghasilkan:

<GCC_XML>
  <Namespace id="_1" name="::" members="_3 " mangled="_Z2::"/>
  <Namespace id="_2" name="std" context="_1" members="" mangled="_Z3std"/>
  <Enumeration id="_3" name="MyEnum" context="_1" location="f0:1" file="f0" line="1">
    <EnumValue name="FOO" init="0"/>
    <EnumValue name="BAR" init="80"/>
  </Enumeration>
  <File id="f0" name="my_enum.h"/>
</GCC_XML>

Anda dapat menggunakan bahasa apa pun yang Anda inginkan untuk mengeluarkan tag Enumeration dan EnumValue dan menghasilkan kode yang Anda inginkan.

Avdi
sumber
Luar biasa! Bekerja sebagai pesona dengan skrip python sederhana. Terima kasih.
Edu Felipe
6
+1, GCCXML terlihat sangat bagus! (Meskipun saya hampir -1ed karena saya awalnya salah membaca ini sebagai saran untuk menggunakan sintaks XML verbose di atas untuk menyandikan enum Anda - solusi yang berbau rekayasa berlebihan!)
j_random_hacker
1
ada perubahan yang Anda dapat memposting skrip python?
phillipwei
74

X-macro adalah solusi terbaik. Contoh:

#include <iostream>

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
};

std::ostream& operator<<(std::ostream& os, enum Colours c)
{
    if (c >= ColoursCount || c < 0) return os << "???";
    return os << colours_str[c];
}

int main()
{
    std::cout << Red << Blue << Green << Cyan << Yellow << Magenta << std::endl;
}

colours.def:

X(Red)
X(Green)
X(Blue)
X(Cyan)
X(Yellow)
X(Magenta)

Namun, saya biasanya lebih suka metode berikut, sehingga mungkin untuk sedikit mengubah string.

#define X(a, b) a,
#define X(a, b) b,

X(Red, "red")
X(Green, "green")
// etc.
Marcin Koziuk
sumber
11
bagus, meskipun saya tidak suka file tambahan
Ronny Brendel
2
Pastikan saja proses build Anda tidak menambahkan #pragma (sekali) sebelum setiap file
penyertaan
24
Saya tidak yakin tentang solusi "terbaik"!
Balapan Ringan di Orbit
2
Solusi ini jauh lebih unggul daripada switch case atau array berbasis satu, karena tidak menduplikasi nama, sehingga mudah untuk mengubah enumerasi.
Julien Guertault
2
@ ikku100 Anda tidak benar tentang #define X(a, b) #b. Ini hanya diperlukan jika definisi terlihat seperti ini X(Red, red), daripada definisi yang ditunjukkan dalam jawaban,X(Red, "red")
learnvst
43

@hydroo: Tanpa file tambahan:

#define SOME_ENUM(DO) \
    DO(Foo) \
    DO(Bar) \
    DO(Baz)

#define MAKE_ENUM(VAR) VAR,
enum MetaSyntacticVariable{
    SOME_ENUM(MAKE_ENUM)
};

#define MAKE_STRINGS(VAR) #VAR,
const char* const MetaSyntacticVariableNames[] = {
    SOME_ENUM(MAKE_STRINGS)
};
Jasper Bekkers
sumber
Saya suka solusi ini. Akan lebih jelas jika SOME_UNION dan MAKE_UNION disebut SOME_ENUM dan MAKE_ENUM.
Bruno Martinez
Ini adalah solusi yang bagus. Saya memiliki pengelola sumber daya C ++ paling mudah dipelihara yang pernah saya tangani.
DCurro
Saya harus berterima kasih atas solusi sederhana ini :-) - Saya memang memodifikasinya sedikit, untuk MetaSyntacticVariableNames[]menjadi bagian dari deklarasi kelas, dengan membuat metodestatic const char* getNameByEnum(MetaSyntacticVariable e) { /*code to return the static string*/ }
DeckerDK
Jawaban yang fantastis! Saya menyederhanakannya lebih jauh dengan mengelompokkan MAKE_ENUM dan MAKE_STRINGS ke dalam satu makro, membuat seluruh proses menjadi lebih sederhana. Saya menambahkan jawaban di utas ini dengan kode itu jika ada yang tertarik.
Francois Bertrand
35

Yang cenderung saya lakukan adalah membuat array C dengan nama dalam urutan dan posisi yang sama dengan nilai enum.

misalnya.

enum colours { red, green, blue };
const char *colour_names[] = { "red", "green", "blue" };

maka Anda dapat menggunakan larik di tempat-tempat yang Anda inginkan nilai yang dapat dibaca manusia, mis

colours mycolour = red;
cout << "the colour is" << colour_names[mycolour];

Anda dapat bereksperimen sedikit dengan operator merangkai (lihat # dalam referensi praprosesor Anda) yang akan melakukan apa yang Anda inginkan, dalam beberapa keadaan- misalnya:

#define printword(XX) cout << #XX;
printword(red);

akan mencetak "merah" ke stdout. Sayangnya itu tidak akan berfungsi untuk variabel (karena Anda akan mendapatkan nama variabel yang dicetak)

gbjbaanb.dll
sumber
Peringatan terakhir (tidak akan berfungsi untuk variabel) adalah kelemahan besar, tetapi tetap +1.
chappjc
3
Bekerja hanya jika Anda tidak akan menetapkan nilai numerik khusus ke entri enum.
kyb
11

Saya memiliki makro yang sangat mudah digunakan yang melakukan ini dengan cara yang benar-benar KERING. Ini melibatkan makro variadic dan beberapa keajaiban parsing sederhana. Ini dia:

#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 menggunakan ini di kode Anda, cukup lakukan:

AWESOME_MAKE_ENUM(Animal,
    DOG,
    CAT,
    HORSE
);
Debdatta Basu
sumber
1
Ide bagus menggunakan enum yang diketik dengan kuat (kelas enum). Ini demo: cpp.sh/4ife
chappjc
Apakah ini berfungsi dengan enumerasi / simbol yang ditentukan secara eksternal. Misalnya, OS yang ditentukan atau pustaka yang ditentukan simbol dengan celah di penomoran?
Jason Harrison
Sangat bagus, tetapi tidak dapat dikompilasi jika dimasukkan ke dalam kelas (saya tidak tahu mengapa).
AlwaysLearning
Saya tidak bisa mendapatkan ini untuk dikompilasi di VS2015. Saya mendapatkan peringatan dan kesalahan: peringatan: komentar multi-baris [-Wcomment] #define MAKE_ENUM (nama, ...) nama kelas enum { VA_ARGS , __COUNT} kesalahan: nyasar '#' di program std *: string enumName = #name
Craig. Feied
8

QT mampu menarik itu (berkat kompiler objek meta):

QNetworkReply::NetworkError error;

error = fetchStuff();

if (error != QNetworkReply::NoError) {

    QString errorValue;

    QMetaObject meta = QNetworkReply::staticMetaObject;

    for (int i=0; i < meta.enumeratorCount(); ++i) {

        QMetaEnum m = meta.enumerator(i);

        if (m.name() == QLatin1String("NetworkError")) {

            errorValue = QLatin1String(m.valueToKey(error));

            break;

        }

    }

    QMessageBox box(QMessageBox::Information, "Failed to fetch",

                "Fetching stuff failed with error '%1`").arg(errorValue),

                QMessageBox::Ok);

    box.exec();

    return 1;

}

Dalam Qt setiap kelas yang memiliki makro Q_OBJECT secara otomatis akan memiliki anggota statis "staticMetaObject" dari tipe QMetaObject. Anda kemudian dapat menemukan segala macam hal keren seperti properti, sinyal, slot, dan memang enum.

Sumber

Ronny Brendel
sumber
7

Ini dapat dilakukan di C ++ 11

#include <map>
enum MyEnum { AA, BB, CC, DD };

static std::map< MyEnum, const char * > info = {
   {AA, "This is an apple"},
   {BB, "This is a book"},
   {CC, "This is a coffee"},
   {DD, "This is a door"}
};

void main()
{
    std::cout << info[AA] << endl
              << info[BB] << endl
              << info[CC] << endl
              << info[DD] << endl;
}
sersan
sumber
1
Ini tidak menjawab pertanyaan OP: dia mencari cara untuk secara otomatis menghasilkan fungsi untuk mengembalikan nama anggota enum sebagai string.
Seram
7

Saya baru saja menemukan kembali roda ini hari ini, dan berpikir saya akan membagikannya.

Implementasi ini tidak memerlukan perubahan apa pun pada kode yang mendefinisikan konstanta, yang dapat berupa enumerasi atau #defines atau apa pun yang berpindah ke integer - dalam kasus saya, saya memiliki simbol yang ditentukan dalam istilah simbol lain. Ini juga bekerja dengan baik dengan nilai yang jarang. Ia bahkan memungkinkan banyak nama untuk nilai yang sama, selalu mengembalikan yang pertama. Satu-satunya downside adalah bahwa Anda harus membuat tabel konstanta, yang mungkin sudah ketinggalan zaman karena yang baru ditambahkan misalnya.

struct IdAndName
{
   int          id;
   const char * name;
   bool operator<(const IdAndName &rhs) const { return id < rhs.id; }
};
#define ID_AND_NAME(x) { x, #x }

const char * IdToName(int id, IdAndName *table_begin, IdAndName *table_end)
{
   if ((table_end - table_begin) > 1 && table_begin[0].id > table_begin[1].id)
      std::stable_sort(table_begin, table_end);

   IdAndName searchee = { id, NULL };
   IdAndName *p = std::lower_bound(table_begin, table_end, searchee);
   return (p == table_end || p->id != id) ? NULL : p->name;
}

template<int N>
const char * IdToName(int id, IdAndName (&table)[N])
{
   return IdToName(id, &table[0], &table[N]);
}

Contoh bagaimana Anda akan menggunakannya:

static IdAndName WindowsErrorTable[] =
{
   ID_AND_NAME(INT_MAX),               // flag value to indicate unsorted table
   ID_AND_NAME(NO_ERROR),
   ID_AND_NAME(ERROR_INVALID_FUNCTION),
   ID_AND_NAME(ERROR_FILE_NOT_FOUND),
   ID_AND_NAME(ERROR_PATH_NOT_FOUND),
   ID_AND_NAME(ERROR_TOO_MANY_OPEN_FILES),
   ID_AND_NAME(ERROR_ACCESS_DENIED),
   ID_AND_NAME(ERROR_INVALID_HANDLE),
   ID_AND_NAME(ERROR_ARENA_TRASHED),
   ID_AND_NAME(ERROR_NOT_ENOUGH_MEMORY),
   ID_AND_NAME(ERROR_INVALID_BLOCK),
   ID_AND_NAME(ERROR_BAD_ENVIRONMENT),
   ID_AND_NAME(ERROR_BAD_FORMAT),
   ID_AND_NAME(ERROR_INVALID_ACCESS),
   ID_AND_NAME(ERROR_INVALID_DATA),
   ID_AND_NAME(ERROR_INVALID_DRIVE),
   ID_AND_NAME(ERROR_CURRENT_DIRECTORY),
   ID_AND_NAME(ERROR_NOT_SAME_DEVICE),
   ID_AND_NAME(ERROR_NO_MORE_FILES)
};

const char * error_name = IdToName(GetLastError(), WindowsErrorTable);

The IdToNamefungsi bergantung padastd::lower_bound untuk melakukan pencarian cepat, yang mengharuskan tabel yang akan diurutkan. Jika dua entri pertama dalam tabel rusak, fungsi akan mengurutkannya secara otomatis.

Sunting: Sebuah komentar membuat saya memikirkan cara lain untuk menggunakan prinsip yang sama. Makro menyederhanakan pembuatan switchpernyataan besar .

#define ID_AND_NAME(x) case x: return #x

const char * WindowsErrorToName(int id)
{
    switch(id)
    {
        ID_AND_NAME(ERROR_INVALID_FUNCTION);
        ID_AND_NAME(ERROR_FILE_NOT_FOUND);
        ID_AND_NAME(ERROR_PATH_NOT_FOUND);
        ID_AND_NAME(ERROR_TOO_MANY_OPEN_FILES);
        ID_AND_NAME(ERROR_ACCESS_DENIED);
        ID_AND_NAME(ERROR_INVALID_HANDLE);
        ID_AND_NAME(ERROR_ARENA_TRASHED);
        ID_AND_NAME(ERROR_NOT_ENOUGH_MEMORY);
        ID_AND_NAME(ERROR_INVALID_BLOCK);
        ID_AND_NAME(ERROR_BAD_ENVIRONMENT);
        ID_AND_NAME(ERROR_BAD_FORMAT);
        ID_AND_NAME(ERROR_INVALID_ACCESS);
        ID_AND_NAME(ERROR_INVALID_DATA);
        ID_AND_NAME(ERROR_INVALID_DRIVE);
        ID_AND_NAME(ERROR_CURRENT_DIRECTORY);
        ID_AND_NAME(ERROR_NOT_SAME_DEVICE);
        ID_AND_NAME(ERROR_NO_MORE_FILES);
        default: return NULL;
    }
}
Mark Ransom
sumber
Solusi bagus. Tapi bagi saya, saya lebih suka switch and casekarena sederhana dan mudah dimengerti.
Deqing
6
#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

Ben
sumber
4
Sebenarnya ini cukup tidak berguna, karena metode stringify ada pada waktu kompilasi dan cukup literal. Jika Anda mengatakan memiliki jenis enum yang dimaksud di dalam variabel, mencoba merangkai variabel hanya akan memberi Anda nama variabel, bukan nama jenis enum.
srcspider
5

Menarik untuk melihat banyak cara. inilah yang saya gunakan sejak lama:

di file myenummap.h:

#include <map>
#include <string>
enum test{ one, two, three, five=5, six, seven };
struct mymap : std::map<unsigned int, std::string>
{
  mymap()
  {
    this->operator[]( one ) = "ONE";
    this->operator[]( two ) = "TWO";
    this->operator[]( three ) = "THREE";
    this->operator[]( five ) = "FIVE";
    this->operator[]( six ) = "SIX";
    this->operator[]( seven ) = "SEVEN";
  };
  ~mymap(){};
};

di main.cpp

#include "myenummap.h"

...
mymap nummap;
std::cout<< nummap[ one ] << std::endl;

Ini bukan const, tapi nyaman.

Berikut cara lain yang menggunakan fitur C ++ 11. Ini adalah const, tidak mewarisi container STL dan sedikit lebih rapi:

#include <vector>
#include <string>
#include <algorithm>
#include <iostream>

//These stay together and must be modified together
enum test{ one, two, three, five=5, six, seven };
std::string enum_to_str(test const& e)
{
    typedef std::pair<int,std::string> mapping;
    auto m = [](test const& e,std::string const& s){return mapping(static_cast<int>(e),s);}; 
    std::vector<mapping> const nummap = 
    { 
        m(one,"one"), 
        m(two,"two"), 
        m(three,"three"),
        m(five,"five"),
        m(six,"six"),
        m(seven,"seven"),
    };
    for(auto i  : nummap)
    {
        if(i.first==static_cast<int>(e))
        {
            return i.second;
        }
    }
    return "";
}

int main()
{
//  std::cout<< enum_to_str( 46 ) << std::endl; //compilation will fail
    std::cout<< "Invalid enum to string : [" << enum_to_str( test(46) ) << "]"<<std::endl; //returns an empty string
    std::cout<< "Enumval five to string : ["<< enum_to_str( five ) << "] "<< std::endl; //works
    return 0;
}
Carl
sumber
1
Ini sangat legal. Saya melakukannya sepanjang waktu.
Jonathan Graehl
Solusi bagus. Ini adalah c ++ jadi menggunakan stl map tidak masalah.
Adam Bruss
4
#include <stdarg.h>
#include <algorithm>
#include <string> 
#include <vector>
#include <sstream>
#include <map>

#define SMART_ENUM(EnumName, ...)                                   \
class EnumName                                                      \
{                                                                   \
private:                                                            \
    static std::map<int, std::string> nameMap;                      \
public:                                                             \
    enum {__VA_ARGS__};                                             \
private:                                                            \
    static std::map<int, std::string> initMap()                     \
    {                                                               \
        using namespace std;                                        \
                                                                    \
        int val = 0;                                                \
        string buf_1, buf_2, str = #__VA_ARGS__;                    \
        replace(str.begin(), str.end(), '=', ' ');                  \
        stringstream stream(str);                                   \
        vector<string> strings;                                     \
        while (getline(stream, buf_1, ','))                         \
            strings.push_back(buf_1);                               \
        map<int, string> tmp;                                       \
        for(vector<string>::iterator it = strings.begin();          \
                                               it != strings.end(); \
                                               ++it)                \
        {                                                           \
            buf_1.clear(); buf_2.clear();                           \
            stringstream localStream(*it);                          \
            localStream>> buf_1 >> buf_2;                           \
            if(buf_2.size() > 0)                                    \
                val = atoi(buf_2.c_str());                          \
            tmp[val++] = buf_1;                                     \
        }                                                           \
        return tmp;                                                 \
    }                                                               \
public:                                                             \
    static std::string toString(int aInt)                           \
    {                                                               \
        return nameMap[aInt];                                       \
    }                                                               \
};                                                                  \
std::map<int, std::string>                                          \
EnumName::nameMap = EnumName::initMap();

Pemakaian:

SMART_ENUM(MyEnum, ONE=1, TWO, THREE, TEN=10, ELEVEN)
cout<<MyEnum::toString(MyEnum::TWO);
cout<<MyEnum::toString(10);

sumber
1
Saya suka API Anda, tapi sayangnya, SmartEnum Anda tidak benar-benar membuat enum "type". Tidak boleh MyEnum x = MyEnum::TWO;. Saya telah memposting hasil edit saya tentang kelas Anda untuk mendukung ini.
Mark Lakata
4

Solusi makro Suma bagus. Anda tidak perlu memiliki dua makro yang berbeda. C ++ akan dengan senang hati menyertakan header dua kali. Tinggalkan saja include guardnya.

Jadi Anda akan memiliki foobar.h yang mendefinisikan adil

ENUM(Foo, 1)
ENUM(Bar, 2)

dan Anda akan memasukkannya seperti ini:

#define ENUMFACTORY_ARGUMENT "foobar.h"
#include "enumfactory.h"

enumfactory.h akan melakukan 2 #include ENUMFACTORY_ARGUMENTdetik. Di babak pertama, ENUM diperluas seperti milik Suma DECLARE_ENUM; di babak kedua ENUM bekerja seperti DEFINE_ENUM.

Anda juga dapat menyertakan enumfactory.h beberapa kali, selama Anda meneruskan # define yang berbeda untuk ENUMFACTORY_ARGUMENT

MSalters
sumber
Sepertinya suma memindahkan jawabannya ke sini . Anda mungkin ingin memasukkan tautan dalam jawaban Anda. Saya hanya menemukan komentar itu secara kebetulan dan tanpa jawaban sumas yang satu ini agak tidak berguna
idclev 463035818
3

Perhatikan bahwa fungsi konversi Anda idealnya mengembalikan karakter const *.

Jika Anda mampu untuk menempatkan enum Anda di file header terpisah, Anda mungkin bisa melakukan sesuatu seperti ini dengan makro (oh, ini akan jelek):

#include "enum_def.h"
#include "colour.h"
#include "enum_conv.h"
#include "colour.h"

Di mana enum_def.h memiliki:

#undef ENUM_START
#undef ENUM_ADD
#undef ENUM_END
#define ENUM_START(NAME) enum NAME {
#define ENUM_ADD(NAME, VALUE) NAME = VALUE,
#define ENUM_END };

Dan enum_conv.h memiliki:

#undef ENUM_START
#undef ENUM_ADD
#undef ENUM_END
#define ENUM_START(NAME) const char *##NAME##_to_string(NAME val) { switch (val) {
#define ENUM_ADD(NAME, VALUE) case NAME: return #NAME;
#define ENUM_END default: return "Invalid value"; } }

Dan terakhir, colour.h memiliki:

ENUM_START(colour)
ENUM_ADD(red,   0xff0000)
ENUM_ADD(green, 0x00ff00)
ENUM_ADD(blue,  0x0000ff)
ENUM_END

Dan Anda dapat menggunakan fungsi konversi sebagai:

printf("%s", colour_to_string(colour::red));

Ini jelek, tapi itu satu-satunya cara (di tingkat preprocessor) yang memungkinkan Anda menentukan enum Anda hanya di satu tempat dalam kode Anda. Oleh karena itu, kode Anda tidak rentan terhadap kesalahan karena modifikasi pada enum. Definisi enum Anda dan fungsi konversi akan selalu sinkron. Namun, saya ulangi, ini jelek :)

Ates Goral
sumber
3

Jawaban lain: dalam beberapa konteks, masuk akal untuk mendefinisikan enumerasi Anda dalam format non-kode, seperti file CSV, YAML, atau XML, dan kemudian menghasilkan kode enumerasi C ++ dan kode to-string dari definisi tersebut. Pendekatan ini mungkin atau mungkin tidak praktis dalam aplikasi Anda, tetapi itu adalah sesuatu yang perlu diingat.

Avdi
sumber
3

Ini adalah modifikasi dari jawaban @ user3360260. Ini memiliki beberapa fitur baru berikut

  • MyEnum fromString(const string&) dukung
  • dikompilasi dengan VisualStudio 2012
  • enum adalah tipe POD sebenarnya (bukan hanya deklarasi const), jadi Anda dapat menetapkannya ke variabel.
  • menambahkan fitur C ++ "range" (dalam bentuk vektor) untuk memungkinkan iterasi "foreach" melalui enum

Pemakaian:

SMART_ENUM(MyEnum, ONE=1, TWO, THREE, TEN=10, ELEVEN)
MyEnum foo = MyEnum::TWO;
cout << MyEnum::toString(foo);  // static method
cout << foo.toString();         // member method
cout << MyEnum::toString(MyEnum::TWO);
cout << MyEnum::toString(10);
MyEnum foo = myEnum::fromString("TWO");

// C++11 iteration over all values
for( auto x : MyEnum::allValues() )
{
  cout << x.toString() << endl;
}

Ini kodenya

#define SMART_ENUM(EnumName, ...)                                   \
class EnumName                                                      \
{                                                                   \
public:                                                             \
    EnumName() : value(0) {}                                        \
    EnumName(int x) : value(x) {}                                   \
public:                                                             \
    enum {__VA_ARGS__};                                             \
private:                                                            \
    static void initMap(std::map<int, std::string>& tmp)                     \
    {                                                               \
        using namespace std;                                        \
                                                                    \
        int val = 0;                                                \
        string buf_1, buf_2, str = #__VA_ARGS__;                    \
        replace(str.begin(), str.end(), '=', ' ');                  \
        stringstream stream(str);                                   \
        vector<string> strings;                                     \
        while (getline(stream, buf_1, ','))                         \
            strings.push_back(buf_1);                               \
        for(vector<string>::iterator it = strings.begin();          \
                                                it != strings.end(); \
                                                ++it)                \
        {                                                           \
            buf_1.clear(); buf_2.clear();                           \
            stringstream localStream(*it);                          \
            localStream>> buf_1 >> buf_2;                           \
            if(buf_2.size() > 0)                                    \
                val = atoi(buf_2.c_str());                          \
            tmp[val++] = buf_1;                                     \
        }                                                           \
    }                                                               \
    int value;                                                      \
public:                                                             \
    operator int () const { return value; }                         \
    std::string toString(void) const {                              \
            return toString(value);                                 \
    }                                                               \
    static std::string toString(int aInt)                           \
    {                                                               \
        return nameMap()[aInt];                                     \
    }                                                               \
    static EnumName fromString(const std::string& s)                \
    {                                                               \
        auto it = find_if(nameMap().begin(), nameMap().end(), [s](const std::pair<int,std::string>& p) { \
            return p.second == s;                                   \
        });                                                         \
        if (it == nameMap().end()) {                                \
        /*value not found*/                                         \
            throw EnumName::Exception();                            \
        } else {                                                    \
            return EnumName(it->first);                             \
        }                                                           \
    }                                                               \
    class Exception : public std::exception {};                     \
    static std::map<int,std::string>& nameMap() {                   \
      static std::map<int,std::string> nameMap0;                    \
      if (nameMap0.size() ==0) initMap(nameMap0);                   \
      return nameMap0;                                              \
    }                                                               \
    static std::vector<EnumName> allValues() {                      \
      std::vector<EnumName> x{ __VA_ARGS__ };                       \
      return x;                                                     \
    }                                                               \
    bool operator<(const EnumName a) const { return (int)*this < (int)a; } \
};         

Perhatikan bahwa konversi keString adalah pencarian cepat, sedangkan konversi fromString adalah pencarian linier yang lambat. Namun string sangat mahal (dan file IO terkait), saya tidak merasa perlu untuk mengoptimalkan atau menggunakan bimap.

Mark Lakata
sumber
Anda dan pengguna3360260 memiliki solusi yang baik. Mengapa tidak memiliki multimap saja?
Vincent
3

Berikut solusi satu file (berdasarkan jawaban elegan oleh @Marcin:

#include <iostream>

#define ENUM_TXT \
X(Red) \
X(Green) \
X(Blue) \
X(Cyan) \
X(Yellow) \
X(Magenta) \

enum Colours {
#   define X(a) a,
ENUM_TXT
#   undef X
    ColoursCount
};

char const* const colours_str[] = {
#   define X(a) #a,
ENUM_TXT
#   undef X
    0
};

std::ostream& operator<<(std::ostream& os, enum Colours c)
{
    if (c >= ColoursCount || c < 0) return os << "???";
    return os << colours_str[c] << std::endl;
}

int main()
{
    std::cout << Red << Blue << Green << Cyan << Yellow << Magenta << std::endl;
}
FractalSpace
sumber
2

Saya melakukan ini dengan kelas pembungkus enum side-by-side terpisah yang dihasilkan dengan makro. Ada beberapa keuntungan:

  • Dapat menghasilkannya untuk enum yang tidak saya tentukan (misalnya: enum header platform OS)
  • Dapat menggabungkan pemeriksaan rentang ke dalam kelas pembungkus
  • Dapat melakukan format "lebih pintar" dengan bit field enums

Kelemahannya, tentu saja, adalah saya perlu menduplikasi nilai enum di kelas pemformat, dan saya tidak memiliki skrip untuk membuatnya. Selain itu, tampaknya bekerja dengan cukup baik.

Berikut adalah contoh enum dari basis kode saya, tanpa semua kode kerangka kerja yang mengimplementasikan makro dan templat, tetapi Anda bisa mendapatkan idenya:

enum EHelpLocation
{
    HELP_LOCATION_UNKNOWN   = 0, 
    HELP_LOCAL_FILE         = 1, 
    HELP_HTML_ONLINE        = 2, 
};
class CEnumFormatter_EHelpLocation : public CEnumDefaultFormatter< EHelpLocation >
{
public:
    static inline CString FormatEnum( EHelpLocation eValue )
    {
        switch ( eValue )
        {
            ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_LOCATION_UNKNOWN );
            ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_LOCAL_FILE );
            ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_HTML_ONLINE );
        default:
            return FormatAsNumber( eValue );
        }
    }
};
DECLARE_RANGE_CHECK_CLASS( EHelpLocation, CRangeInfoSequential< HELP_HTML_ONLINE > );
typedef ESmartEnum< EHelpLocation, HELP_LOCATION_UNKNOWN, CEnumFormatter_EHelpLocation, CRangeInfo_EHelpLocation > SEHelpLocation;

Idenya adalah alih-alih menggunakan EHelpLocation, Anda menggunakan SEHelpLocation; semuanya bekerja sama, tetapi Anda mendapatkan pemeriksaan rentang dan metode 'Format ()' pada variabel enum itu sendiri. Jika Anda perlu memformat nilai yang berdiri sendiri, Anda dapat menggunakan CEnumFormatter_EHelpLocation :: FormatEnum (...).

Semoga bermanfaat. Saya menyadari ini juga tidak menjawab pertanyaan asli tentang skrip untuk benar-benar menghasilkan kelas lain, tetapi saya harap strukturnya membantu seseorang mencoba memecahkan masalah yang sama, atau menulis skrip semacam itu.

Nick
sumber
2

Ini adalah perangkat lunak yang belum dirilis tetapi tampaknya BOOST_ENUM dari Frank Laub sesuai dengan tagihan. Bagian yang saya sukai adalah Anda dapat mendefinisikan enum dalam lingkup kelas yang biasanya tidak diizinkan oleh sebagian besar enum berbasis Makro. Itu terletak di Boost Vault di: http://www.boostpro.com/vault/index.php?action=downloadfile&filename=enum_rev4.6.zip&directory=& Belum melihat perkembangan apa pun sejak tahun 2006 jadi saya tidak tahu seberapa baik kompilasi dengan rilis Boost baru. Lihat di bawah libs / test untuk contoh penggunaan.

Alexis
sumber
2

Ini adalah solusi saya dengan BOOST:

#include <boost/preprocessor.hpp>

#define X_STR_ENUM_TOSTRING_CASE(r, data, elem)                                 \
    case elem : return BOOST_PP_STRINGIZE(elem);

#define X_ENUM_STR_TOENUM_IF(r, data, elem)                                     \
    else if(data == BOOST_PP_STRINGIZE(elem)) return elem;

#define STR_ENUM(name, enumerators)                                             \
    enum name {                                                                 \
        BOOST_PP_SEQ_ENUM(enumerators)                                          \
    };                                                                          \
                                                                                \
    inline const QString enumToStr(name v)                                      \
    {                                                                           \
        switch (v)                                                              \
        {                                                                       \
            BOOST_PP_SEQ_FOR_EACH(                                              \
                X_STR_ENUM_TOSTRING_CASE,                                       \
                name,                                                           \
                enumerators                                                     \
            )                                                                   \
                                                                                \
            default:                                                            \
                return "[Unknown " BOOST_PP_STRINGIZE(name) "]";                \
        }                                                                       \
    }                                                                           \
                                                                                \
    template <typename T>                                                       \
    inline const T strToEnum(QString v);                                        \
                                                                                \
    template <>                                                                 \
    inline const name strToEnum(QString v)                                      \
    {                                                                           \
        if(v=="")                                                               \
            throw std::runtime_error("Empty enum value");                       \
                                                                                \
        BOOST_PP_SEQ_FOR_EACH(                                                  \
            X_ENUM_STR_TOENUM_IF,                                               \
            v,                                                                  \
            enumerators                                                         \
        )                                                                       \
                                                                                \
        else                                                                    \
            throw std::runtime_error(                                           \
                        QString("[Unknown value %1 for enum %2]")               \
                            .arg(v)                                             \
                            .arg(BOOST_PP_STRINGIZE(name))                      \
                                .toStdString().c_str());                        \
    }

Untuk membuat enum, nyatakan:

STR_ENUM
(
    SERVICE_RELOAD,
        (reload_log)
        (reload_settings)
        (reload_qxml_server)
)

Untuk konversi:

SERVICE_RELOAD serviceReloadEnum = strToEnum<SERVICE_RELOAD>("reload_log");
QString serviceReloadStr = enumToStr(reload_log);
lopes
sumber
2

Saya ingin memposting ini jika ada yang menganggapnya berguna.

Dalam kasus saya, saya hanya perlu membuat ToString()dan FromString()berfungsi untuk satu enum C ++ 11 dari satu .hppfile.

Saya menulis skrip python yang mem-parsing file header yang berisi item enum dan menghasilkan fungsi dalam .cppfile baru .

Anda dapat menambahkan skrip ini di CMakeLists.txt dengan execute_process , atau sebagai acara pra-bangun di Visual Studio. The .cppfile akan secara otomatis dihasilkan, tanpa perlu update secara manual setiap kali item enum baru ditambahkan.

generate_enum_strings.py

# This script is used to generate strings from C++ enums

import re
import sys
import os

fileName = sys.argv[1]
enumName = os.path.basename(os.path.splitext(fileName)[0])

with open(fileName, 'r') as f:
    content = f.read().replace('\n', '')

searchResult = re.search('enum(.*)\{(.*?)\};', content)
tokens = searchResult.group(2)
tokens = tokens.split(',')
tokens = map(str.strip, tokens)
tokens = map(lambda token: re.search('([a-zA-Z0-9_]*)', token).group(1), tokens)

textOut = ''
textOut += '\n#include "' + enumName + '.hpp"\n\n'
textOut += 'namespace myns\n'
textOut += '{\n'
textOut += '    std::string ToString(ErrorCode errorCode)\n'
textOut += '    {\n'
textOut += '        switch (errorCode)\n'
textOut += '        {\n'

for token in tokens:
    textOut += '        case ' + enumName + '::' + token + ':\n'
    textOut += '            return "' + token + '";\n'

textOut += '        default:\n'
textOut += '            return "Last";\n'
textOut += '        }\n'
textOut += '    }\n'
textOut += '\n'
textOut += '    ' + enumName + ' FromString(const std::string &errorCode)\n'
textOut += '    {\n'
textOut += '        if ("' + tokens[0] + '" == errorCode)\n'
textOut += '        {\n'
textOut += '            return ' + enumName + '::' + tokens[0] + ';\n'
textOut += '        }\n'

for token in tokens[1:]:
    textOut += '        else if("' + token + '" == errorCode)\n'
    textOut += '        {\n'
    textOut += '            return ' + enumName + '::' + token + ';\n'
    textOut += '        }\n'

textOut += '\n'
textOut += '        return ' + enumName + '::Last;\n'
textOut += '    }\n'
textOut += '}\n'

fileOut = open(enumName + '.cpp', 'w')
fileOut.write(textOut)

Contoh:

ErrorCode.hpp

#pragma once

#include <string>
#include <cstdint>

namespace myns
{
    enum class ErrorCode : uint32_t
    {
        OK = 0,
        OutOfSpace,
        ConnectionFailure,
        InvalidJson,
        DatabaseFailure,
        HttpError,
        FileSystemError,
        FailedToEncrypt,
        FailedToDecrypt,
        EndOfFile,
        FailedToOpenFileForRead,
        FailedToOpenFileForWrite,
        FailedToLaunchProcess,

        Last
    };

    std::string ToString(ErrorCode errorCode);
    ErrorCode FromString(const std::string &errorCode);
}

Lari python generate_enum_strings.py ErrorCode.hpp

Hasil:

ErrorCode.cpp

#include "ErrorCode.hpp"

namespace myns
{
    std::string ToString(ErrorCode errorCode)
    {
        switch (errorCode)
        {
        case ErrorCode::OK:
            return "OK";
        case ErrorCode::OutOfSpace:
            return "OutOfSpace";
        case ErrorCode::ConnectionFailure:
            return "ConnectionFailure";
        case ErrorCode::InvalidJson:
            return "InvalidJson";
        case ErrorCode::DatabaseFailure:
            return "DatabaseFailure";
        case ErrorCode::HttpError:
            return "HttpError";
        case ErrorCode::FileSystemError:
            return "FileSystemError";
        case ErrorCode::FailedToEncrypt:
            return "FailedToEncrypt";
        case ErrorCode::FailedToDecrypt:
            return "FailedToDecrypt";
        case ErrorCode::EndOfFile:
            return "EndOfFile";
        case ErrorCode::FailedToOpenFileForRead:
            return "FailedToOpenFileForRead";
        case ErrorCode::FailedToOpenFileForWrite:
            return "FailedToOpenFileForWrite";
        case ErrorCode::FailedToLaunchProcess:
            return "FailedToLaunchProcess";
        case ErrorCode::Last:
            return "Last";
        default:
            return "Last";
        }
    }

    ErrorCode FromString(const std::string &errorCode)
    {
        if ("OK" == errorCode)
        {
            return ErrorCode::OK;
        }
        else if("OutOfSpace" == errorCode)
        {
            return ErrorCode::OutOfSpace;
        }
        else if("ConnectionFailure" == errorCode)
        {
            return ErrorCode::ConnectionFailure;
        }
        else if("InvalidJson" == errorCode)
        {
            return ErrorCode::InvalidJson;
        }
        else if("DatabaseFailure" == errorCode)
        {
            return ErrorCode::DatabaseFailure;
        }
        else if("HttpError" == errorCode)
        {
            return ErrorCode::HttpError;
        }
        else if("FileSystemError" == errorCode)
        {
            return ErrorCode::FileSystemError;
        }
        else if("FailedToEncrypt" == errorCode)
        {
            return ErrorCode::FailedToEncrypt;
        }
        else if("FailedToDecrypt" == errorCode)
        {
            return ErrorCode::FailedToDecrypt;
        }
        else if("EndOfFile" == errorCode)
        {
            return ErrorCode::EndOfFile;
        }
        else if("FailedToOpenFileForRead" == errorCode)
        {
            return ErrorCode::FailedToOpenFileForRead;
        }
        else if("FailedToOpenFileForWrite" == errorCode)
        {
            return ErrorCode::FailedToOpenFileForWrite;
        }
        else if("FailedToLaunchProcess" == errorCode)
        {
            return ErrorCode::FailedToLaunchProcess;
        }
        else if("Last" == errorCode)
        {
            return ErrorCode::Last;
        }

        return ErrorCode::Last;
    }
}
Alexandru Irimiea
sumber
1
Berikut adalah generator online: th-thielemann.de/tools/cpp-enum-to-string.html
Th. Thielemann
2

Menambahkan lebih banyak kesederhanaan penggunaan ke jawaban fantastis Jasper Bekkers :

Siapkan sekali:

#define MAKE_ENUM(VAR) VAR,
#define MAKE_STRINGS(VAR) #VAR,
#define MAKE_ENUM_AND_STRINGS(source, enumName, enumStringName) \
    enum enumName { \
    source(MAKE_ENUM) \
    };\
const char* const enumStringName[] = { \
    source(MAKE_STRINGS) \
    };

Kemudian, untuk pemakaian:

#define SOME_ENUM(DO) \
    DO(Foo) \
    DO(Bar) \
    DO(Baz)
...
MAKE_ENUM_AND_STRINGS(SOME_ENUM, someEnum, someEnumNames)
Francois Bertrand
sumber
2

Anda bisa menggunakan perpustakaan refleksi, seperti Merenungkan . Anda mendaftarkan enum dan kemudian Anda dapat mengubahnya bolak-balik dengan API.

enum class MyEnum
{
    Zero = 0,
    One  = 1,
    Two  = 2
};

ponder::Enum::declare<MyEnum>()
    .value("Zero", MyEnum::Zero)
    .value("One",  MyEnum::One)
    .value("Two",  MyEnum::Two);

ponder::EnumObject zero(MyEnum::Zero);

zero.name(); // -> "Zero"
Nick
sumber
1

Masalah dengan jawaban 0 adalah bahwa nilai biner enum tidak selalu dimulai dari 0 dan tidak harus bersebelahan.

Ketika saya membutuhkan ini, saya biasanya:

  • tarik definisi enum ke sumber saya
  • edit untuk mendapatkan namanya saja
  • lakukan makro untuk mengubah nama menjadi klausa kasus dalam pertanyaan, meskipun biasanya dalam satu baris: case foo: return "foo";
  • tambahkan sakelar, default, dan sintaks lainnya untuk membuatnya legal
mpez0
sumber
1

Skrip ruby ​​berikut mencoba mengurai header dan membangun sumber yang diperlukan di samping header asli.

#! /usr/bin/env ruby

# Let's "parse" the headers
# Note that using a regular expression is rather fragile
# and may break on some inputs

GLOBS = [
  "toto/*.h",
  "tutu/*.h",
  "tutu/*.hxx"
]

enums = {}
GLOBS.each { |glob|
  Dir[glob].each { |header|
    enums[header] = File.open(header, 'rb') { |f|
      f.read
    }.scan(/enum\s+(\w+)\s+\{\s*([^}]+?)\s*\}/m).collect { |enum_name, enum_key_and_values|
      [
        enum_name, enum_key_and_values.split(/\s*,\s*/).collect { |enum_key_and_value|
          enum_key_and_value.split(/\s*=\s*/).first
        }
      ]
    }
  }
}


# Now we build a .h and .cpp alongside the parsed headers
# using the template engine provided with ruby
require 'erb'

template_h = ERB.new <<-EOS
#ifndef <%= enum_name %>_to_string_h_
#define <%= enum_name %>_to_string_h_ 1

#include "<%= header %>"
char* enum_to_string(<%= enum_name %> e);

#endif
EOS

template_cpp = ERB.new <<-EOS
#include "<%= enum_name %>_to_string.h"

char* enum_to_string(<%= enum_name %> e)
{
  switch (e)
  {<% enum_keys.each do |enum_key| %>
    case <%= enum_key %>: return "<%= enum_key %>";<% end %>
    default: return "INVALID <%= enum_name %> VALUE";
  }
}
EOS

enums.each { |header, enum_name_and_keys|
  enum_name_and_keys.each { |enum_name, enum_keys|
    File.open("#{File.dirname(header)}/#{enum_name}_to_string.h", 'wb') { |built_h|
      built_h.write(template_h.result(binding))
    }

    File.open("#{File.dirname(header)}/#{enum_name}_to_string.cpp", 'wb') { |built_cpp|
      built_cpp.write(template_cpp.result(binding))
    }
  }
}

Menggunakan ekspresi reguler membuat "parser" ini cukup rapuh, mungkin tidak dapat menangani header spesifik Anda dengan baik.

Misalkan Anda memiliki header toto / ah, yang berisi definisi untuk enum MyEnum dan MyEnum2. Skrip akan membangun:

toto/MyEnum_to_string.h
toto/MyEnum_to_string.cpp
toto/MyEnum2_to_string.h
toto/MyEnum2_to_string.cpp

Solusi yang lebih kuat adalah:

  • Buat semua sumber yang mendefinisikan enum dan operasinya dari sumber lain. Ini berarti Anda akan menentukan enum Anda dalam file XML / YML / apa pun yang lebih mudah diurai daripada C / C ++.
  • Gunakan kompiler nyata seperti yang disarankan oleh Avdi.
  • Gunakan makro preprocessor dengan atau tanpa template.
bltxd
sumber
0

Itu adalah satu-satunya cara yang bisa dilakukan (array string juga bisa berfungsi).

Masalahnya adalah, setelah program C dikompilasi, nilai biner dari enum adalah semua yang digunakan, dan namanya hilang.

James Curran
sumber
0

Berikut adalah program CLI yang saya tulis untuk dengan mudah mengubah enum menjadi string. Mudah digunakan, dan membutuhkan waktu sekitar 5 detik untuk menyelesaikannya (termasuk waktu untuk melakukan cd ke direktori yang berisi program, lalu menjalankannya, meneruskan file yang berisi enum ke sana).

Unduh di sini: http://www.mediafire.com/?nttignoozzz

Topik diskusi di sini: http://cboard.cprogramming.com/projects-job-recruitment/127488-free-program-im-sharing-convertenumtostrings.html

Jalankan program dengan argumen "--help" untuk mendapatkan penjelasan bagaimana menggunakannya.

Programmer_P
sumber
Bisakah Anda meletakkan ini di repositori di suatu tempat (github, kode google atau bitbucket) dan memposting tautan di sini, alih-alih mediafire? Saya akan membantu orang yang ingin memahaminya :)
Edu Felipe
0

Belum lama ini saya membuat beberapa trik agar enum ditampilkan dengan benar di QComboBox dan memiliki definisi representasi enum dan string sebagai satu pernyataan

#pragma once
#include <boost/unordered_map.hpp>

namespace enumeration
{

   struct enumerator_base : boost::noncopyable
   {
      typedef
         boost::unordered_map<int, std::wstring>
         kv_storage_t;
      typedef
         kv_storage_t::value_type
         kv_type;
      kv_storage_t const & kv() const
      {
         return storage_;
      }

      LPCWSTR name(int i) const
      {
         kv_storage_t::const_iterator it = storage_.find(i);
         if(it != storage_.end())
            return it->second.c_str();
         return L"empty";
      }

   protected:
      kv_storage_t storage_;
   };

   template<class T>
   struct enumerator;

   template<class D>
   struct enum_singleton : enumerator_base
   {
      static enumerator_base const & instance()
      {
         static D inst;
         return inst;
      }
   };
}

#define QENUM_ENTRY(K, V, N)  K, N storage_.insert(std::make_pair((int)K, V));

#define QBEGIN_ENUM(NAME, C)   \
enum NAME                     \
{                             \
   C                          \
}                             \
};                            \
}                             \

#define QEND_ENUM(NAME) \
};                     \
namespace enumeration  \
{                      \
template<>             \
struct enumerator<NAME>\
   : enum_singleton< enumerator<NAME> >\
{                      \
   enumerator()        \
   {

//usage
/*
QBEGIN_ENUM(test_t,
   QENUM_ENTRY(test_entry_1, L"number uno",
   QENUM_ENTRY(test_entry_2, L"number dos",
   QENUM_ENTRY(test_entry_3, L"number tres",
QEND_ENUM(test_t)))))
*/

Sekarang Anda enumeration::enum_singleton<your_enum>::instance()bisa mengubah enum menjadi string. Jika Anda mengganti kv_storage_tdengan boost::bimap, Anda juga dapat melakukan konversi mundur. Kelas dasar umum untuk konverter diperkenalkan untuk menyimpannya di objek Qt, karena objek Qt tidak bisa menjadi template

Penampilan sebelumnya

kassak
sumber
0

Sebagai varian, gunakan lib sederhana> http://codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C

Di dalam kode

#include <EnumString.h>

enum FORM {
    F_NONE = 0,
    F_BOX,
    F_CUBE,
    F_SPHERE,
};

tambahkan garis

Begin_Enum_String( FORM )
{
    Enum_String( F_NONE );
    Enum_String( F_BOX );
    Enum_String( F_CUBE );
    Enum_String( F_SPHERE );
}
End_Enum_String;

Bekerja dengan baik, jika nilai dalam enum tidak dipublikasikan .

Contoh penggunaan

enum FORM f = ...
const std::string& str = EnumString< FORM >::From( f );

dan sebaliknya

assert( EnumString< FORM >::To( f, str ) );
Andrey Syrokomskiy
sumber
0

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
0
#include <iostream>
#include <map>
#define IDMAP(x) (x,#x)

std::map<int , std::string> enToStr;
class mapEnumtoString
{
public:
    mapEnumtoString(){  }
    mapEnumtoString& operator()(int i,std::string str)
    {
        enToStr[i] = str;
        return *this;
    }
public:
   std::string operator [] (int i)
    {
        return enToStr[i];
    }

};
mapEnumtoString k;
mapEnumtoString& init()
{
    return k;
}

int main()
{

init()
    IDMAP(1)
    IDMAP(2)
    IDMAP(3)
    IDMAP(4)
    IDMAP(5);
std::cout<<enToStr[1];
std::cout<<enToStr[2];
std::cout<<enToStr[3];
std::cout<<enToStr[4];
std::cout<<enToStr[5];
}
pengguna3510054
sumber
2
Mohon jelaskan mengapa ini adalah jawabannya.
Alok