열거 형 변수를 문자열로 변환하는 방법은 무엇입니까?


128

열거 형 유형의 변수 값을 표시하기 위해 printf를 만드는 방법은 무엇입니까? 예를 들어 :

typedef enum {Linux, Apple, Windows} OS_type; 
OS_type myOS = Linux;

내가 필요한 것은

printenum(OS_type, "My OS is %s", myOS);

정수가 아닌 문자열 "Linux"를 표시해야합니다.

먼저 값 인덱스 문자열 배열을 만들어야한다고 가정합니다. 그러나 그것이 가장 아름다운 방법인지는 모르겠습니다. 전혀 가능합니까?


2
골라 봐 : 여기 , 여기 , 여기 . 그것은 엉망입니다. 모두 반 중복입니다.
rubenvb

다른 하나 는 더 완전합니다.
bit2shift


너무 많은 답변은 아직 "enum class"
Vivick

답변:


70

실제로이 일을하는 아름다운 방법은 없습니다. 열거 형으로 색인 된 문자열 배열을 설정하십시오.

많은 출력을 수행하는 경우 enum 매개 변수를 사용하고 조회를 수행하는 operator <<를 정의 할 수 있습니다.


2
또한 컴파일 타임에 배열에 예상되는 수의 문자열이 있는지 확인할 수 있습니다.
markh44

2
나는 이것에 대해 소수의 사람이라는 것을 알고 있지만, 언어와 관련된 단점을 해결하기 위해 거대한 제 3 자 라이브러리 및 / 또는 Marco-riddled 코드에 의존하고 싶지 않은 저와 같은 프로그래머에게는 이것이 오늘날의 표준을위한 가장 단순하고 순수한 솔루션입니다. +1
Syndog

13
@Syndog 그런 다음 프로덕션 코드의 56 열거 형 열거 형 열거 형은이 프로그래머가 기한이 지난 기능을 해제 해야하는 압력으로 업데이트되며 열거 형 인덱스 배열을 업데이트하는 것을 잊어 버립니다. 관련 인쇄 기능은 응용 프로그램 디버깅 코드에서만 사용되므로 눈에 띄지 않습니다. 2 개월 후, 당신은 실제로 그 디버그 코드를 실행하는 첫 번째 사람입니다. 그러면 잘못된 정보를 제공하므로, 처음으로 디버그 코드를 디버깅해야한다는 것을 깨닫기 전에이 잘못된 정보를 기반으로 가정을 구축하는 데 반나절을 잃게됩니다. 디자인은 명시적인 복제에 의존합니다.
Ad N

1
@AdN 디자인이 잘못되었습니다. 열거 형에서 사람이 읽을 수있는 문자열로의 매핑은 열거 형 값으로 인덱싱 된 문자열 배열로 구현해서는 안됩니다. 당신의 경험은 아마도 그 이유를 보여줍니다. 매핑은 (enum, string) 쌍으로 구성된 빠른 배열이어야하므로 새 열거 형 값에 대한 항목을 추가하는 것을 잊어 버린 경우 "???" 출력으로하지만 적어도 다른 모든 열거 형의 이름을 망치지는 않습니다.
brewbuck

8
@ AdN 시나리오는 배열 대신 스위치 (기본 절 없음)가 포함 된 함수를 선호하고 빌드 파일에서 컴파일러 스위치를 설정하여 모든 것을 다루지 않는 열거 형 스위치에 대한 오류를 발생시키는 이유입니다. 가능한 값. 관련 switch 문을 업데이트하지 않고 새 열거 형 항목을 추가하면 컴파일 오류가 발생합니다.
divegeek

131

물론 순진한 솔루션은 문자열로의 변환을 수행하는 각 열거에 대한 함수를 작성하는 것입니다.

enum OS_type { Linux, Apple, Windows };

inline const char* ToString(OS_type v)
{
    switch (v)
    {
        case Linux:   return "Linux";
        case Apple:   return "Apple";
        case Windows: return "Windows";
        default:      return "[Unknown OS_type]";
    }
}

그러나 이것은 유지 보수 재해입니다. C 및 C ++ 코드와 함께 사용할 수있는 Boost.Preprocessor 라이브러리를 사용하면 쉽게 전처리기를 활용하여이 함수를 생성 할 수 있습니다. 생성 매크로는 다음과 같습니다.

#include <boost/preprocessor.hpp>

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

#define DEFINE_ENUM_WITH_STRING_CONVERSIONS(name, enumerators)                \
    enum name {                                                               \
        BOOST_PP_SEQ_ENUM(enumerators)                                        \
    };                                                                        \
                                                                              \
    inline const char* ToString(name v)                                       \
    {                                                                         \
        switch (v)                                                            \
        {                                                                     \
            BOOST_PP_SEQ_FOR_EACH(                                            \
                X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOSTRING_CASE,          \
                name,                                                         \
                enumerators                                                   \
            )                                                                 \
            default: return "[Unknown " BOOST_PP_STRINGIZE(name) "]";         \
        }                                                                     \
    }

첫 번째 매크로 (로 시작 X_)는 두 번째 매크로에 의해 내부적으로 사용됩니다. 두 번째 매크로는 먼저 열거를 생성 한 다음 ToString해당 유형의 객체를 가져와 열거 자 이름을 문자열로 반환 하는 함수 를 생성 합니다 (이 구현에서는 명백한 이유로 열거자가 고유 한 값에 매핑되어야 함).

C ++에서는 대신 오버로드 ToString로 함수를 구현할 수 operator<<있지만 ToString값을 문자열 형식으로 변환하려면 명시적인 " "이 필요합니다 .

사용 예제로서 OS_type열거는 다음과 같이 정의됩니다.

DEFINE_ENUM_WITH_STRING_CONVERSIONS(OS_type, (Linux)(Apple)(Windows))

처음에는 매크로가 많은 작업 인 OS_type것처럼 보이고 외형은 다소 외형 인 것처럼 보이지만 매크로를 한 번 작성해야한다는 점을 기억하면 모든 열거에 사용할 수 있습니다. 너무 많은 문제없이 추가 기능 (예 : 문자열 형식에서 열거 형 변환)을 추가 할 수 있으며 매크로를 호출 할 때 이름을 한 번만 제공하면되므로 유지 관리 문제를 완전히 해결합니다.

그런 다음 열거는 정상적으로 정의 된 것처럼 사용할 수 있습니다.

#include <iostream>

int main()
{
    OS_type t = Windows;
    std::cout << ToString(t) << " " << ToString(Apple) << std::endl;
}

이 게시물의 코드 스 니펫은 #include <boost/preprocessor.hpp>줄로 시작 하여 솔루션을 보여주기 위해 게시 된대로 컴파일 할 수 있습니다.

이 특정 솔루션은 C ++ 관련 구문 (예 : no typedef enum)과 함수 오버로드를 사용하므로 C ++ 에 적합하지만 C 에서도이 작업을 수행하는 것이 간단합니다.


7
+1, 구현 기계는 무섭지 만 최종 인터페이스는 우아함을 이길 수 없습니다.
deft_code

4
어쨌든 열거 형 정수 값을 제공 할 수 있도록이 방법이 있습니까? 예를 들어 Windows는 3, Linux 5 및 Apple 7입니까?
Mark

4
예, (Windows)로 변경 (Windows, 3)한 다음을 BOOST_PP_SEQ_ENUM적합하게 작성된 로 바꿀 수 있습니다 BOOST_PP_SEQ_FOR_EACH. 그 편리한 예는 없지만 원하는 경우 쓸 수 있습니다.
James McNellis

2
@JamesMcNellis Mark가 요청한 것을 달성하는 코드의 예를 분명히 원합니다. 어떻게 우리에게 길을 보여줄 수 있습니까? :)
Omer Raviv

2
참고 : 부스트 전 처리기의 하드 제한은 256 개입니다. 더 큰 열거 형의 경우 다른 솔루션이 필요합니다.
dshin

32

이것은 전 처리기 블록입니다

#ifndef GENERATE_ENUM_STRINGS
    #define DECL_ENUM_ELEMENT( element ) element
    #define BEGIN_ENUM( ENUM_NAME ) typedef enum tag##ENUM_NAME
    #define END_ENUM( ENUM_NAME ) ENUM_NAME; \
            char* GetString##ENUM_NAME(enum tag##ENUM_NAME index);
#else
    #define DECL_ENUM_ELEMENT( element ) #element
    #define BEGIN_ENUM( ENUM_NAME ) char* gs_##ENUM_NAME [] =
    #define END_ENUM( ENUM_NAME ) ; char* GetString##ENUM_NAME(enum \
            tag##ENUM_NAME index){ return gs_##ENUM_NAME [index]; }
#endif

열거 형 정의

BEGIN_ENUM(Os_type)
{
    DECL_ENUM_ELEMENT(winblows),
    DECL_ENUM_ELEMENT(hackintosh),
} END_ENUM(Os_type)

를 사용하여 전화

GetStringOs_type(winblows);

여기 에서 찍은 . 얼마나 멋진가요? :)


