C ++ 열거 형을 문자열로 변환하는 간단한 방법이 있습니까?


123

명명 된 열거 형이 있다고 가정합니다.

enum MyEnum {
      FOO,
      BAR = 0x50
};

내가 검색 한 것은 내 프로젝트의 모든 헤더를 스캔하고 열거 형당 하나의 함수로 헤더를 생성하는 스크립트 (모든 언어)입니다.

char* enum_to_string(MyEnum t);

그리고 다음과 같은 구현 :

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

문제는 실제로 typedefed 열거 형과 명명되지 않은 C 스타일 열거 형입니다. 아무도 이것에 대해 알고 있습니까?

편집 : 솔루션은 생성 된 기능을 제외하고 내 소스를 수정해서는 안됩니다. 열거 형은 API에 있으므로 지금까지 제안 된 솔루션을 사용하는 것은 옵션이 아닙니다.


매크로 기반 공장에 대한 답변이 stackoverflow.com/questions/147267/… 으로 이동했습니다 .-질문이 업데이트 된 후에는 더 이상 여기서 관련이 없습니다.
Suma

답변:


48

GCCXML 을 확인하는 것이 좋습니다 .

샘플 코드에서 GCCXML을 실행하면 다음이 생성됩니다.

<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 및 EnumValue 태그를 꺼내고 원하는 코드를 생성하기 위해 선호하는 언어를 사용할 수 있습니다.


우수한! 간단한 파이썬 스크립트로 매력으로 작동했습니다. 감사.
Edu Felipe

6
+1, GCCXML은 매우 멋져 보입니다! (I 거의 -1ed하지만 나는 처음에 열거 인코딩하는 XML 구문 상세 위를 사용하는 제안으로 이것을 오해로! - overengineering의 냄새 솔루션을)
j_random_hacker

1
파이썬 스크립트를 게시 할 수있는 변경 사항이 있습니까?
phillipwei

74

X- 매크로가 최상의 솔루션입니다. 예:

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

그러나 나는 일반적으로 다음 방법을 선호하므로 문자열을 약간 조정할 수 있습니다.

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

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

11
비록 내가 여분의 파일을 좋아하지 않지만
멋지다

2
빌드 프로세스가 모든 포함 파일 앞에 #pragma (한 번)를 추가하지 않도록하십시오 ...
xtofl

24
"최상의"솔루션이 확실하지 않습니다!
궤도의 경쾌함 레이스 2011

2
이 솔루션은 이름이 중복되지 않아 열거를 쉽게 변경할 수 있기 때문에 스위치 케이스 또는 어레이 기반 솔루션보다 훨씬 우수합니다.
줄리앙 Guertault

2
@ ikku100에 대해 잘못되었습니다 #define X(a, b) #b. 이것은 정의가 X(Red, red)대답에 표시된 정의가 아니라 다음과 같은 경우에만 필요합니다.X(Red, "red")
learnvst

43

@hydroo : 추가 파일없이 :

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

저는이 솔루션을 좋아합니다. 하지만 SOME_UNION과 MAKE_UNION이 SOME_ENUM과 MAKE_ENUM으로 불려진다면 더 분명 할 것입니다.
브루노 마르티네스

이것은 훌륭한 솔루션입니다. 저는 지금까지 다루어 본 것 중 가장 유지 관리가 쉬운 C ++ 리소스 관리자를 가지고 있습니다.
DCurro 2014-07-05

나는 :-)이 간단한 솔루션을 주셔서 감사합니다 -는 가지고 있지만 나는 그것을 조금 수정 않은 MetaSyntacticVariableNames[]클래스 선언이 될 부분을, 방법함으로써static const char* getNameByEnum(MetaSyntacticVariable e) { /*code to return the static string*/ }
DeckerDK

환상적인 대답! MAKE_ENUM 및 MAKE_STRINGS를 단일 매크로로 그룹화하여 전체 프로세스를 더 간단하게 만들어 더 단순화했습니다. 누군가 관심이 있다면이 스레드에 해당 코드로 답변을 추가했습니다.
Francois Bertrand

35

내가하는 경향이있는 것은 열거 형 값과 동일한 순서와 위치로 이름을 가진 C 배열을 만드는 것입니다.

