C ++ numaralandırmasını dizeye dönüştürmenin basit bir yolu var mı?


123

Bazı adlandırılmış numaralarımız olduğunu varsayalım:

enum MyEnum {
      FOO,
      BAR = 0x50
};

Google'da aradığım şey, projemdeki tüm başlıkları tarayan ve enum başına bir işlevle bir başlık oluşturan bir komut dosyası (herhangi bir dil).

char* enum_to_string(MyEnum t);

Ve bunun gibi bir uygulama:

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

Gotcha gerçekten typedefed numaralandırmalarla ve isimsiz C style enums ile. Bunun için bir şey bilen var mı?

DÜZENLEME: Çözüm, üretilen işlevler dışında kaynağımı değiştirmemelidir. Numaralandırmalar bir API içindedir, bu nedenle şimdiye kadar önerilen çözümleri kullanmak bir seçenek değildir.


Makro tabanlı fabrika hakkındaki cevap stackoverflow.com/questions/147267/… 'e taşındı - soru güncellendikten sonra artık burada geçerli değil.
Suma

Yanıtlar:


48

GCCXML'ye göz atmak isteyebilirsiniz .

Örnek kodunuz üzerinde GCCXML çalıştırmak şunları üretir:

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

Enumeration ve EnumValue etiketlerini çıkarmak ve istediğiniz kodu oluşturmak için tercih ettiğiniz herhangi bir dili kullanabilirsiniz.


Mükemmel! Basit bir python komut dosyasıyla bir cazibe olarak çalıştı. Teşekkürler.
Edu Felipe

6
+1, GCCXML çok güzel görünüyor! (Her ne kadar başlangıçta bunu numaralandırmanızı kodlamak için yukarıdaki ayrıntılı XML sözdizimini kullanma önerisi olarak yanlış yorumlamama rağmen
aşırı mühendislik

1
python betiğini gönderebileceğiniz herhangi bir değişiklik var mı?
phillipwei

74

X-makroları en iyi çözümdür. Misal:

#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)

Bununla birlikte, genellikle aşağıdaki yöntemi tercih ederim, böylece dizeyi biraz değiştirmek mümkün.

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

X(Red, "red")
X(Green, "green")
// etc.

11
şık, ancak fazladan dosyayı sevmeme rağmen
Ronny Brendel

2
Yapım sürecinizin her include dosyasından önce #pragma (bir kez) eklemediğinden emin olun ...
xtofl

24
"En iyi" çözümden emin değilim!
Orbit'te Hafiflik Yarışları

2
Bu çözüm, herhangi bir anahtar durumu veya dizi tabanlı çözümden çok daha üstündür çünkü isimleri çoğaltmaz ve numaralandırmayı değiştirmeyi kolaylaştırır.
Julien Guertault

2
@ ikku100 hakkında yanılıyorsunuz #define X(a, b) #b. Bu, X(Red, red)cevapta gösterilen tanım yerine sadece tanım böyle görünüyorsa gereklidir ,X(Red, "red")
learnvst

43

@hydroo: Ekstra dosya olmadan:

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

Bu çözümü seviyorum. Yine de SOME_UNION ve MAKE_UNION'ın SOME_ENUM ve MAKE_ENUM olarak adlandırılması daha net olurdu.
Bruno Martinez

Bu harika bir çözüm. Bugüne kadar uğraştığım en sürdürülebilir C ++ kaynak yöneticisine sahibim.
DCurro

Bu basit çözüm için teşekkür etmeliyim :-) - Yine de, MetaSyntacticVariableNames[]bir yöntem yaparak bir sınıf bildiriminin parçası olmak için biraz değiştirdimstatic const char* getNameByEnum(MetaSyntacticVariable e) { /*code to return the static string*/ }
DeckerDK

Harika cevap! MAKE_ENUM ve MAKE_STRINGS'i tek bir makro halinde gruplayarak tüm süreci daha da basit hale getirerek bunu daha da basitleştirdim. İlgilenen varsa bu konuya bu kodla bir cevap ekledim.
Francois Bertrand

35

Yapmaya eğilimli olduğum şey, enum değerleriyle aynı sırada ve konumda olan adlarla bir C dizisi oluşturmaktır.

Örneğin.

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

daha sonra diziyi, insan tarafından okunabilir bir değer istediğiniz yerlerde kullanabilirsiniz, örneğin

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