1
이것은 열거 형에 256 개 이상의 요소가있을 때 작동하는 유일한 솔루션입니다.
dshin

8

std::map<OS_type, std::string>enum을 키로 사용하고 문자열 표현을 값으로 사용 하고 채우면 다음을 수행 할 수 있습니다.

printf("My OS is %s", enumMap[myOS].c_str());
std::cout << enumMap[myOS] ;

7

C 열거 형의 문제점은 C ++에서와 같이 자체 유형이 아니라는 것입니다. C의 열거 형은 식별자를 정수 값에 매핑하는 방법입니다. 그냥 그렇기 때문에 열거 형 값을 정수 값과 교환 할 수 있습니다.

올바르게 추측하면 좋은 방법은 열거 형 값과 문자열 사이의 매핑을 만드는 것입니다. 예를 들면 다음과 같습니다.

char * OS_type_label[] = {
    "Linux",
    "Apple",
    "Windows"
};

프로그래밍 언어가 C로 제한되어 있다고 생각합니다.
Andrew

1
당신이 조금 떨어져있는, enum 이다 C. 적분 열거 형 상수의 종류는 유형입니다 int아닌의 enum가 정의되는 유형, 당신이 말을 무슨 뜻인지 아마입니다. 그러나 이것이 질문과 어떤 관련이 있는지 전혀 알지 못합니다.
Jens Gustedt

7

James ' , Howard 'sÉder 's 솔루션 을 결합하고 보다 일반적인 구현을 만들었습니다.

  • 각 enum 요소에 대해 int 값 및 사용자 정의 문자열 표현을 선택적으로 정의 할 수 있습니다.
  • "enum class"가 사용됩니다

전체 코드는 다음과 같이 작성됩니다 (열 정의를 위해 "DEFINE_ENUM_CLASS_WITH_ToString_METHOD"사용) ( 온라인 데모 ).

#include <boost/preprocessor.hpp>
#include <iostream>

// ADD_PARENTHESES_FOR_EACH_TUPLE_IN_SEQ implementation is taken from:
// http://lists.boost.org/boost-users/2012/09/76055.php
//
// This macro do the following:
// input:
//      (Element1, "Element 1 string repr", 2) (Element2) (Element3, "Element 3 string repr")
// output:
//      ((Element1, "Element 1 string repr", 2)) ((Element2)) ((Element3, "Element 3 string repr"))
#define HELPER1(...) ((__VA_ARGS__)) HELPER2
#define HELPER2(...) ((__VA_ARGS__)) HELPER1
#define HELPER1_END
#define HELPER2_END
#define ADD_PARENTHESES_FOR_EACH_TUPLE_IN_SEQ(sequence) BOOST_PP_CAT(HELPER1 sequence,_END)


// CREATE_ENUM_ELEMENT_IMPL works in the following way:
//  if (elementTuple.GetSize() == 4) {
//      GENERATE: elementTuple.GetElement(0) = elementTuple.GetElement(2)),
//  } else {
//      GENERATE: elementTuple.GetElement(0),
//  }
// Example 1:
//      CREATE_ENUM_ELEMENT_IMPL((Element1, "Element 1 string repr", 2, _))
//  generates:
//      Element1 = 2,
//
// Example 2:
//      CREATE_ENUM_ELEMENT_IMPL((Element2, _))
//  generates:
//      Element1,
#define CREATE_ENUM_ELEMENT_IMPL(elementTuple)                                          \
BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_TUPLE_SIZE(elementTuple), 4),                       \
    BOOST_PP_TUPLE_ELEM(0, elementTuple) = BOOST_PP_TUPLE_ELEM(2, elementTuple),        \
    BOOST_PP_TUPLE_ELEM(0, elementTuple)                                                \
),

// we have to add a dummy element at the end of a tuple in order to make 
// BOOST_PP_TUPLE_ELEM macro work in case an initial tuple has only one element.
// if we have a tuple (Element1), BOOST_PP_TUPLE_ELEM(2, (Element1)) macro won't compile.
// It requires that a tuple with only one element looked like (Element1,).
// Unfortunately I couldn't find a way to make this transformation, so
// I just use BOOST_PP_TUPLE_PUSH_BACK macro to add a dummy element at the end
// of a tuple, in this case the initial tuple will look like (Element1, _) what
// makes it compatible with BOOST_PP_TUPLE_ELEM macro
#define CREATE_ENUM_ELEMENT(r, data, elementTuple)                                      \
    CREATE_ENUM_ELEMENT_IMPL(BOOST_PP_TUPLE_PUSH_BACK(elementTuple, _))

#define DEFINE_CASE_HAVING_ONLY_ENUM_ELEMENT_NAME(enumName, element)                                        \
    case enumName::element : return BOOST_PP_STRINGIZE(element);
#define DEFINE_CASE_HAVING_STRING_REPRESENTATION_FOR_ENUM_ELEMENT(enumName, element, stringRepresentation)  \
    case enumName::element : return stringRepresentation;

// GENERATE_CASE_FOR_SWITCH macro generates case for switch operator.
// Algorithm of working is the following
//  if (elementTuple.GetSize() == 1) {
//      DEFINE_CASE_HAVING_ONLY_ENUM_ELEMENT_NAME(enumName, elementTuple.GetElement(0))
//  } else {
//      DEFINE_CASE_HAVING_STRING_REPRESENTATION_FOR_ENUM_ELEMENT(enumName, elementTuple.GetElement(0), elementTuple.GetElement(1))
//  }
//
// Example 1:
//      GENERATE_CASE_FOR_SWITCH(_, EnumName, (Element1, "Element 1 string repr", 2))
//  generates:
//      case EnumName::Element1 : return "Element 1 string repr";
//
// Example 2:
//      GENERATE_CASE_FOR_SWITCH(_, EnumName, (Element2))
//  generates:
//      case EnumName::Element2 : return "Element2";
#define GENERATE_CASE_FOR_SWITCH(r, enumName, elementTuple)                                                                                                 \
    BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_TUPLE_SIZE(elementTuple), 1),                                                                                       \
        DEFINE_CASE_HAVING_ONLY_ENUM_ELEMENT_NAME(enumName, BOOST_PP_TUPLE_ELEM(0, elementTuple)),                                                          \
        DEFINE_CASE_HAVING_STRING_REPRESENTATION_FOR_ENUM_ELEMENT(enumName, BOOST_PP_TUPLE_ELEM(0, elementTuple), BOOST_PP_TUPLE_ELEM(1, elementTuple))     \
    )


// DEFINE_ENUM_CLASS_WITH_ToString_METHOD final macro witch do the job
#define DEFINE_ENUM_CLASS_WITH_ToString_METHOD(enumName, enumElements)          \
enum class enumName {                                                           \
    BOOST_PP_SEQ_FOR_EACH(                                                      \
        CREATE_ENUM_ELEMENT,                                                    \
        0,                                                                      \
        ADD_PARENTHESES_FOR_EACH_TUPLE_IN_SEQ(enumElements)                     \
    )                                                                           \
};                                                                              \
inline const char* ToString(const enumName element) {                           \
        switch (element) {                                                      \
            BOOST_PP_SEQ_FOR_EACH(                                              \
                GENERATE_CASE_FOR_SWITCH,                                       \
                enumName,                                                       \
                ADD_PARENTHESES_FOR_EACH_TUPLE_IN_SEQ(enumElements)             \
            )                                                                   \
            default: return "[Unknown " BOOST_PP_STRINGIZE(enumName) "]";       \
        }                                                                       \
}

DEFINE_ENUM_CLASS_WITH_ToString_METHOD(Elements,
(Element1)
(Element2, "string representation for Element2 ")
(Element3, "Element3 string representation", 1000)
(Element4, "Element 4 string repr")
(Element5, "Element5", 1005)
(Element6, "Element6 ")
(Element7)
)
// Generates the following:
//      enum class Elements {
//          Element1, Element2, Element3 = 1000, Element4, Element5 = 1005, Element6,
//      };
//      inline const char* ToString(const Elements element) {
//          switch (element) {
//              case Elements::Element1: return "Element1";
//              case Elements::Element2: return "string representation for Element2 ";
//              case Elements::Element3: return "Element3 string representation";
//              case Elements::Element4: return "Element 4 string repr";
//              case Elements::Element5: return "Element5";
//              case Elements::Element6: return "Element6 ";
//              case Elements::Element7: return "Element7";
//              default: return "[Unknown " "Elements" "]";
//          }
//      }

int main() {
    std::cout << ToString(Elements::Element1) << std::endl;
    std::cout << ToString(Elements::Element2) << std::endl;
    std::cout << ToString(Elements::Element3) << std::endl;
    std::cout << ToString(Elements::Element4) << std::endl;
    std::cout << ToString(Elements::Element5) << std::endl;
    std::cout << ToString(Elements::Element6) << std::endl;
    std::cout << ToString(Elements::Element7) << std::endl;

    return 0;
}

이것은 지금까지 가장 좋은 답변입니다
Arnout

