C ++ 열거 형을 문자열에 쉽게 매핑하는 방법


119

사용중인 일부 라이브러리 헤더 파일에 열거 형 유형이 많이 있으며 열거 형 값을 사용자 문자열로 변환하는 방법을 원합니다.

RTTI는 '사용자 문자열'이 열거 형보다 조금 더 읽기 쉬워야하기 때문에 나를 위해하지 않습니다.

무차별 대입 솔루션은 이와 같은 여러 기능이 될 수 있지만 너무 C와 비슷하다고 생각합니다.

enum MyEnum {VAL1, VAL2,VAL3};

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

템플릿을 사용하는 우아한 솔루션이 있다는 직감이 있지만 아직 머리가 잘 잡히지 않습니다.

업데이트 : 제안 해 주셔서 감사합니다-열거 형이 타사 라이브러리 헤더에 정의되어 있음을 분명히 했어야했기 때문에 정의를 변경할 필요가 없습니다.

이제 내 직감은 템플릿을 피하고 다음과 같이하는 것입니다.

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

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

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

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

// To use...

    eee e;
    fff f;
    std::cout<< getStringValue(e);
    std::cout<< getStringValue(f);

답변:


60

열거 형 이름 자체를 문자열로하려면 이 게시물을 참조하십시오 . 그렇지 않으면 a std::map<MyEnum, char const*>가 잘 작동합니다. (문자열 리터럴을 맵의 std :: strings에 복사 할 필요가 없습니다)

추가 구문 설탕을 위해 map_init 클래스를 작성하는 방법은 다음과 같습니다. 목표는

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

이 함수 template <typename T> map_init(T&)는 map_init_helper<T>. map_init_helper<T>T &를 저장하고 사소한 map_init_helper& operator()(typename T::key_type const&, typename T::value_type const&). (반환 *this에서 것은 operator()의 체인을 허용 operator()처럼, operator<<에 std::ostream들)

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

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

함수 및 도우미 클래스는 템플릿 화되어 있으므로 모든지도 또는지도와 유사한 구조에 사용할 수 있습니다. 즉, 항목을 추가 할 수도 있습니다.std::unordered_map

이러한 도우미를 작성하는 것을 좋아하지 않는 경우 boost :: assign은 기본적으로 동일한 기능을 제공합니다.


다른 질문을 참조하는 것이 옳습니다. 사람들은 게시하기 전에 "관련 질문"을 살펴 봐야합니다 ...
— xtofl

2
@xtofl : 여기에 표시된 "관련 질문"은 질문을 게시 할 때 나열된 관련 질문과 완전히 다릅니다!
— Roddy

@MSalters, std :: map은 구현을 처리하는 유용한 방법이지만 필요한 상용구 코드를 줄이는 몇 가지 방법을 찾고 있습니다.
— Roddy