Bazı durumlarda istediğinizi yapacak olan telleştirme operatörü ile biraz deneyebilirsiniz (önişlemci referansınızda # bakın) - örneğin:

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

standart çıktıya "kırmızı" yazdıracaktır. Maalesef bir değişken için işe yaramayacaktır (değişken adını yazdıracağınız için)


Son uyarı (bir değişken için işe yaramaz) büyük bir dezavantajdır, ancak yine de +1.
chappjc

3
Yalnızca girdileri numaralandırmak için özel sayısal değerler ayarlamazsanız çalışır.
kyb

11

Bunu tamamen KURU bir şekilde yapan inanılmaz derecede basit bir makro kullanıyorum. Çeşitli makrolar ve bazı basit ayrıştırma sihri içerir. İşte:

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

Bunu kodunuzda kullanmak için şunları yapmanız yeterlidir:

AWESOME_MAKE_ENUM(Animal,
    DOG,
    CAT,
    HORSE
);

1
Kesin olarak yazılmış bir numaralandırma (enum sınıfı) kullanmak iyi fikir. İşte bir demo: cpp.sh/4ife
chappjc

Bu, harici olarak tanımlanmış numaralandırma / sembollerle çalışır mı? Örneğin, numaralandırmada boşluklar olan işletim sistemi tanımlı veya kitaplık tanımlı semboller?
Jason Harrison

Çok güzel, ama bir sınıfa yerleştirilirse derleme yapmıyor (nedenini anlayamadım).
AlwaysLearning

Bunu VS2015'te derleyemedim. Bir uyarı ve bir hata alıyorum: uyarı: çok satırlı açıklama [-Wcomment] #define MAKE_ENUM (ad, ...) enum sınıf adı { VA_ARGS , __COUNT} hata: program std'de başıboş '#' *: string enumName = #name
Craig.Feied

8

QT şunları çekebilir (meta nesne derleyicisi sayesinde):

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;

}

Qt'de, Q_OBJECT makrosuna sahip her sınıf, otomatik olarak QMetaObject türünde "staticMetaObject" statik üyesine sahip olacaktır. Daha sonra özellikler, sinyaller, yuvalar ve gerçekten numaralandırmalar gibi her türden harika şeyi bulabilirsiniz.

Kaynak


7

Bu, C ++ 11'de yapılabilir

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

1
Bu, OP'nin sorusuna cevap vermiyor: bir enum üyesinin adını bir dizge olarak döndürmek için otomatik olarak bir işlev oluşturmanın bir yolunu arıyordu .
Spooky

7

Bugün bu tekerleği yeniden icat ettim ve paylaşacağımı düşündüm.

Bu uygulama yok değil numaralandırma veya olabilir sabitleri tanımlar kodunda herhangi bir değişiklik gerektirmez #definebenim durumumda ben semboller diğer sembollerin açısından tanımlanır etmişti - ler veya bir tamsayı devolves başka bir şey. Aynı zamanda seyrek değerlerle de iyi çalışır. Hatta aynı değer için birden fazla isme izin vererek her zaman ilkini döndürür. Tek dezavantajı, sabitlerin bir tablosunu oluşturmanızı gerektirmesidir, bu, örneğin yenileri eklendikçe geçerliliğini yitirebilir.

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

Nasıl kullanacağınıza dair bir örnek:

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

IdToNameFonksiyonu dayanır std::lower_boundsıralanmasını tablo gerektirir hızlı aramalarını yapmak. Tablodaki ilk iki giriş sıra dışıysa, işlev onu otomatik olarak sıralayacaktır.

Düzenleme: Bir yorum bana aynı prensibi kullanmanın başka bir yolunu düşündürdü. Bir makro, büyük bir switchifadenin oluşturulmasını basitleştirir .

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

Güzel çözüm. Ama benim switch and caseiçin basit ve anlaşılması kolay olduğu için tercih ederim .
Deqing

6
#define stringify( name ) # name

enum MyEnum {
    ENUMVAL1
};
...stuff...

stringify(EnumName::ENUMVAL1);  // Returns MyEnum::ENUMVAL1

Bu yöntemle ilgili daha fazla tartışma

Yeni gelenler için önişlemci yönergesi püf noktaları


