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.