예.

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

그런 다음 사람이 읽을 수있는 값을 원하는 곳에서 배열을 사용할 수 있습니다.

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

다음과 같은 상황에서 원하는 작업을 수행하는 스트링 화 연산자 (전 처리기 참조에서 # 참조)로 약간 실험 할 수 있습니다.

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

stdout에 "red"를 인쇄합니다. 불행히도 변수에 대해서는 작동하지 않습니다 (변수 이름이 인쇄되므로)


마지막 경고 (변수에는 작동하지 않음)는 큰 단점이지만 어쨌든 +1입니다.
chappjc 2015-08-27

3
열거 형 항목에 특수 숫자 값을 설정하지 않는 경우에만 작동합니다.
kyb

11

나는 완전히 DRY 방식으로 이것을 수행하는 매크로를 사용하기 매우 간단합니다. 가변 매크로와 간단한 구문 분석 마법이 포함됩니다. 여기에 간다 :

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

코드에서 이것을 사용하려면 다음을 수행하십시오.

AWESOME_MAKE_ENUM(Animal,
    DOG,
    CAT,
    HORSE
);

1
강력한 형식의 열거 형 (열거 형 클래스)을 사용하는 것이 좋습니다. 여기에 데모가 있습니다 : cpp.sh/4ife
chappjc 2015-08-27

외부에서 정의 된 열거 형 / 기호와 함께 작동합니까? 예를 들어, 번호 매기기에 간격이있는 OS 정의 또는 라이브러리 정의 기호?
Jason Harrison

매우 좋지만 클래스 안에 넣으면 컴파일되지 않습니다 (이유를 알 수 없습니다).
AlwaysLearning 2015

나는 이것을 VS2015에서 컴파일하지 못했습니다. 경고 및 오류가 발생합니다. warning : multi-line comment [-Wcomment] #define MAKE_ENUM (name, ...) enum class name { VA_ARGS , __COUNT} error : stray '#'in program std * : string enumName = # NAME
Craig.Feied

8

QT는 (메타 개체 컴파일러 덕분에) 다음을 가져올 수 있습니다.

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에서 Q_OBJECT 매크로가있는 모든 클래스는 QMetaObject 유형의 정적 멤버 "staticMetaObject"를 자동으로 갖게됩니다. 그런 다음 속성, 신호, 슬롯 및 실제로 열거 형과 같은 모든 종류의 멋진 것을 찾을 수 있습니다.

출처


7

이것은 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;
}

1
이것은 OP의 질문에 대한 답이 아닙니다. 그는 열거 형 멤버의 이름을 문자열로 반환하는 함수를 자동으로 생성 하는 방법을 찾고있었습니다 .
Spooky

7

저는 오늘이 바퀴를 다시 발명했고 그것을 공유 할 것이라고 생각했습니다.

이 구현에서는 상수를 정의하는 코드를 변경할 필요 가 없습니다 . 상수는 열거 형이나 #defines 또는 정수로 이관되는 다른 모든 것이 될 수 있습니다 . 제 경우에는 다른 기호로 정의 된 기호가 있습니다. 희소 값에서도 잘 작동합니다. 동일한 값에 대해 여러 이름을 허용하여 항상 첫 번째 이름을 반환합니다. 유일한 단점은 상수 테이블을 만들어야한다는 것입니다. 예를 들어 새 상수가 추가되면 구식이 될 수 있습니다.

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

사용 방법의 예 :

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

IdToName기능은std::lower_bound 는 테이블을 정렬해야하는 빠른 조회를 수행 데 합니다. 테이블의 처음 두 항목이 순서가 맞지 않으면 함수가 자동으로 정렬합니다.

편집 : 코멘트를 통해 동일한 원칙을 사용하는 다른 방법을 생각하게되었습니다. 매크로는 큰 switch문 생성을 단순화합니다 .

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

좋은 솔루션입니다. 그러나 나는 switch and case간단하고 이해하기 쉬운 것을 선호합니다 .
Deqing

6
#define stringify( name ) # name

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

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

이 방법에 대한 추가 논의

신규 이민자를위한 전 처리기 지시어 트릭