4
Aslında bu oldukça işe yaramaz, çünkü stringify metodu derleme zamanında ve oldukça gerçek. Bir değişkenin içinde söz konusu enum türüne sahip olduğunuzu söylerseniz, değişkeni dizgilendirmeye çalışmak size enum türü adını değil, yalnızca değişken adını verir.
srcspider

5

Yolların sayısını görmek ilginç. işte uzun zaman önce kullandığım bir şey:

myenummap.h dosyasında:

#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(){};
};

main.cpp'de

#include "myenummap.h"

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

Sabit değil, ama kullanışlı.

İşte C ++ 11 özelliklerini kullanmanın başka bir yolu. Bu sabittir, bir STL kabını miras almaz ve biraz daha derli topludur:

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

1
Tamamen yasal. Onu her zaman yaparım.
Jonathan Graehl

Güzel çözüm. Bu c ++ olduğundan stl haritasını kullanmak tamamdır.
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();

Kullanımı:

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

1
API'nizi beğendim, ancak maalesef SmartEnum'unuz aslında bir sıralama "türü" oluşturmuyor. Yapamazsın MyEnum x = MyEnum::TWO;. Bunu desteklemek için sınıfınıza ilişkin düzenlememi yayınladım.
Mark Lakata

4

Suma'nın makro çözümü güzel. Yine de iki farklı makroya sahip olmanıza gerek yok. C ++ mutlu bir şekilde iki kez başlık ekleyecektir. Sadece dahil etme korumasını dışarıda bırakın.

Yani bir foobar.h tanımlayan

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

ve bunu şu şekilde dahil edersiniz:

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

enumfactory.h 2 yapacak #include ENUMFACTORY_ARGUMENT saniye . İlk turda, Suma'nınki gibi ENUM'u genişletiyor DECLARE_ENUM; ikinci turda ENUM gibi çalışır DEFINE_ENUM.

ENUMFACTORY_ARGUMENT için farklı # tanımları geçtiğiniz sürece enumfactory.h'yi birden çok kez ekleyebilirsiniz.


Görünüşe göre suma cevabı buraya taşıdı . Bağlantıyı cevabınıza eklemek isteyebilirsiniz. Yorumu sadece tesadüfen buldum ve cevapsız sumalar bu oldukça anlamsız
idclev 463035818

3

Dönüştürme işlevinizin ideal olarak bir const char * döndürmesi gerektiğini unutmayın .

Numaralandırmalarınızı ayrı başlık dosyalarına koymayı göze alabiliyorsanız, makrolarla şöyle bir şey yapabilirsiniz (oh, bu çirkin olacaktır):

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

Enum_def.h'de:

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

Ve enum_conv.h'de:

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

Ve son olarak, colour.h'de:

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

Ve dönüştürme işlevini şu şekilde kullanabilirsiniz:

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

Bu çirkin, ancak numaranızı kodunuzda yalnızca tek bir yerde tanımlamanıza izin veren tek yol (önişlemci düzeyinde). Bu nedenle kodunuz, numaralandırmada yapılan değişikliklerden kaynaklanan hatalara meyilli değildir. Numaralandırma tanımınız ve dönüştürme işlevi her zaman senkronize olacaktır. Yine de tekrar ediyorum, bu çirkin :)


3

Başka bir cevap: Bazı bağlamlarda, numaralandırmanızı CSV, YAML veya XML dosyası gibi kod olmayan bir biçimde tanımlamak ve ardından tanımdan hem C ++ numaralandırma kodunu hem de dizeye dönüştürme kodunu oluşturmak mantıklıdır. Bu yaklaşım, uygulamanızda pratik olabilir veya olmayabilir, ancak akılda tutulması gereken bir şeydir.


3

Bu, @ user3360260 cevabına yapılan bir değişikliktir. Aşağıdaki yeni özelliklere sahiptir

  • MyEnum fromString(const string&) destek
  • VisualStudio 2012 ile derlenir
  • enum gerçek bir POD türüdür (yalnızca sabit bildirimleri değil), bu nedenle onu bir değişkene atayabilirsiniz.
  • numaralandırma üzerinden "foreach" yinelemesine izin vermek için C ++ "aralık" özelliği (vektör biçiminde) eklendi

Kullanımı:

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

İşte kod

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