6

이 간단한 예는 저에게 효과적이었습니다. 도움이 되었기를 바랍니다.

#include <iostream>
#include <string>

#define ENUM_TO_STR(ENUM) std::string(#ENUM)

enum DIRECTION{NORTH, SOUTH, WEST, EAST};

int main()
{
  std::cout << "Hello, " << ENUM_TO_STR(NORTH) << "!\n";
  std::cout << "Hello, " << ENUM_TO_STR(SOUTH) << "!\n";
  std::cout << "Hello, " << ENUM_TO_STR(EAST) << "!\n";
  std::cout << "Hello, " << ENUM_TO_STR(WEST) << "!\n";
}

13
DIRECTION a = NORTH 인 경우 작동하지 않습니다. 다음 ENUM_TO_STR (가) 쓰기
mathreadler

5

이것을 시도 했습니까?

#define stringify( name ) # name

enum enMyErrorValue
  {
  ERROR_INVALIDINPUT = 0,
  ERROR_NULLINPUT,
  ERROR_INPUTTOOMUCH,
  ERROR_IAMBUSY
  };

const char* enMyErrorValueNames[] = 
  {
  stringify( ERROR_INVALIDINPUT ),
  stringify( ERROR_NULLINPUT ),
  stringify( ERROR_INPUTTOOMUCH ),
  stringify( ERROR_IAMBUSY )
  };

void vPrintError( enMyErrorValue enError )
  {
  cout << enMyErrorValueNames[ enError ] << endl;
  }

int main()
  {
  vPrintError((enMyErrorValue)1);
  }

stringify()매크로는 문자열로 코드의 텍스트를 설정하는 데 사용하지만 정확한 텍스트 괄호 사이에 할 수있다. 변수 역 참조 또는 매크로 대체 또는 다른 종류의 작업은 없습니다.

http://www.cplusplus.com/forum/general/2949/


비록 이것의 첫 번째 것만으로 충분할지라도, 이것은 실제로 최고가 될 것입니다 :)
pholat

잘 작동하지만 컴파일 오류를 피하려면 #ifndef stringify를 맨 위에 추가해야합니다. dgmz가 제안한 것처럼 enum 유형을 std :: string으로 변경했습니다.
astarakastara

5

여기에는 좋은 대답이 많이 있지만 일부 사람들은 내 도움이 될 것이라고 생각했습니다. 매크로를 정의하는 데 사용하는 인터페이스가 가능한 한 간단하기 때문에 마음에 듭니다. 추가 라이브러리를 포함 할 필요가 없으므로 편리합니다. 모두 C ++과 함께 제공되며 실제로 최신 버전이 필요하지 않습니다. 온라인으로 여러 곳에서 조각을 가져 와서 모든 점을 인정받을 수는 없지만 새로운 답변을 보증 할만큼 독창적이라고 생각합니다.

먼저 헤더 파일을 만드십시오 ... EnumMacros.h 또는 이와 유사한 것으로 부르고 이것을 넣으십시오.

// Search and remove whitespace from both ends of the string
static std::string TrimEnumString(const std::string &s)
{
    std::string::const_iterator it = s.begin();
    while (it != s.end() && isspace(*it)) { it++; }
    std::string::const_reverse_iterator rit = s.rbegin();
    while (rit.base() != it && isspace(*rit)) { rit++; }
    return std::string(it, rit.base());
}