@MSalters, operator []에 대한 여러 인수를 받아 들일 수 있으면 좋을 것입니다. 하지만 슬프게도 그렇게 할 수 없습니다. x [a, b]는 x [b]로 평가됩니다. (a, b) 표현식은 쉼표 연산자를 사용합니다. 따라서 코드에서 [ "A"] [ "B"] [ "C"]와 동일합니다. [eValue1] [ "A"] [eValu ..
— Johannes Schaub-litb

함수 호출 연산자도 좋은 후보입니다. map_init (MyMap) (eValue1, "A") (eValue2, "B") .... 그러면 boost :: assign : insert (MyMap) (eValue1, "A") (eValue2, "B") ... ( boost.org/doc/libs/1_35_0/libs/assign/doc/index.html )
— Johannes Schaub-litb

31

MSalters 솔루션은 좋은 솔루션이지만 기본적으로 boost::assign::map_list_of. 부스트가있는 경우 직접 사용할 수 있습니다.

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

using boost::assign::map_list_of;

enum eee { AA,BB,CC };

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

int main()
{
    std::cout << " enum AA = " << eeeToString.at(AA) << std::endl;
    return 0;
}

eeeToString이 클래스의 데이터 멤버 인 경우 어떻게 사용합니까? "오류 : 데이터 멤버 초기화가 허용되지 않습니다"가 나타납니다
— User

@User : 클래스 데이터 멤버는 일반적으로 이니셜 라이저 목록에서 생성자에서 초기화됩니다.
— MSalters

모든 열거 형에 대해이 작업을 수행하는 방법이 있습니까? 여러 개의 열거 형 선언이 있으며 eee귀하의 경우 유형 에 대해서만 맵이 작동하는 것을 원하지 않습니다 .
— Justin Liang

템플릿을 사용해 보았지만 오류가 발생 error: template declaration of 'const boost::unordered::unordered_map<T, const char*> enumToString'했습니다..
— Justin Liang

4
실제로이 답변은 C ++ 11에서 대체로 쓸모가 없습니다.
— Alastair 2015 년

19

한 양식을 다른 양식에서 자동 생성합니다.

출처:

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

생성 :

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

열거 형 값이 크면 생성 된 양식은 Constantin이 제안한대로 unorder_map <> 또는 템플릿을 사용할 수 있습니다.

출처:

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

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

생성 :

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

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

예:

#include <iostream>

int main()
{
  std::cout << enum2str<state16>::value << std::endl;
  return 0;
}

가장 빠르지 만 @MSalters만큼 쉽지는 않습니다.
— kenny

2
텍스트 파일에서 문자열 목록을 읽고 컴파일 타임에 정적 문자로 .h 파일을 생성하는 데 약간의 perl / python이있는 경우입니다. = "프로그램을 작성하는 프로그램 작성"
— Martin Beckett

@mgb : perl / python은 모든 언어의 거의 모든 템플릿 엔진이 할 수있는 유일한 옵션이 아닙니다 (이 경우 하나는 템플릿에서 두 양식을 모두 생성합니다).
— jfs 09-05-05

@jf. 예, 중요한 점은 컴파일 타임에 자동으로 정적 데이터 테이블을 빌드하는 것이 었습니다. 나는 아마도 멍청한 정적 배열을 생성하는 것을 선호 할 것입니다.
— Martin Beckett

컴파일 시간에 State가 알려지지 않은 경우 이것이 작동합니까? 이론상 컴파일러는 enum의 가능한 모든 값을 사용하여 enum2str 템플릿을 인스턴스화해야하므로 gcc (적어도)가 수행하지 않을 것이라고 확신합니다.
— Alastair 2011

11

나는 StackOverflow의 다른 곳에서 이것을 답한 것을 기억합니다. 여기에서 반복합니다. 기본적으로 가변 매크로를 기반으로하는 솔루션이며 사용하기 매우 쉽습니다.

#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
);
auto dog = Animal::DOG;
std::cout<<dog;

1
enum 클래스 선언을 enum으로 변경하여 이전 C ++ 11에서 작동합니다.
— Debdatta Basu

1
당신이 맞습니다 (자동은 또한 C ++ 11입니다). 좋은 해결책! 일부 열거 형에 대한 값도 설정할 수 있다면 완벽 할 것입니다
— jamk

나는 그런 부스트 뭔가 알 것 같아요
— 세르게이

10

X- 매크로를 혼합하여 사용 하는 것이 최상의 솔루션 이라고 제안합니다. 이며 다음과 같은 템플릿 기능을 합니다.

marcinkoziukmyopenidcom 을 빌리고 확장 하려면

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

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

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

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

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

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

ENUMANDSTR(Colours,colours_str)

colour.def

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

열거 형 문자열 배열 정의를 일반화하는 방법이 있습니까? (매크로 내에서 X-Macro를 처리하는 방법을 모르고 템플릿을 쉽게 처리하지 않습니다)
— Jonathan

5

아래에서 재현 하는 이 솔루션을 사용 합니다.

#define MACROSTR(k) #k

#define X_NUMBERS \
       X(kZero  ) \
       X(kOne   ) \
       X(kTwo   ) \
       X(kThree ) \
       X(kFour  ) \
       X(kMax   )

enum {
#define X(Enum)       Enum,
    X_NUMBERS
#undef X
} kConst;

static char *kConstStr[] = {
#define X(String) MACROSTR(String),
    X_NUMBERS
#undef X
};

int main(void)
{
    int k;
    printf("Hello World!\n\n");

    for (k = 0; k < kMax; k++)
    {
        printf("%s\n", kConstStr[k]);
    }

    return 0;
}

1
이것은 기본 X 매크로이며 이것이 제안하는 첫 번째 답변이라는 것에 놀랐습니다! +1
— 궤도의 가벼운 경주

4

MyEnum 변수의 문자열 표현을 얻으려면 템플릿이 잘라 내지 않습니다. 템플릿은 컴파일 타임에 알려진 정수 값에 특화 될 수 있습니다.

그러나 그것이 당신이 원하는 것이라면 다음을 시도하십시오.

#include <iostream>

enum MyEnum { VAL1, VAL2 };

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

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

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

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