4
실제로 이것은 stringify 메서드가 컴파일 타임에 있고 문자 그대로이기 때문에 상당히 쓸모가 없습니다. 변수 안에 문제의 열거 형 유형이 있다고 말하면 변수를 문자열 화하려고하면 열거 형 유형 이름이 아닌 변수 이름 만 제공됩니다.
srcspider jul.

5

여러 가지 방법을 보는 것이 흥미 롭습니다. 여기 내가 오래전에 사용한 것이 있습니다.

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

main.cpp에서

#include "myenummap.h"

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

const는 아니지만 편리합니다.

다음은 C ++ 11 기능을 사용하는 또 다른 방법입니다. 이것은 const이며 STL 컨테이너를 상속하지 않으며 약간 더 깔끔합니다.

#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
완벽하게 합법적입니다. 나는 항상 그것을한다.
Jonathan Graehl 2010 년

좋은 솔루션입니다. 이것은 C ++이므로 stl 맵을 사용하는 것이 좋습니다.
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();

용법:

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

1
나는 당신의 API를 좋아하지만, 불행히도 당신의 SmartEnum은 실제로 열거 형 "유형"을 생성하지 않습니다. 당신은 할 수 없습니다 MyEnum x = MyEnum::TWO;. 이를 지원하기 위해 수업에 대한 편집 내용을 게시했습니다.
Mark Lakata

4

수마의 매크로 솔루션 이 좋습니다. 하지만 두 개의 다른 매크로를 가질 필요는 없습니다. C ++는 행복하게 헤더를 두 번 포함합니다. 포함 가드를 제외하십시오.

그래서 당신은 foobar.h가

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

다음과 같이 포함합니다.

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

enumfactory.h는 2 #include ENUMFACTORY_ARGUMENT초를 수행합니다. 첫 번째 라운드에서는 Suma처럼 ENUM을 확장합니다 DECLARE_ENUM. 두 번째 라운드에서 ENUM은 DEFINE_ENUM.

ENUMFACTORY_ARGUMENT에 대해 다른 #define을 전달하는 한 enumfactory.h도 여러 번 포함 할 수 있습니다.


수마가 여기 에 답을 옮긴 것 같습니다 . 답변에 링크를 포함 할 수 있습니다. 난 단지 우연히 주석을 발견하고 마스 대답 우유없는이 사람은 오히려 무의미
idclev 463,035,818

3

변환 함수는 이상적으로 const char *를 반환해야합니다 .

열거 형을 별도의 헤더 파일에 넣을 여유가 있다면 매크로를 사용하여 다음과 같이 할 수 있습니다 (오, 이것은 추악 할 것입니다).

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

enum_def.h에는 다음이 있습니다.

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

enum_conv.h에는 다음이 있습니다.

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

마지막으로 colour.h에는 다음이 있습니다.

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

그리고 변환 기능을 다음과 같이 사용할 수 있습니다.

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

이것은 추악하지만 코드의 단일 위치에서 열거 형을 정의 할 수있는 유일한 방법 (전 처리기 수준에서)입니다. 따라서 코드는 열거 형 수정으로 인해 오류가 발생하지 않습니다. 열거 형 정의와 변환 함수는 항상 동기화됩니다. 그러나 반복합니다. 이것은 추악합니다. :)


3

또 다른 대답 : 일부 컨텍스트에서는 CSV, YAML 또는 XML 파일과 같은 코드가 아닌 형식으로 열거를 정의한 다음 정의에서 C ++ 열거 코드와 to-string 코드를 모두 생성하는 것이 합리적입니다. 이 접근 방식은 응용 프로그램에서 실용적 일 수도 있고 실용적이지 않을 수도 있지만 명심해야 할 사항입니다.


3

이것은 @ user3360260 답변에 대한 수정입니다. 다음과 같은 새로운 기능이 있습니다.

  • MyEnum fromString(const string&) 지원하다
  • VisualStudio 2012로 컴파일
  • 열거 형은 실제 POD 유형 (const 선언뿐 아니라)이므로 변수에 할당 할 수 있습니다.
  • 열거 형에 대한 "foreach"반복을 허용하기 위해 C ++ "범위"기능 (벡터 형태) 추가

용법:

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