static void SplitEnumArgs(const char* szArgs, std::string Array[], int nMax)
{
    std::stringstream ss(szArgs);
    std::string strSub;
    int nIdx = 0;
    while (ss.good() && (nIdx < nMax)) {
        getline(ss, strSub, ',');
        Array[nIdx] = TrimEnumString(strSub);
        nIdx++;
    }
};
// This will to define an enum that is wrapped in a namespace of the same name along with ToString(), FromString(), and COUNT
#define DECLARE_ENUM(ename, ...) \
    namespace ename { \
        enum ename { __VA_ARGS__, COUNT }; \
        static std::string _Strings[COUNT]; \
        static const char* ToString(ename e) { \
            if (_Strings[0].empty()) { SplitEnumArgs(#__VA_ARGS__, _Strings, COUNT); } \
            return _Strings[e].c_str(); \
        } \
        static ename FromString(const std::string& strEnum) { \
            if (_Strings[0].empty()) { SplitEnumArgs(#__VA_ARGS__, _Strings, COUNT); } \
            for (int i = 0; i < COUNT; i++) { if (_Strings[i] == strEnum) { return (ename)i; } } \
            return COUNT; \
        } \
    }

그런 다음 주 프로그램 에서이 작업을 수행 할 수 있습니다 ...

#include "EnumMacros.h"
DECLARE_ENUM(OsType, Windows, Linux, Apple)

void main() {
    OsType::OsType MyOs = OSType::Apple;
    printf("The value of '%s' is: %d of %d\n", OsType::ToString(MyOs), (int)OsType::FromString("Apple"), OsType::COUNT);
}

출력이되는 위치 >> 'Apple'의 값은 다음과 같습니다. 2 of 4

즐겨!


이 특정 접근 방식에서 내가 좋아하는 핵심은 열거 형에 값 설정 할당이 포함되어 있지 않은 한 일반적인 쉼표로 구분 된 열거 형 구문으로 작동한다는 것입니다. 제 경우에는 많은 수의 멤버가있는 기존 열거 형으로 작업해야 했으므로 부스트 방식보다 훨씬 쉽게 드롭 할 수 있습니다.
CuriousKea

4

열거 형이 이미 정의되어 있다고 가정하면 쌍의 배열을 만들 수 있습니다.

std::pair<QTask::TASK, QString> pairs [] = {
std::pair<OS_type, string>(Linux, "Linux"),
std::pair<OS_type, string>(Windows, "Windows"),
std::pair<OS_type, string>(Apple, "Apple"),
};

이제 맵을 만들 수 있습니다 :

std::map<OS_type, std::string> stdmap(pairs, pairs + sizeof(pairs) / sizeof(pairs[0]));

이제지도를 사용할 수 있습니다. 열거 형이 변경되면 배열 쌍에서 쌍을 추가 / 제거해야합니다 []. C ++에서 열거 형에서 문자열을 얻는 가장 우아한 방법이라고 생각했습니다.


2
여기에 Qt가 필요하지 않다는 공정한 의견 외에도, 또 다른 요점은 bimap이름을 구문 분석하고 열거 형 (예 : XML 파일)으로 바꾸려는 경우 Boost를 사용하고 싶을 수도 있습니다 .
Dmitri Nesteruk

4
일반적인 C ++ 질문에서 Qt 유형을 사용 해서는 안됩니다 .
벡터

3

C99의 경우가 P99_DECLARE_ENUM에서 P99 단순히 선언 할 수 있습니다 enum다음과 같이 :

P99_DECLARE_ENUM(color, red, green, blue);

color_getname(A)색상 이름이있는 문자열을 얻는 데 사용 합니다.


2

내 C ++ 코드는 다음과 같습니다.

/* 
 * File:   main.cpp
 * Author: y2k1234
 *
 * Created on June 14, 2013, 9:50 AM
 */

#include <cstdlib>
#include <stdio.h>

using namespace std;


#define MESSAGE_LIST(OPERATOR)                          \
                                       OPERATOR(MSG_A), \
                                       OPERATOR(MSG_B), \
                                       OPERATOR(MSG_C)
#define GET_LIST_VALUE_OPERATOR(msg)   ERROR_##msg##_VALUE
#define GET_LIST_SRTING_OPERATOR(msg)  "ERROR_"#msg"_NAME"

enum ErrorMessagesEnum
{
   MESSAGE_LIST(GET_LIST_VALUE_OPERATOR)
};
static const char* ErrorMessagesName[] = 
{
   MESSAGE_LIST(GET_LIST_SRTING_OPERATOR)
};

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

    int totalMessages = sizeof(ErrorMessagesName)/4;

    for (int i = 0; i < totalMessages; i++)
    {
        if (i == ERROR_MSG_A_VALUE)
        {
                printf ("ERROR_MSG_A_VALUE => [%d]=[%s]\n", i, ErrorMessagesName[i]);
        }
        else if (i == ERROR_MSG_B_VALUE)
        {
                printf ("ERROR_MSG_B_VALUE => [%d]=[%s]\n", i, ErrorMessagesName[i]);
        }
        else if (i == ERROR_MSG_C_VALUE)
        {
                printf ("ERROR_MSG_C_VALUE => [%d]=[%s]\n", i, ErrorMessagesName[i]);
        }
        else
        {
                printf ("??? => [%d]=[%s]\n", i, ErrorMessagesName[i]);
        }
    }   

    return 0;
}

Output:

ERROR_MSG_A_VALUE => [0]=[ERROR_MSG_A_NAME]

ERROR_MSG_B_VALUE => [1]=[ERROR_MSG_B_NAME]

ERROR_MSG_C_VALUE => [2]=[ERROR_MSG_C_NAME]

RUN SUCCESSFUL (total time: 126ms)

2

파티에 조금 늦었지만 C ++ 11 솔루션은 다음과 같습니다.

namespace std {
    template<> struct hash<enum_one> {
        std::size_t operator()(const enum_one & e) const {
            return static_cast<std::size_t>(e);
        }
    };
    template<> struct hash<enum_two> { //repeat for each enum type
        std::size_t operator()(const enum_two & e) const {
            return static_cast<std::size_t>(e);
        }
    };
}

const std::string & enum_name(const enum_one & e) {
    static const std::unordered_map<enum_one, const std::string> names = {
    #define v_name(n) {enum_one::n, std::string(#n)}
        v_name(value1),
        v_name(value2),
        v_name(value3)
    #undef v_name
    };
    return names.at(e);
}

const std::string & enum_name(const enum_two & e) { //repeat for each enum type
    .................
}

1
error: ‘hash’ is not a class template->#include <functional>
Ruggero Turra

2

필자가 선호하는 것은 반복적 인 타이핑과 이해하기 어려운 매크로를 최소화하고 일반적인 컴파일러 공간에 매크로 정의를 도입하지 않는 것입니다.

따라서 헤더 파일에서

enum Level{
        /**
        * zero reserved for internal use
        */
        verbose = 1,
        trace,
        debug,
        info,
        warn,
        fatal
    };

static Level readLevel(const char *);

그리고 cpp 구현은 다음과 같습니다.

 Logger::Level Logger::readLevel(const char *in) { 
 #  define MATCH(x) if (strcmp(in,#x) ==0) return x; 
    MATCH(verbose);
    MATCH(trace);
    MATCH(debug);
    MATCH(info);
    MATCH(warn);
    MATCH(fatal);
 # undef MATCH
    std::string s("No match for logging level ");
    s += in;
    throw new std::domain_error(s);
 }

매크로가 완료 되 자마자 #undef에 주목하십시오.


2

부스트를 사용하지 않는 내 솔루션 :

#ifndef EN2STR_HXX_
#define EN2STR_HXX_

#define MAKE_STRING_1(str     ) #str
#define MAKE_STRING_2(str, ...) #str, MAKE_STRING_1(__VA_ARGS__)
#define MAKE_STRING_3(str, ...) #str, MAKE_STRING_2(__VA_ARGS__)
#define MAKE_STRING_4(str, ...) #str, MAKE_STRING_3(__VA_ARGS__)
#define MAKE_STRING_5(str, ...) #str, MAKE_STRING_4(__VA_ARGS__)
#define MAKE_STRING_6(str, ...) #str, MAKE_STRING_5(__VA_ARGS__)
#define MAKE_STRING_7(str, ...) #str, MAKE_STRING_6(__VA_ARGS__)
#define MAKE_STRING_8(str, ...) #str, MAKE_STRING_7(__VA_ARGS__)

#define PRIMITIVE_CAT(a, b) a##b
#define MAKE_STRING(N, ...) PRIMITIVE_CAT(MAKE_STRING_, N)     (__VA_ARGS__)


#define PP_RSEQ_N() 8,7,6,5,4,3,2,1,0
#define PP_ARG_N(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define PP_NARG_(...) PP_ARG_N(__VA_ARGS__)
#define PP_NARG( ...) PP_NARG_(__VA_ARGS__,PP_RSEQ_N())

#define MAKE_ENUM(NAME, ...) enum NAME { __VA_ARGS__ };            \
  struct NAME##_str {                                              \
    static const char * get(const NAME et) {                       \
      static const char* NAME##Str[] = {                           \
                MAKE_STRING(PP_NARG(__VA_ARGS__), __VA_ARGS__) };  \
      return NAME##Str[et];                                        \
      }                                                            \
    };

#endif /* EN2STR_HXX_ */

사용 방법은 다음과 같습니다.

int main()
  {
  MAKE_ENUM(pippo, pp1, pp2, pp3,a,s,d);
  pippo c = d;
  cout << pippo_str::get(c) << "\n";
  return 0;
  }

2

전처리기를 사용하여 상대방에게 또 다른 늦음 :

 1  #define MY_ENUM_LIST \
 2      DEFINE_ENUM_ELEMENT(First) \
 3      DEFINE_ENUM_ELEMENT(Second) \
 4      DEFINE_ENUM_ELEMENT(Third) \
 5  
 6  //--------------------------------------
 7  #define DEFINE_ENUM_ELEMENT(name) , name
 8  enum MyEnum {
 9      Zeroth = 0
10      MY_ENUM_LIST
11  };
12  #undef DEFINE_ENUM_ELEMENT
13 
14  #define DEFINE_ENUM_ELEMENT(name) , #name
15  const char* MyEnumToString[] = {
16      "Zeroth"
17      MY_ENUM_LIST
18  };
19  #undef DEFINE_ENUM_ELEMENT
20
21  #define DEFINE_ENUM_ELEMENT(name) else if (strcmp(s, #name)==0) return name;
22  enum MyEnum StringToMyEnum(const char* s){
23      if (strcmp(s, "Zeroth")==0) return Zeroth;
24      MY_ENUM_LIST
25      return NULL;
26  }
27  #undef DEFINE_ENUM_ELEMENT

(단순히 이야기하기 쉽도록 줄 번호를 넣었습니다.) 줄 1-4는 열거 형의 요소를 정의하기 위해 편집 한 것입니다. (물건 목록을 만드는 매크로이기 때문에 "목록 매크로"라고 불렀습니다. @Lundin은 저에게 X- 매크로라는 잘 알려진 기술을 알려줍니다.)

7 행은 8-11 행의 실제 열거 선언을 채우기 위해 내부 매크로를 정의합니다. 12 행은 내부 매크로의 정의를 해제합니다 (컴파일러 경고를 침묵시키기 위해).

14 행은 열거 형 요소 이름의 문자열 버전을 만들기 위해 내부 매크로를 정의합니다. 그런 다음 15-18 행은 열거 형 값을 해당 문자열로 변환 할 수있는 배열을 생성합니다.

21-27 행은 문자열을 열거 형 값으로 변환하거나 문자열이 일치하지 않으면 NULL을 반환하는 함수를 생성합니다.

이것은 0 번째 요소를 처리하는 방식에 약간 번거 롭습니다. 나는 실제로 과거에 그 문제를 해결했습니다.

이 기술은 프리 프로세서 자체가 코드를 작성하도록 프로그래밍 될 수 있다고 생각하지 않는 사람들을 귀찮게한다. 가독성유지 관리 성의 차이를 강력하게 보여주고 있다고 생각합니다 . 코드를 읽기는 어렵지만 열거 형에 수백 개의 요소가있는 경우 요소를 추가, 제거 또는 재 배열 할 수 있으며 생성 된 코드에 오류가 없는지 확인할 수 있습니다.


"X 매크로"는 거의 모든 문제에 대한 우아한 솔루션이 아닙니다. 이 경우, 단순히 매크로 항목을 정의하는 것이 훨씬 더 읽을 것 #define TEST_1 hello #define TEST_2 world다음을 typedef enum { TEST_1, TEST_2 } test_t;다음 캐릭터 라인 화 매크로를 사용하는 문자열 룩업 테이블을 만들 : const char* table[]= { STRINGIFY(TEST_1), STRINGIFY(TEST_2), }; 비슷한 솔루션을 암시 복수 응답 이미 있습니다. 훨씬 더 읽기 쉽다.
Lundin

@Lundin : 나는 단지 1) 이것은 가장 원시적 인 C 컴파일러에서도 작동한다고 주장하고 2) 요소를 추가하거나 삭제하는 것은 한 줄 편집입니다.
Mike Dunlavey

내 자신의 답변을 게시했습니다 : stackoverflow.com/a/39877228/584518 . 잘하면 x 매크로 솔루션에서 가난한 영혼을 구할 수 있기를 바랍니다.
Lundin

1
나는 당신의 해결책을 사용했습니다. 나는 그것이 최고라고 생각합니다. C 구문은 여전히 ​​존재하므로 어떤 일이 발생하는지 이해하고 목록은 한 번만 정의됩니다. DEFINE_ENUM_ELEMENT에서 항목 뒤에 쉼표를 넣어서 0 번째 요소를 제거 할 수 있습니다.
발행

1

C 프리 프로세서 만 사용하는 Old Skool 방법 (gcc에서 광범위하게 사용됨)은 다음과 같습니다. 개별 데이터 구조를 생성하지만 순서를 일관성있게 유지해야하는 경우에 유용합니다. 물론 mylist.tbl의 항목은 훨씬 더 복잡한 것으로 확장 될 수 있습니다.

test.cpp :

enum {
#undef XX
#define XX(name, ignore) name ,
#include "mylist.tbl"
  LAST_ENUM
};

char * enum_names [] = {
#undef XX
#define XX(name, ignore) #name ,
#include "mylist.tbl"
   "LAST_ENUM"
};

그런 다음 mylist.tbl :