이것은 장황하지만 문제가 된 것과 같은 오류를 포착합니다 case VAL1.


실제로 메서드 name ()은 필요하지 않습니다. 내 대답을 참조하십시오.
— jfs

3

인정하고 싶은이 주제를 연구하는 데 더 많은 시간을 할애했습니다. 다행히도 훌륭한 오픈 소스 솔루션이 있습니다.

이 두 가지 방법은 충분히 잘 알려지지 않았지만

wise_enum

  • C ++ 11 / 14 / 17 용 독립형 스마트 열거 형 라이브러리. C ++의 스마트 열거 형 클래스에서 기대할 수있는 모든 표준 기능을 지원합니다.
  • 제한 : 최소한 C ++ 11이 필요합니다.

더 나은 열거 형

  • 단일 헤더 파일에서 종속성없이 깨끗한 구문을 사용하는 반사 컴파일 타임 열거 형 라이브러리.
  • 제한 사항 : 매크로에 따라 클래스 내에서 사용할 수 없습니다.

2

나는지도 m을 갖고 싶을 것입니다. 그리고 이것을 열거 형에 포함 시켰습니다.

m [MyEnum.VAL1] = "값 1"로 설정;

그리고 모든 것이 끝났습니다.


2

다른 사람의 코드를 디버깅 / 분석하기 위해이 기능이 여러 번 필요했습니다. 이를 위해 여러 오버로드 된 toString메서드 로 클래스를 생성하는 Perl 스크립트를 작성했습니다 . 각 toString메서드는를 Enum인수로 사용하고를 반환합니다 const char*.

물론 스크립트는 열거 형 자체에 대해 C ++를 구문 분석하지 않지만 기호 테이블을 생성하기 위해 ctag를 사용합니다.

Perl 스크립트는 여기에 있습니다 : http://heinitz-it.de/download/enum2string/enum2string.pl.html


2

귀하의 답변은 저에게 일부 매크로를 작성하도록 영감을주었습니다. 내 요구 사항은 다음과 같습니다.

  1. 열거 형의 각 값을 한 번만 작성하므로 유지할 이중 목록이 없습니다.

  2. 나중에 #included 별도의 파일에 열거 형 값을 보관하지 마십시오. 원하는 곳에 쓸 수 있습니다.

  3. enum 자체를 바꾸지 말고 enum 유형을 정의하고 싶지만 그 외에도 모든 enum 이름을 해당 문자열에 매핑 할 수 있기를 원합니다 (레거시 코드에 영향을주지 않음).

  4. 검색은 빨라야합니다. 따라서 그 거대한 열거 형에 대해서는 스위치 케이스가없는 것이 좋습니다.

이 코드는 일부 값이있는 클래식 열거 형을 만듭니다. 또한 각 enum 값을 이름에 매핑하는 std :: map으로 생성합니다 (예 : map [E_SUNDAY] = "E_SUNDAY"등).

좋아, 이제 코드는 다음과 같다.

EnumUtilsImpl.h :

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

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

EnumUtils.h //이 작업을 수행해야 할 때마다 포함 할 파일입니다. 여기에서 매크로를 사용합니다.

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

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

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

MyProjectCodeFile.h // 다음은이를 사용하여 사용자 지정 열거 형을 만드는 방법의 예입니다.

#include "EnumUtils.h*

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

enum MyEnum {
    MyEnumValues(ADD_TO_ENUM)
};

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

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

건배.


2

다음은 한 줄의 매크로 명령만으로 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;
}

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


1

헤더에서 :

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

.cpp 파일에서 :

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

주의 사항 : 잘못된 배열 인덱스를 처리하지 마십시오. :) 그러나 배열에서 문자열을 가져 오기 전에 열거 형을 확인하는 함수를 쉽게 추가 할 수 있습니다.


실제로 매우 non-DRY-SPOT 솔루션입니다.
— xtofl

이제 DRY를 언급했습니다. .h 및 .cpp 파일은 다른 입력 파일에서 자동으로 생성됩니다. (불필요한 복잡성에 의존하지 않는) 더 나은 솔루션을보고 싶습니다
— moogs

1

매크로를 사용하여이 가능한 우아한 솔루션을 보여주고 싶었습니다. 이것은 문제를 해결하지 못하지만 문제에 대해 다시 생각하는 좋은 방법이라고 생각합니다.

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

enum eMyEnum
    {
    MY_LIST(PLAIN)
    };

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


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

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

return 0;
}

---- 편집하다 ----