다음은 코드입니다.

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

변환 toString은 빠른 검색이며 fromString 변환은 느린 선형 검색입니다. 그러나 문자열은 어쨌든 비용이 많이 들고 (및 관련 파일 IO) bimap을 최적화하거나 사용할 필요가 없다고 느꼈습니다.


당신과 user3360260은 좋은 솔루션을 가지고 있습니다. 대신 멀티 맵을 사용하지 않는 이유는 무엇입니까?
Vincent

3

여기에 하나의 파일 솔루션이 있습니다 (@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;
}

2

매크로로 생성되는 별도의 병렬 열거 형 래퍼 클래스로이 작업을 수행합니다. 몇 가지 장점이 있습니다.

  • 정의하지 않은 열거 형에 대해 생성 할 수 있습니다 (예 : OS 플랫폼 헤더 열거 형).
  • 범위 검사를 래퍼 클래스에 통합 할 수 있습니다.
  • 비트 필드 열거 형으로 "스마트 한"서식 지정 가능

물론 단점은 포맷터 클래스에서 열거 형 값을 복제해야하고 생성 할 스크립트가 없다는 것입니다. 그 외에는 꽤 잘 작동하는 것 같습니다.

다음은 매크로와 템플릿을 구현하는 모든 프레임 워크 코드를 제외한 코드베이스의 열거 형 예제입니다.하지만 아이디어를 얻을 수 있습니다.

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;

아이디어는 EHelpLocation을 사용하는 대신 SEHelpLocation을 사용하는 것입니다. 모든 것이 동일하게 작동하지만 열거 형 변수 자체에 대해 범위 검사와 'Format ()'메서드가 제공됩니다. 독립 실행 형 값의 형식을 지정해야하는 경우 CEnumFormatter_EHelpLocation :: FormatEnum (...)을 사용할 수 있습니다.

이것이 도움이되기를 바랍니다. 나는 이것이 실제로 다른 클래스를 생성하는 스크립트에 대한 원래의 질문을 다루지 않는다는 것을 알고 있지만 구조가 동일한 문제를 해결하거나 그러한 스크립트를 작성하려는 사람에게 도움이되기를 바랍니다.


2

아직 출시되지 않은 소프트웨어이지만 Frank Laub의 BOOST_ENUM이 비용에 맞을 것 같습니다. 내가 좋아하는 부분은 대부분의 매크로 기반 열거 형에서 일반적으로 허용하지 않는 클래스 범위 내에서 열거 형을 정의 할 수 있다는 것입니다. Boost Vault에 있습니다 : http://www.boostpro.com/vault/index.php?action=downloadfile&filename=enum_rev4.6.zip&directory=& 2006 년 이후로 어떤 발전도 보지 못했기 때문에 저는 그렇지 않습니다. 새로운 Boost 릴리스와 얼마나 잘 컴파일되는지 알 수 있습니다. 사용법의 예는 libs / test를 참조하십시오.


2

이것은 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());                        \
    }

열거 형을 만들려면 다음을 선언하십시오.

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

전환의 경우 :

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

2

누군가 유용하다고 생각되면 이것을 게시하고 싶습니다.

제 경우 에는 단일 파일 에서 단일 C ++ 11 열거 형 을 생성 ToString()하고 FromString()함수 만하면 .hpp됩니다.

열거 형 항목이 포함 된 헤더 파일을 구문 분석하고 새 .cpp파일에 함수를 생성하는 파이썬 스크립트를 작성했습니다 .

execute_process를 사용 하여 CMakeLists.txt에이 스크립트를 추가 하거나 Visual Studio에서 사전 빌드 이벤트로 추가 할 수 있습니다 . .cpp파일이 자동으로 수동으로 새로운 열거 항목이 추가 될 때마다 업데이트 할 필요없이 생성됩니다.

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)

예:

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

운영 python generate_enum_strings.py ErrorCode.hpp

결과:

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
다음은 온라인 생성기입니다. th-thielemann.de/tools/cpp-enum-to-string.html
Th. Thielemann

2

Jasper Bekkers의 환상적인 대답 에 사용의 단순성을 더 추가합니다 .

한 번 설정 :

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

그런 다음 사용법 :

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