/*    A = enum                  */
/*    B = some associated value */
/*     A        B   */
  XX( enum_1 , 100)
  XX( enum_2 , 100 )
  XX( enum_3 , 200 )
  XX( enum_4 , 900 )
  XX( enum_5 , 500 )

1
이 기술을 x 매크로라고합니다!
Watusimoto

0

C ++에서 다음과 같이 :

enum OS_type{Linux, Apple, Windows};

std::string ToString( const OS_type v )
{
  const std::map< OS_type, std::string > lut =
    boost::assign::map_list_of( Linux, "Linux" )(Apple, "Apple )( Windows,"Windows");
  std::map< OS_type, std::string >::const_iterator it = lut.find( v );
  if ( lut.end() != it )
    return it->second;
  return "NOT FOUND";
}

0
#include <EnumString.h>

에서 http://www.codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C 이후

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 FORM f = ...
const std::string& str = EnumString< FORM >::From( f );

그 반대의 샘플 코드 :

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

0

제안 해 주셔서 감사합니다. 매우 유용했기 때문에 다른 방식으로 구현하여 어떤 방식 으로든 기여했습니다.

#include <iostream>
#include <boost/preprocessor.hpp>

using namespace std;

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

#define X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOENUM_IF(r, data, elem) \
    if (BOOST_PP_SEQ_TAIL(data) ==                                     \
            BOOST_PP_STRINGIZE(elem)) return                           \
            static_cast<int>(BOOST_PP_SEQ_HEAD(data)::elem); else

#define DEFINE_ENUM_WITH_STRING_CONVERSIONS(name, enumerators)         \
    enum class name {                                                  \
        BOOST_PP_SEQ_ENUM(enumerators)                                 \
    };                                                                 \
                                                                       \
    inline const char* ToString(name v)                                \
    {                                                                  \
        switch (v)                                                     \
        {                                                              \
            BOOST_PP_SEQ_FOR_EACH(                                     \
                X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOSTRING_CASE,   \
                name,                                                  \
                enumerators                                            \
            )                                                          \
            default: return "[Unknown " BOOST_PP_STRINGIZE(name) "]";  \
        }                                                              \
    }                                                                  \
                                                                       \
    inline int ToEnum(std::string s)                                   \
    {                                                                  \
        BOOST_PP_SEQ_FOR_EACH(                                         \
                X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOENUM_IF,       \
                (name)(s),                                             \
                enumerators                                            \
            )                                                          \
        return -1;                                                     \
    }


DEFINE_ENUM_WITH_STRING_CONVERSIONS(OS_type, (Linux)(Apple)(Windows));

int main(void)
{
    OS_type t = OS_type::Windows;

    cout << ToString(t) << " " << ToString(OS_type::Apple) << " " << ToString(OS_type::Linux) << endl;

    cout << ToEnum("Windows") << " " << ToEnum("Apple") << " " << ToEnum("Linux") << endl;

    return 0;
}

0

James의 답변을 확장하기 위해 누군가 int 값으로 enum define을 지원하는 예제 코드를 원합니다.이 요구 사항도 있으므로 다음과 같습니다.

첫 번째는 내부 사용 매크로이며 FOR_EACH에서 사용됩니다.

#define DEFINE_ENUM_WITH_STRING_CONVERSIONS_EXPAND_VALUE(r, data, elem)         \
    BOOST_PP_IF(                                                                \
        BOOST_PP_EQUAL(BOOST_PP_TUPLE_SIZE(elem), 2),                           \
        BOOST_PP_TUPLE_ELEM(0, elem) = BOOST_PP_TUPLE_ELEM(1, elem),            \
        BOOST_PP_TUPLE_ELEM(0, elem) ),

그리고 여기에 매크로 정의가 있습니다 :

#define DEFINE_ENUM_WITH_STRING_CONVERSIONS(name, enumerators)                  \
    enum name {                                                                 \
        BOOST_PP_SEQ_FOR_EACH(DEFINE_ENUM_WITH_STRING_CONVERSIONS_EXPAND_VALUE, \
                              0, enumerators) };

따라서 그것을 사용할 때 다음과 같이 쓸 수 있습니다 :

DEFINE_ENUM_WITH_STRING_CONVERSIONS(MyEnum,
    ((FIRST, 1))
    ((SECOND))
    ((MAX, SECOND)) )

다음으로 확장됩니다.

enum MyEnum
{
    FIRST = 1,
    SECOND,
    MAX = SECOND,
};

기본 아이디어는 모든 요소가 TUPLE 인 SEQ를 정의하는 것이므로 열거 형 멤버에 더하기 값을 넣을 수 있습니다. FOR_EACH 루프에서 항목 TUPLE 크기를 확인하십시오. 크기가 2 인 경우 코드를 KEY = VALUE로 확장하십시오. 그렇지 않으면 TUPLE의 첫 번째 요소 만 유지하십시오.

입력 SEQ는 실제로 TUPLE이므로 STRINGIZE 함수를 정의하려면 먼저 입력 열거자를 사전 처리해야 할 수도 있습니다. 다음은 작업을 수행하는 매크로입니다.

#define DEFINE_ENUM_WITH_STRING_CONVERSIONS_FIRST_ELEM(r, data, elem)           \
    BOOST_PP_TUPLE_ELEM(0, elem),

#define DEFINE_ENUM_WITH_STRING_CONVERSIONS_FIRST_ELEM_SEQ(enumerators)         \
    BOOST_PP_SEQ_SUBSEQ(                                                        \
        BOOST_PP_TUPLE_TO_SEQ(                                                  \
            (BOOST_PP_SEQ_FOR_EACH(                                             \
                DEFINE_ENUM_WITH_STRING_CONVERSIONS_FIRST_ELEM, 0, enumerators) \
            )),                                                                 \
            0,                                                                  \
            BOOST_PP_SEQ_SIZE(enumerators))

매크로 DEFINE_ENUM_WITH_STRING_CONVERSIONS_FIRST_ELEM_SEQ는 모든 TUPLE의 첫 번째 요소 만 유지하고 나중에 SEQ로 변환하고 이제 제임스 코드를 수정하면 모든 기능을 사용할 수 있습니다.

내 구현은 가장 간단한 구현이 아닐 수 있으므로 깨끗한 코드를 찾지 못하면 참조 용으로 사용하십시오.


0

순수한 표준 C의 깨끗하고 안전한 솔루션 :

#include <stdio.h>

#define STRF(x) #x
#define STRINGIFY(x) STRF(x)

/* list of enum constants */
#define TEST_0 hello
#define TEST_1 world

typedef enum
{
  TEST_0,
  TEST_1,
  TEST_N
} test_t;

const char* test_str[]=
{
  STRINGIFY(TEST_0),
  STRINGIFY(TEST_1),
};

int main()
{  
  _Static_assert(sizeof test_str / sizeof *test_str == TEST_N, 
                 "Incorrect number of items in enum or look-up table");

  printf("%d %s\n", hello, test_str[hello]);
  printf("%d %s\n", world, test_str[world]);
  test_t x = world;
  printf("%d %s\n", x, test_str[x]);

  return 0;
}

산출

0 hello
1 world
1 world

이론적 해석

핵심 문제를 해결할 때 "해당 문자열로 열거 형 상수가 있어야 함"은 합리적인 프로그래머가 다음 요구 사항을 충족시킵니다.

  • 코드 반복을 피하십시오 ( "DRY"원칙).
  • 열거 형 내부에 항목을 추가하거나 제거하더라도 코드는 확장 가능하고 유지 관리 가능하며 안전해야합니다.
  • 모든 코드는 읽기 쉽고 유지 관리하기 쉬운 고품질이어야합니다.

첫 번째 요구 사항 및 두 번째 요구 사항은 악명 높은 "x 매크로"트릭 또는 다른 형태의 매크로 마법과 같은 다양한 지저분한 매크로 솔루션으로 충족 될 수 있습니다. 이러한 솔루션의 문제점은 완전히 읽을 수없는 신비한 매크로를 남겨두고 위의 세 번째 요구 사항을 충족시키지 못한다는 것입니다.

여기에 필요한 유일한 것은 실제로 문자열 조회 테이블을 갖는 것인데, 열거 형 변수를 인덱스로 사용하여 액세스 할 수 있습니다. 이러한 테이블은 자연스럽게 열거 형과 직접적으로 일치해야하며 그 반대도 마찬가지입니다. 그중 하나가 업데이트되면 다른 하나도 업데이트해야합니다. 그렇지 않으면 작동하지 않습니다.


코드 설명

우리와 같은 열거 형이 있다고 가정하십시오.

typedef enum
{
  hello,
  world
} test_t;

이로 변경할 수 있습니다

#define TEST_0 hello
#define TEST_1 world

typedef enum
{
  TEST_0,
  TEST_1,
} test_t;

이러한 매크로 상수를 이제 다른 곳에서 사용하여 문자열 조회 테이블을 생성 할 수 있다는 이점이 있습니다. "stringify"매크로를 사용하여 전 처리기 상수를 문자열로 변환 할 수 있습니다.

#define STRF(x) #x
#define STRINGIFY(x) STRF(x)

const char* test_str[]=
{
  STRINGIFY(TEST_0),
  STRINGIFY(TEST_1),
};

그리고 그게 다야. 를 사용 hello하면 값이 0 인 열거 형 상수를 얻습니다.test_str[hello] 얻을 수 있습니다.이를 하여 문자열 "hello"를 얻습니다.

열거 형과 조회 테이블을 직접 대응 시키려면 동일한 양의 항목을 포함해야합니다. 누군가 코드를 유지 관리하고 조회 테이블이 아닌 열거 형만 변경하거나 그 반대의 경우에는이 방법이 작동하지 않습니다.

해결책은 열거 형에 포함 된 항목 수를 알려주는 것입니다. 이것을 위해 일반적으로 사용되는 C 트릭이 있습니다. 끝에 항목을 추가하면 열거 형에 몇 개의 항목이 있는지 알려주는 목적 만 채울 수 있습니다.

typedef enum
{
  TEST_0,
  TEST_1,
  TEST_N  // will have value 2, there are 2 enum constants in this enum
} test_t;

이제 컴파일 타임에 열거 형의 항목 수가 조회 테이블의 항목 수만큼, 바람직하게는 C11 정적 어설 션을 사용하여 확인합니다.

_Static_assert(sizeof test_str / sizeof *test_str == TEST_N, 
               "Incorrect number of items in enum or look-up table");

(누군가 공룡 컴파일러 사용을 주장하는 경우 이전 버전의 C 표준에서도 정적 어설 션을 만드는 추악하지만 완전한 기능의 방법이 있습니다. C ++의 경우 정적 어설 션도 지원합니다.)


참고로 C11에서는 stringify 매크로를 변경하여 더 높은 유형 안전성을 얻을 수 있습니다.

#define STRINGIFY(x) _Generic((x), int : STRF(x))

( int열거 상수가 실제로 형이기 때문에 int,하지 test_t)

이렇게하면 코드 STRINGIFY(random_stuff)가 컴파일되지 않습니다.


나는 당신이 말하는 것을 이해하지만 요점은 남아 있습니다. 일반적으로 예측 가능한 변경에는 최소한 한 줄의 편집 만 필요합니다. (나는 그것이 DRY의 이유라고 생각합니다.) 여기서 열거 형의 크기가 500과 같고 중간에 새 요소를 삽입 (또는 제거 / 이름 바꾸기 / 스왑)하려는 경우 몇 줄의 코드 줄을 사용해야합니까? 실수하지 않았는지 확인하기 위해 얼마나 많은 점검을해야합니까? 목록의 각 요소에 대해 균일 한 작업을 수행하는 다른 코드 조각이있을 수도 있습니다.
Mike Dunlavey

X-macros 라고 합니다 . 나는 몰랐다. 내가 보지 못하는 것은 사람들이 일반적으로 거부하는 것입니다.
Mike Dunlavey

@MikeDunlavey 열거 형의 크기에 관계없이 정확히 3 줄을 변경 #define해야합니다. 해당 행을 추가 할 때 문제가 발생하면 프로그램이 컴파일되지 않습니다. 내가 식별자에 추가 숫자는 결코 필수로, 당신은뿐만 아니라 쓸 수 있습니다 #define APPLES hello#define ORANGES world다음 typedef enum { APPES, ORANGES, TEST_N } test_t;등등.
Lundin

@MikeDunlavey X 매크로와 관련하여 인수는 함수와 유사한 매크로에 대한 인수와 동일합니다. 함수형 매크로에 대해 매우 유효한 비판을 많이 찾기 위해 멀리 볼 필요는 없습니다.
Lundin

0

내가 만든 것은 여기에서 본 것과이 사이트에서 비슷한 질문을 한 것입니다. Visual Studio 2013으로 만들었습니다. 다른 컴파일러로는 테스트하지 않았습니다.

우선 트릭을 수행 할 매크로 세트를 정의합니다.

// concatenation macros
#define CONCAT_(A, B) A ## B
#define CONCAT(A, B)  CONCAT_(A, B)

// generic expansion and stringification macros
#define EXPAND(X)           X
#define STRINGIFY(ARG)      #ARG
#define EXPANDSTRING(ARG)   STRINGIFY(ARG)        

// number of arguments macros
#define NUM_ARGS_(X100, X99, X98, X97, X96, X95, X94, X93, X92, X91, X90, X89, X88, X87, X86, X85, X84, X83, X82, X81, X80, X79, X78, X77, X76, X75, X74, X73, X72, X71, X70, X69, X68, X67, X66, X65, X64, X63, X62, X61, X60, X59, X58, X57, X56, X55, X54, X53, X52, X51, X50, X49, X48, X47, X46, X45, X44, X43, X42, X41, X40, X39, X38, X37, X36, X35, X34, X33, X32, X31, X30, X29, X28, X27, X26, X25, X24, X23, X22, X21, X20, X19, X18, X17, X16, X15, X14, X13, X12, X11, X10, X9, X8, X7, X6, X5, X4, X3, X2, X1, N, ...) N
#define NUM_ARGS(...) EXPAND(NUM_ARGS_(__VA_ARGS__, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1))

// argument extraction macros
#define FIRST_ARG(ARG, ...) ARG
#define REST_ARGS(ARG, ...) __VA_ARGS__

// arguments to strings macros
#define ARGS_STR__(N, ...)  ARGS_STR_##N(__VA_ARGS__)
#define ARGS_STR_(N, ...)   ARGS_STR__(N, __VA_ARGS__)
#define ARGS_STR(...)       ARGS_STR_(NUM_ARGS(__VA_ARGS__), __VA_ARGS__)

#define ARGS_STR_1(ARG)     EXPANDSTRING(ARG)
#define ARGS_STR_2(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_1(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_3(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_2(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_4(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_3(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_5(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_4(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_6(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_5(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_7(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_6(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_8(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_7(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_9(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_8(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_10(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_9(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_11(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_10(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_12(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_11(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_13(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_12(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_14(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_13(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_15(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_14(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_16(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_15(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_17(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_16(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_18(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_17(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_19(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_18(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_20(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_19(EXPAND(REST_ARGS(__VA_ARGS__)))
// expand until _100 or as much as you need

다음으로 열거 형 클래스와 문자열을 가져 오는 함수를 생성 할 단일 매크로를 정의하십시오.

#define ENUM(NAME, ...)                                                                                             \
    enum class NAME                                                                                                 \
    {                                                                                                               \
        __VA_ARGS__                                                                                                 \
    };                                                                                                              \
                                                                                                                    \
    static const std::array<std::string, NUM_ARGS(__VA_ARGS__)> CONCAT(NAME, Strings) = { ARGS_STR(__VA_ARGS__) };  \
                                                                                                                    \
    inline const std::string& ToString(NAME value)                                                                  \
    {                                                                                                               \
        return CONCAT(NAME, Strings)[static_cast<std::underlying_type<NAME>::type>(value)];                         \
    }                                                                                                               \
                                                                                                                    \
    inline std::ostream& operator<<(std::ostream& os, NAME value)                                                   \
    {                                                                                                               \
        os << ToString(value);                                                                                      \
        return os;                                                                                                  \
    }

이제 열거 형을 정의하고 문자열을 쉽게 사용할 수 있습니다. 당신이해야 할 일은 :

ENUM(MyEnumType, A, B, C);

다음 줄을 사용하여 테스트 할 수 있습니다.

int main()
{
    std::cout << MyEnumTypeStrings.size() << std::endl;

    std::cout << ToString(MyEnumType::A) << std::endl;
    std::cout << ToString(MyEnumType::B) << std::endl;
    std::cout << ToString(MyEnumType::C) << std::endl;

    std::cout << MyEnumType::A << std::endl;
    std::cout << MyEnumType::B << std::endl;
    std::cout << MyEnumType::C << std::endl;

    auto myVar = MyEnumType::A;
    std::cout << myVar << std::endl;
    myVar = MyEnumType::B;
    std::cout << myVar << std::endl;
    myVar = MyEnumType::C;
    std::cout << myVar << std::endl;

    return 0;
}

출력됩니다 :

3
A
B
C
A
B
C
A
B
C

나는 그것이 매우 깨끗하고 사용하기 쉽다고 생각합니다. 몇 가지 제한 사항이 있습니다.

  • 열거 형 멤버에는 값을 할당 할 수 없습니다.
  • 열거 형 멤버의 값은 인덱스로 사용되지만 모든 것이 단일 매크로에 정의되어 있기 때문에 괜찮습니다.
  • 클래스 내에서 열거 형을 정의하는 데 사용할 수 없습니다.

이 문제를 해결할 수 있다면. 나는 특히 그것을 사용하는 방법, 이것은 멋지고 마른 생각합니다. 장점 :

  • 사용하기 쉬운.
  • 런타임시 문자열 분할이 필요하지 않습니다.
  • 컴파일시 별도의 문자열을 사용할 수 있습니다.
  • 읽기 쉬운. 첫 번째 매크로 집합은 1 초가 더 필요할 수 있지만 실제로 그렇게 복잡하지는 않습니다.

0

이 문제에 대한 확실한 해결책은 다음과 같습니다.

#define RETURN_STR(val, e) {if (val == e) {return #e;}}

std::string conv_dxgi_format_to_string(int value) {
    RETURN_STR(value, DXGI_FORMAT_UNKNOWN);
    RETURN_STR(value, DXGI_FORMAT_R32G32B32A32_TYPELESS);
    RETURN_STR(value, DXGI_FORMAT_R32G32B32A32_FLOAT);
    RETURN_STR(value, DXGI_FORMAT_R32G32B32A32_UINT);
    RETURN_STR(value, DXGI_FORMAT_R32G32B32A32_SINT);
    RETURN_STR(value, DXGI_FORMAT_R32G32B32_TYPELESS);
    RETURN_STR(value, DXGI_FORMAT_R32G32B32_FLOAT);

    /* ... */

    return "<UNKNOWN>";
}

이 솔루션의 장점은 간단하고 복사 및 바꾸기를 통해 쉽게 기능을 구성 할 수 있다는 것입니다. 많은 변환을 수행하고 열거 형에 가능한 값이 너무 많은 경우이 솔루션은 CPU를 많이 사용하게됩니다.


0

나는 조금 늦었지만 여기에 g ++ 및 표준 라이브러리 만 사용하는 솔루션이 있습니다. 네임 스페이스 오염을 최소화하고 열거 형 이름을 다시 입력해야 할 필요성을 제거하려고했습니다.

헤더 파일 "my_enum.hpp"는 다음과 같습니다.

#include <cstring>

namespace ENUM_HELPERS{
    int replace_commas_and_spaces_with_null(char* string){
        int i, N;
        N = strlen(string);
        for(i=0; i<N; ++i){
            if( isspace(string[i]) || string[i] == ','){
                string[i]='\0';
            }
        }
        return(N);
    }

    int count_words_null_delim(char* string, int tot_N){
        int i;
        int j=0;
        char last = '\0';
        for(i=0;i<tot_N;++i){
            if((last == '\0') && (string[i]!='\0')){
                ++j;
            }
            last = string[i];
        }
        return(j);
    }

    int get_null_word_offsets(char* string, int tot_N, int current_w){
        int i;
        int j=0;
        char last = '\0';
        for(i=0; i<tot_N; ++i){
            if((last=='\0') && (string[i]!='\0')){
                if(j == current_w){
                    return(i);
                }
                ++j;
            }
            last = string[i];
        }
        return(tot_N); //null value for offset
    }

    int find_offsets(int* offsets, char* string, int tot_N, int N_words){
        int i;
        for(i=0; i<N_words; ++i){
            offsets[i] = get_null_word_offsets(string, tot_N, i);
        }
        return(0);
    }
}


#define MAKE_ENUM(NAME, ...)                                            \
namespace NAME{                                                         \
    enum ENUM {__VA_ARGS__};                                            \
    char name_holder[] = #__VA_ARGS__;                                  \
    int name_holder_N =                                                 \
        ENUM_HELPERS::replace_commas_and_spaces_with_null(name_holder); \
    int N =                                                             \
        ENUM_HELPERS::count_words_null_delim(                           \
            name_holder, name_holder_N);                                \
    int offsets[] = {__VA_ARGS__};                                      \
    int ZERO =                                                          \
        ENUM_HELPERS::find_offsets(                                     \
            offsets, name_holder, name_holder_N, N);                    \
    char* tostring(int i){                                              \
       return(&name_holder[offsets[i]]);                                \
    }                                                                   \
}

사용 예 :

#include <cstdio>
#include "my_enum.hpp"

MAKE_ENUM(Planets, MERCURY, VENUS, EARTH, MARS)

int main(int argc, char** argv){    
    Planets::ENUM a_planet = Planets::EARTH;
    printf("%s\n", Planets::tostring(Planets::MERCURY));
    printf("%s\n", Planets::tostring(a_planet));
}

출력됩니다 :

MERCURY
EARTH

모든 것을 한 번만 정의하면 네임 스페이스가 오염되어서는 안되며 모든 계산은 한 번만 수행됩니다 (나머지는 단지 조회). 그러나 열거 형 클래스의 형식 안전성을 얻지 못하고 (여전히 짧은 정수 일뿐입니다) 열거 형에 값을 할당 할 수 없으며 네임 스페이스를 정의 할 수있는 곳 (예 : 전역)에 열거 형을 정의해야합니다.

나는 이것의 성능이 얼마나 좋은지 또는 좋은 아이디어인지 확실하지 않습니다 (C ++ 전에 C를 배웠으므로 뇌는 여전히 그렇게 작동합니다). 이것이 나쁜 생각 인 이유를 아는 사람은 언제든지 지적하십시오.


0

2017 년이지만 문제는 여전히 살아 있습니다

또 다른 방법 :

#include <iostream>

#define ERROR_VALUES \
ERROR_VALUE(NO_ERROR, 0, "OK") \
ERROR_VALUE(FILE_NOT_FOUND, 1, "Not found") \
ERROR_VALUE(LABEL_UNINITIALISED, 2, "Uninitialized usage")

enum Error
{
#define ERROR_VALUE(NAME, VALUE, TEXT) NAME = VALUE,
    ERROR_VALUES
#undef ERROR_VALUE
};

inline std::ostream& operator<<(std::ostream& os, Error err)
{
    int errVal = static_cast<int>(err);
    switch (err)
    {
#define ERROR_VALUE(NAME, VALUE, TEXT) case NAME: return os << "[" << errVal << "]" << #NAME << ", " << TEXT;
    ERROR_VALUES
#undef ERROR_VALUE
    default:
        // If the error value isn't found (shouldn't happen)
        return os << errVal;
    }
}

int main() {
    std::cout << "Error: " << NO_ERROR << std::endl;
    std::cout << "Error: " << FILE_NOT_FOUND << std::endl;
    std::cout << "Error: " << LABEL_UNINITIALISED << std::endl;
    return 0;
}

출력 :

Error: [0]NO_ERROR, OK
Error: [1]FILE_NOT_FOUND, Not found
Error: [2]LABEL_UNINITIALISED, Uninitialized usage

0
#pragma once

#include <string>
#include <vector>
#include <sstream>
#include <algorithm>

namespace StringifyEnum
{
static std::string TrimEnumString(const std::string &s)
{
    std::string::const_iterator it = s.begin();
    while (it != s.end() && isspace(*it)) { it++; }
    std::string::const_reverse_iterator rit = s.rbegin();
    while (rit.base() != it && isspace(*rit)) { ++rit; }
    return std::string(it, rit.base());
}

static std::vector<std::string> SplitEnumArgs(const char* szArgs, int     nMax)
{
    std::vector<std::string> enums;
    std::stringstream ss(szArgs);
    std::string strSub;
    int nIdx = 0;
    while (ss.good() && (nIdx < nMax)) {
        getline(ss, strSub, ',');
        enums.push_back(StringifyEnum::TrimEnumString(strSub));
        ++nIdx;
    }
    return std::move(enums);
}    
}

#define DECLARE_ENUM_SEQ(ename, n, ...) \
    enum class ename { __VA_ARGS__ }; \
    const int MAX_NUMBER_OF_##ename(n); \
    static std::vector<std::string> ename##Strings = StringifyEnum::SplitEnumArgs(#__VA_ARGS__, MAX_NUMBER_OF_##ename); \
    inline static std::string ename##ToString(ename e) { \
        return ename##Strings.at((int)e); \
    } \
    inline static ename StringTo##ename(const std::string& en) { \
        const auto it = std::find(ename##Strings.begin(), ename##Strings.end(), en); \
        if (it != ename##Strings.end()) \
            return (ename) std::distance(ename##Strings.begin(), it); \
        throw std::runtime_error("Could not resolve string enum value");     \
    }

이것은 정교한 클래스 확장 열거 형 버전입니다 ... 제공 된 것 이외의 다른 열거 형 값을 추가하지 않습니다.

사용법 : DECLARE_ENUM_SEQ (카메라 모드, (3), 비행, FirstPerson, PerspectiveCorrect)


0

나는 양방향으로 작동하기 위해 이것을 필요로했으며 종종 열거 형을 포함하는 클래스에 포함시키기 때문에 James McNellis의 솔루션으로 시작 하여이 답변의 맨 위에 있지만 솔루션을 만들었습니다. 또한 열거 형이 아닌 열거 형 클래스를 선호하므로 대답이 다소 복잡합니다.

#define X_DEFINE_ENUMERATION(r, datatype, elem) case datatype::elem : return BOOST_PP_STRINGIZE(elem);

// The data portion of the FOR_EACH should be (variable type)(value)
#define X_DEFINE_ENUMERATION2(r, dataseq, elem) \
    if (BOOST_PP_SEQ_ELEM(1, dataseq) == BOOST_PP_STRINGIZE(elem) ) return BOOST_PP_SEQ_ELEM(0, dataseq)::elem;

#define DEFINE_ENUMERATION_MASTER(modifier, name, toFunctionName, enumerators)    \
    enum class name {                                                         \
        Undefined,                                                            \
        BOOST_PP_SEQ_ENUM(enumerators)                                        \
    };                                                                        \
                                                                              \
    modifier const char* ToString(const name & v)                               \
    {                                                                         \
        switch (v)                                                            \
        {                                                                     \
            BOOST_PP_SEQ_FOR_EACH(                                            \
                X_DEFINE_ENUMERATION,                                         \
                name,                                                         \
                enumerators                                                   \
            )                                                                 \
            default: return "[Unknown " BOOST_PP_STRINGIZE(name) "]";         \
        }                                                                     \
    }                                                                         \
                                                                              \
    modifier const name toFunctionName(const std::string & value)               \
    {                                                                         \
        BOOST_PP_SEQ_FOR_EACH(                                                \
            X_DEFINE_ENUMERATION2,                                            \
            (name)(value),                                                    \
            enumerators                                                       \
        )                                                                     \
        return name::Undefined;                                               \
    }

#define DEFINE_ENUMERATION(name, toFunctionName, enumerators)                 \
    DEFINE_ENUMERATION_MASTER(inline, name, toFunctionName, enumerators)

#define DEFINE_ENUMERATION_INSIDE_CLASS(name, toFunctionName, enumerators)                 \
    DEFINE_ENUMERATION_MASTER(static, name, toFunctionName, enumerators)

클래스 내에서 사용하려면 다음과 같이 할 수 있습니다.

class ComponentStatus {
public:
    /** This is a simple bad, iffy, and good status. See other places for greater details. */
    DEFINE_ENUMERATION_INSIDE_CLASS(Status, toStatus, (RED)(YELLOW)(GREEN)
}

그리고 CppUnit 테스트를 작성하여 사용 방법을 보여줍니다.

void
ComponentStatusTest::testSimple() {
    ComponentStatus::Status value = ComponentStatus::Status::RED;

    const char * valueStr = ComponentStatus::ToString(value);

    ComponentStatus::Status convertedValue = ComponentStatus::toStatus(string(valueStr));

    CPPUNIT_ASSERT_EQUAL_MESSAGE("Incorrect conversion to a string.", (const char *)"RED", valueStr);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Incorrect conversion back from a string.", convertedValue, value);
}

DEFINE_ENUMERATION(Status, toStatus, (RED)(YELLOW)(GREEN))

void
ComponentStatusTest::testOutside() {
    Status value = Status::RED;

    const char * valueStr = ToString(value);

    Status convertedValue = toStatus(string(valueStr));

    CPPUNIT_ASSERT_EQUAL_MESSAGE("Incorrect conversion to a string.", (const char *)"RED", valueStr);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Incorrect conversion back from a string.", convertedValue, value);
}

사용할 매크로를 DEFINE_ENUMERATION 또는 DEFINE_ENUMERATION_INSIDE_CLASS 중에서 선택해야합니다. ComponentStatus :: Status를 정의 할 때 후자를 사용했지만 Status를 정의 할 때 전자를 사용했습니다. 차이점은 간단합니다. 클래스 내에서 to / from 메소드 접두어를 "정적"으로 지정하고 클래스에 없으면 "인라인"을 사용합니다. 사소한 차이점이지만 필요합니다.

불행히도, 나는 이것을 피할 수있는 깨끗한 방법이 있다고 생각하지 않습니다.

const char * valueStr = ComponentStatus::ToString(value);

클래스 정의 후에 클래스 메소드에 연결되는 인라인 메소드를 수동으로 작성할 수 있지만 다음과 같습니다.

inline const char * toString(const ComponentStatus::Status value) { return ComponentStatus::ToString(value); }

0

부스트를 사용하지 않는 내 자신의 대답-무거운 마법을 정의하지 않고 내 자신의 접근 방식을 사용하면이 솔루션은 특정 열거 형 값을 정의 할 수 없다는 한계가 있습니다.

#pragma once
#include <string>

template <class Enum>
class EnumReflect
{
public:
    static const char* getEnums() { return ""; }
};

#define DECLARE_ENUM(name, ...)                                         \
    enum name { __VA_ARGS__ };                                          \
    template <>                                                         \
    class EnumReflect<##name> {                                         \
    public:                                                             \
        static const char* getEnums() { return #__VA_ARGS__; }          \
    };

/*
    Basic usage:

    Declare enumeration:

DECLARE_ENUM( enumName,

    enumValue1,
    enumValue2,
    enumValue3,

    // comment
    enumValue4
);

    Conversion logic:

    From enumeration to string:

        printf( EnumToString(enumValue3).c_str() );

    From string to enumeration:

       enumName value;

       if( !StringToEnum("enumValue4", value) )
            printf("Conversion failed...");

    WARNING: At the moment assigning enum value to specific number is not supported.
*/

//
//  Converts enumeration to string, if not found - empty string is returned.
//
template <class T>
std::string EnumToString(T t)
{
    const char* enums = EnumReflect<T>::getEnums();
    const char *token, *next = enums - 1;
    int id = (int)t;

    do
    {
        token = next + 1;
        if (*token == ' ') token++;
        next = strchr(token, ',');
        if (!next) next = token + strlen(token);

        if (id == 0)
            return std::string(token, next);
        id--;
    } while (*next != 0);

    return std::string();
}

//
//  Converts string to enumeration, if not found - false is returned.
//
template <class T>
bool StringToEnum(const char* enumName, T& t)
{
    const char* enums = EnumReflect<T>::getEnums();
    const char *token, *next = enums - 1;
    int id = 0;

    do
    {
        token = next + 1;
        if (*token == ' ') token++;
        next = strchr(token, ',');
        if (!next) next = token + strlen(token);

        if (strncmp(token, enumName, next - token) == 0)
        {
            t = (T)id;
            return true;
        }

        id++;
    } while (*next != 0);

    return false;
}

최신 버전은 github에서 찾을 수 있습니다 :

https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/EnumReflect.h


0

이것에 대한 다른 많은 답변이 있지만 더 나은 방법은 C ++ 17 기능을 사용하고 constexpr을 사용하여 컴파일 타임에 번역을 수행하는 것입니다. 이것은 안전한 유형이며 매크로를 망칠 필요가 없습니다. 아래를보십시오 :

//enum.hpp
#include <array>
#include <string_view>

namespace Enum
{

template <class ENUM_TYPE, size_t SIZE>
constexpr ENUM_TYPE findKey(const char * value, std::array<std::pair<ENUM_TYPE, const char *>, SIZE> map, size_t index = -1)
{
    index = (index == -1) ? map.size() : index;
    return
        (index == 0) ? throw "Value not in map":
        (std::string_view(map[index - 1].second) == value) ? map[index- 1].first:
        findKey(value, map, index - 1);
};

template <class ENUM_TYPE, size_t SIZE>
constexpr const char * findValue(ENUM_TYPE key, std::array<std::pair<ENUM_TYPE, const char *>, SIZE> map, size_t index = -1)
{
    index = (index == -1) ? map.size() : index;
    return
        (index == 0) ? throw "Key not in map":
        (map[index - 1].first == key) ? map[index- 1].second:
        findValue(key, map, index - 1);
};

}

//test_enum.hpp
#include "enum.hpp"

namespace TestEnum
{
    enum class Fields
    {
        Test1,
        Test2,
        Test3,
        //This has to be at the end
        NUMBER_OF_FIELDS
    };

    constexpr std::array<std::pair<Fields, const char *>, (size_t)Fields::NUMBER_OF_FIELDS> GetMap()
    {
        std::array<std::pair<Fields, const char *>, (size_t)Fields::NUMBER_OF_FIELDS> map =
        {
            {
                    {Fields::Test1, "Test1"},
                    {Fields::Test2, "Test2"},
                    {Fields::Test3, "Test3"},
            }
        };
        return map;
    };

    constexpr Fields StringToEnum(const char * value)
    {
        return Enum::findKey(value, GetMap());
    }

    constexpr const char * EnumToString(Fields key)
    {
        return Enum::findValue(key, GetMap());
    }

}

그런 다음 컴파일 타임에 문자열 키 오류가 감지되도록 쉽게 사용할 수 있습니다.

#include "test_enum.hpp"

int main()
{
    auto constexpr a = TestEnum::StringToEnum("Test2"); //a = TestEnum::Fields::Test2
    auto constexpr b = TestEnum::EnumToString(TestEnum::Fields::Test1); //b = "Test1"
    auto constexpr c = TestEnum::StringToEnum("AnyStringNotInTheMap"); //compile time failure
    return 0;
}

코드는 다른 솔루션보다 더 장황하지만 컴파일 타임에 Enum to String 변환 및 String to Enum 변환을 쉽게 수행하고 유형 오류를 감지 할 수 있습니다. 미래의 C ++ 20 기능 중 일부는 아마도 조금 더 단순화 될 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.