인터넷 조사와 약간의 노력 끝에 다음 솔루션에 도달했습니다.

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

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

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

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

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


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

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

  return EXIT_SUCCESS;
}

누군가 가이 솔루션이 유용하다고 생각할 수 있다고 게시하고 싶었습니다. 템플릿 클래스가 필요하지 않으며 C ++ 11이 필요하지 않으며 부스트가 필요하지 않으므로 간단한 C에도 사용할 수 있습니다.

---- EDIT2 ----

정보 테이블은 둘 이상의 열거 형을 사용할 때 몇 가지 문제를 일으킬 수 있습니다 (컴파일러 문제). 다음 해결 방법이 작동했습니다.

#define ENUM2STRUCTINFO(spacename,listname) namespace spacename { int spacename##_##values[] = {listname(enumfunc)};int spacename##_##N = sizeof(spacename##_##values)/sizeof(int);ENUM2STRTABLE(spacename##_##enum2str,listname)};

1
typedef enum {
    ERR_CODE_OK = 0,
    ERR_CODE_SNAP,

    ERR_CODE_NUM
} ERR_CODE;

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

위는 내 간단한 해결책입니다. 그것의 한 가지 이점은 메시지 배열의 크기를 제어하는 ​​'NUM'이며 경계를 벗어난 액세스를 방지합니다 (현명하게 사용하는 경우).

문자열을 가져 오는 함수를 정의 할 수도 있습니다.

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

내 솔루션에 더하여 다음 중 하나가 매우 흥미 로움을 발견했습니다. 일반적으로 위의 동기화 문제를 해결했습니다.

여기 슬라이드 : http://www.slideshare.net/arunksaha/touchless-enum-tostring-28684724

여기에 코드 : https://github.com/arunksaha/enum_to_string


1

나는 내가 파티에 늦었다는 것을 알고 있지만,이 페이지를 방문하는 다른 모든 사람들에게 이것을 시도해 볼 수 있습니다.

namespace texs {
    typedef std::string Type;
    Type apple = "apple";
    Type wood = "wood";
}

문자열을 사용하고 열거 형을 전혀 사용하지 않을 것을 제안하고 있습니까? 그것은 실제로 문제를 해결하지 못합니다.
— Roddy

0

최근에 공급 업체 라이브러리 (Fincad)에서 동일한 문제가 발생했습니다. 다행히 공급 업체는 모든 열거 형에 대해 xml 문서화를 제공했습니다. 결국 각 enum 유형에 대한 맵을 생성하고 각 enum에 대한 조회 기능을 제공했습니다. 이 기술을 사용하면 열거 형 범위를 벗어난 조회를 가로 챌 수도 있습니다.

나는 swig가 당신을 위해 비슷한 일을 할 수 있다고 확신하지만, 루비로 작성된 코드 생성 유틸리티를 제공하게되어 기쁩니다.

다음은 코드 샘플입니다.

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

다른 방향으로 가고 싶은 것처럼 보이지만 (문자열을 열거하는 것이 아니라 열거를 문자열로) 반대로하는 것은 사소한 것입니다.

-성령 강림 대축일


1
a) 다른 사람이 이것을 절대적으로 읽을 수 없다고 생각합니까? 몇 가지 typedef와 선언을 사용 하면 가독성 이 크게 향상됩니다. b) 로컬 정적 선언은 스레드로부터 안전하지 않습니다. c) char * 대신 const string & 사용 d) throw 된 예외에서 찾을 수없는 값을 포함하는 것은 어떻습니까?
— Alastair

0

다음 구문이 적합한 지 확인하십시오.

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

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

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

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

그렇다면 http://www.gamedev.net/reference/snippets/features/cppstringizing/ 문서를 확인하세요
.


0

이 바로 오래된 엉망은 SO의 비트와 조각을 기반으로 한 나의 노력입니다. for_each는 20 개 이상의 열거 형 값을 지원하도록 확장되어야합니다. Visual Studio 2019, clang 및 gcc에서 테스트했습니다. C ++ 11

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

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

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

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

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

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

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

다음 코드를 생성합니다.

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

세계에서 가장 많이 사용되는 프로그래밍 언어 중 하나에서이 작업을 수행하기 위해 전처리기를 사용하여 점프해야하는 농구가 정말 아쉽습니다 ...


0

지정된 배열 이니셜 라이저를 사용하면 문자열 배열이 열거 형의 요소 순서와 무관합니다.

enum Values {
    Val1,
    Val2
};

constexpr string_view v_name[] = {
    [Val1] = "Value 1",
    [Val2] = "Value 2"
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.