2

Ponder 와 같은 반사 라이브러리를 사용할 수 있습니다 . 열거 형을 등록한 다음 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"

1

대답 0의 문제는 열거 형 이진 값이 반드시 0에서 시작하지 않고 반드시 연속적 일 필요는 없다는 것입니다.

필요한 경우 일반적으로 다음을 수행합니다.

  • 열거 형 정의를 내 소스로 가져옵니다.
  • 이름 만 얻기 위해 편집
  • 일반적으로 한 줄에 있지만 질문의 case 절로 이름을 변경하는 매크로를 수행합니다. case foo : return "foo";
  • 합법적으로 만들기 위해 스위치, 기본 및 기타 구문을 추가하십시오.

1

다음 루비 스크립트는 헤더를 구문 분석하고 원본 헤더와 함께 필요한 소스를 빌드합니다.

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

정규식을 사용하면이 "파서"가 매우 취약 해져 특정 헤더를 정상적으로 처리하지 못할 수 있습니다.

열거 형 MyEnum 및 MyEnum2에 대한 정의를 포함하는 헤더 toto / ah가 있다고 가정 해 보겠습니다. 스크립트는 다음을 빌드합니다.

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

더 강력한 솔루션은 다음과 같습니다.

  • 다른 소스에서 열거 형 및 해당 작업을 정의하는 모든 소스를 빌드합니다. 즉, C / C ++보다 구문 분석하기 훨씬 쉬운 XML / YML / 어떤 파일에서든 열거 형을 정의 할 수 있습니다.
  • Avdi에서 제안한 것과 같은 실제 컴파일러를 사용하십시오.
  • 템플릿이 있거나없는 전 처리기 매크로를 사용합니다.

0

이것이 할 수있는 유일한 방법입니다 (문자열 배열도 작동 할 수 있음).

문제는 일단 C 프로그램이 컴파일되면 enum의 이진 값이 사용되는 전부이며 이름이 사라진다는 것입니다.


0

다음은 열거 형을 문자열로 쉽게 변환하기 위해 작성한 CLI 프로그램입니다. 사용하기 쉬우 며 완료하는 데 약 5 초가 걸립니다 (프로그램이 포함 된 디렉토리로 이동 한 다음 실행하고 열거 형이 포함 된 파일에 전달하는 데 걸리는 시간 포함).

여기에서 다운로드하십시오 : http://www.mediafire.com/?nttignoozzz

그것에 대한 토론 주제 : http://cboard.cprogramming.com/projects-job-recruitment/127488-free-program-im-sharing-convertenumtostrings.html

사용 방법에 대한 설명을 보려면 "--help"인수로 프로그램을 실행하십시오.


이것을 저장소 어딘가 (github, google 코드 또는 bitbucket)에 넣고 여기에 링크를 게시 해주세요. mediafire 대신? 나는 그것을 이해하고 싶어하는 사람들을 도울 것입니다 :)
Edu Felipe

0

얼마 전에 QComboBox에 열거 형을 올바르게 표시하고 열거 형 및 문자열 표현을 하나의 문으로 정의하는 트릭을 만들었습니다.

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

이제 enumeration::enum_singleton<your_enum>::instance()열거 형을 문자열로 변환 할 수 있습니다. 당신이 교체하는 경우 kv_storage_tboost::bimap, 당신은 또한 이전 버전과의 변환을 할 수있을 것입니다. Qt 객체는 템플릿이 될 수 없기 때문에 변환기의 공통 기본 클래스가 도입되어 Qt 객체에 저장됩니다.

이전 모습


0

변형으로 간단한 lib> http://codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C를 사용 하십시오.

코드에서

#include <EnumString.h>

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

줄 추가

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

enum의 값이 중복되지 않으면 제대로 작동합니다 .

사용 예

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

그 반대

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

0

다음은 한 줄의 매크로 명령만으로 enum에서 << 및 >> 스트림 연산자를 자동으로 가져 오려는 시도입니다.

정의 :

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

용법:

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

이 계획의 한계에 대해 확실하지 않습니다 ... 의견을 환영합니다!


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
이것이 왜 대답인지 친절하게 설명하십시오.
Alok
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.