Diziye dönüştürme işleminin hızlı bir arama olduğunu, Dizeden dönüştürme ise yavaş doğrusal bir arama olduğunu unutmayın. Ancak dizeler zaten çok pahalı (ve ilişkili dosya IO), bir bimap'i optimize etme veya kullanma ihtiyacı hissetmedim.


Siz ve user3360260 iyi bir çözüme sahipsiniz. Neden bunun yerine bir çoklu haritaya sahip değilsiniz?
Vincent

3

İşte tek dosyalık bir çözüm (@Marcin'in zarif cevabına göre:

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

2

Bunu makrolarla oluşturulan ayrı yan yana enum sarmalayıcı sınıflarıyla yapıyorum. Birkaç avantajı vardır:

  • Tanımlamadığım numaralandırmalar için bunları oluşturabilir (örneğin: OS platform başlık numaralandırmaları)
  • Aralık kontrolünü sarmalayıcı sınıfına dahil edebilir
  • Bit alanı numaralandırmalarıyla "daha akıllı" biçimlendirme yapabilir

Elbette dezavantajı, formatlayıcı sınıflarındaki enum değerlerini çoğaltmam gerektiğidir ve bunları oluşturmak için herhangi bir komut dosyam yok. Bunun dışında, oldukça iyi çalışıyor gibi görünüyor.

İşte benim kod tabanımdan bir enum örneği, makroları ve şablonları uygulayan tüm çerçeve kodunu sans, ancak fikri edinebilirsiniz:

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;

O halde fikir, EHelpLocation yerine SEHelpLocation kullanırsınız; her şey aynı şekilde çalışır, ancak aralık denetimi ve enum değişkeninin kendisinde bir 'Format ()' yöntemi alırsınız. Tek başına bir değeri biçimlendirmeniz gerekiyorsa, CEnumFormatter_EHelpLocation :: FormatEnum (...) kullanabilirsiniz.

Umarım bu yardımcı olur. Bunun aynı zamanda, diğer sınıfı gerçekten oluşturmak için bir senaryo hakkındaki orijinal soruyu ele almadığını fark ettim, ancak yapının, aynı sorunu çözmeye veya böyle bir senaryo yazmaya çalışan birine yardımcı olacağını umuyorum.


2

Yayınlanmamış bir yazılım, ancak Frank Laub'dan BOOST_ENUM bu faturayı karşılayabilir gibi görünüyor. Bu konuda sevdiğim kısım, Makro tabanlı numaralandırmaların çoğunun genellikle yapmanıza izin vermediği bir sınıf kapsamında bir enum tanımlayabilmenizdir. Boost Kasasında şu adrestedir: http://www.boostpro.com/vault/index.php?action=downloadfile&filename=enum_rev4.6.zip&directory=& 2006'dan beri herhangi bir gelişme görmedi, bu yüzden görmüyorum Yeni Boost sürümleriyle ne kadar iyi derlendiğini bilir. Kullanım örneği için libs / test bölümüne bakın.


2

BOOST ile çözümüm buydu:

#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());                        \
    }

Enum oluşturmak için şunları beyan edin:

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

Dönüşümler için:

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

2

Birinin yararlı bulması durumunda bunu yayınlamak istiyorum.

Benim durumumda, tek bir dosyadan tek bir C ++ 11 numaralandırması oluşturmam ToString()ve FromString()işlev görmem gerekiyor .hpp.

Enum öğelerini içeren başlık dosyasını ayrıştıran ve işlevleri yeni bir .cppdosyada oluşturan bir python betiği yazdım .

Sen ile CMakeLists.txt Bu komut dosyasını ekleyebilir execute_process veya Visual Studio Pre-build olay olarak. .cppDosya otomatik manuel yeni bir numaralandırma maddesi eklenir her zaman güncellemek gerek kalmadan, oluşturulur.

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)

Misal:

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

Çalıştırmak python generate_enum_strings.py ErrorCode.hpp

Sonuç:

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

1
İşte bir çevrimiçi oluşturucu: th-thielemann.de/tools/cpp-enum-to-string.html
Th. Thielemann

2

Jasper Bekkers'ın harika cevabına daha da fazla kullanım kolaylığı ekleyerek :

Bir kez kurun:

#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) \
    };

Daha sonra kullanım için:

#define SOME_ENUM(DO) \
    DO(Foo) \
    DO(Bar) \
    DO(Baz)
...
MAKE_ENUM_AND_STRINGS(SOME_ENUM, someEnum, someEnumNames)

2

Ponder gibi bir yansıma kitaplığı kullanabilirsiniz . Numaralandırmaları kaydedersiniz ve sonra bunları API ile ileri geri dönüştürebilirsiniz.

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"

1

0 cevabıyla ilgili bir sorun, enum ikili değerlerinin mutlaka 0'dan başlamaması ve mutlaka bitişik olmamasıdır.

Buna ihtiyacım olduğunda genellikle:

  • enum tanımını kaynağıma çek
  • sadece isimleri almak için düzenleyin
  • adı sorudaki case cümlesine değiştirmek için bir makro yapın, ancak tipik olarak bir satır üzerindedir: case foo: return "foo";
  • yasal hale getirmek için anahtarı, varsayılanı ve diğer sözdizimini ekleyin

1

Aşağıdaki Ruby betiği, başlıkları ayrıştırmaya çalışır ve orijinal başlıkların yanında gerekli kaynakları oluşturur.

#! /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))
    }
  }
}

Normal ifadeler kullanmak bu "ayrıştırıcıyı" oldukça kırılgan hale getirir, özel başlıklarınızı incelikle işleyemeyebilir.

MyEnum ve MyEnum2 numaralandırmaları için tanımları içeren bir to / ah başlığınız olduğunu varsayalım. Komut dosyası şunları oluşturacak:

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

Daha sağlam çözümler şunlar olacaktır:

  • Numaralandırmaları ve işlemlerini başka bir kaynaktan tanımlayan tüm kaynakları oluşturun. Bu, numaralandırmalarınızı bir XML / YML / ayrıştırması C / C ++ 'dan çok daha kolay olan dosyada tanımlayacağınız anlamına gelir.
  • Avdi'nin önerdiği gibi gerçek bir derleyici kullanın.
  • Ön işlemci makrolarını şablonlu veya şablonsuz kullanın.

0

Yapılmasının hemen hemen tek yolu budur (bir dizi de işe yarayabilir).

Sorun şu ki, bir C programı bir kez derlendiğinde, kullanılan tek şeyin enum ikili değeridir ve isim kaybolur.


0

İşte numaralandırmaları kolayca dizelere dönüştürmek için yazdığım bir CLI programı. Kullanımı kolaydır ve tamamlanması yaklaşık 5 saniye sürer (programı içeren dizine cd yazma, ardından çalıştırma, numaralandırmayı içeren dosyaya geçme süresi dahil).

Buradan indirin: http://www.mediafire.com/?nttignoozzz

Tartışma konusu burada: http://cboard.cprogramming.com/projects-job-recruitment/127488-free-program-im-sharing-convertenumtostrings.html

Nasıl kullanılacağına dair bir açıklama almak için programı "--help" argümanıyla çalıştırın.


Lütfen bunu bir depoya (github, google kodu veya bitbucket) koyup mediafire yerine bağlantıyı buraya gönderebilir misiniz? Bunu anlamak isteyen insanlara yardım ederdim :)
Edu Felipe

0

Çok uzun zaman önce, numaralandırmaların QComboBox'ta düzgün şekilde görüntülenmesi ve enum ve dize temsillerinin tek bir ifade olarak tanımlanması için bazı hileler yaptım

#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)))))
*/

Artık enumeration::enum_singleton<your_enum>::instance()numaralandırmaları dizelere dönüştürebilirsiniz. Eğer değiştirirseniz kv_storage_tile boost::bimap, ayrıca geriye dönüşüm yapmak mümkün olacak. Qt nesneleri şablon olamaz çünkü dönüştürücü için ortak temel sınıf Qt nesnesinde saklanması için tanıtıldı

Önceki görünüm


0

Varyant olarak basit lib kullanın> http://codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C

Kodda

#include <EnumString.h>

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

satır ekle

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

Çalışma ince, enum değerler dublicate değilse .

Örnek kullanım

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

ve tam tersi

assert( EnumString< FORM >::To( f, str ) );

0

Burada << ve >> akış operatörlerini enum'a otomatik olarak yalnızca bir satır makro komutuyla alma girişimi ...

Tanımlar:

#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]); \
    }

Kullanımı:

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

Yine de bu planın sınırlamalarından emin değilim ... yorumlarınızı bekliyoruz!


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

2
Lütfen bunun neden cevap olduğunu açıklayın.
Alok
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.