programing

열거형 변수를 C에서 문자열로 사용하는 쉬운 방법?

newsource 2022. 8. 11. 22:58

열거형 변수를 C에서 문자열로 사용하는 쉬운 방법?

제가 하려는 일은 다음과 같습니다.

typedef enum { ONE, TWO, THREE } Numbers;

다음과 같은 스위치 케이스를 실행하는 함수를 작성하려고 합니다.

char num_str[10];
int process_numbers_str(Numbers num) {
  switch(num) {
    case ONE:
    case TWO:
    case THREE:
    {
      strcpy(num_str, num); //some way to get the symbolic constant name in here?
    } break;
    default:
      return 0; //no match
  return 1;
}

모든 경우에 정의하는 것이 아니라 위에서와 같이 열거 변수를 사용하여 설정하는 방법이 있습니까?

여기서 Making something a something a cidentifier and string?의 기법을 사용할 수 있습니다.

이러한 프리프로세서의 경우와 마찬가지로 프리프로세서 부분을 쓰고 이해하는 것은 어려울 수 있으며, 매크로를 다른 매크로에 전달하고 # 및 ## 연산자를 사용하는 것도 포함되지만, 사용법은 매우 간단합니다.이 스타일은 같은 목록을 두 번 유지하는 것이 매우 번거로울 수 있는 긴 에넘에 매우 유용하다고 생각합니다.

공장 코드 - 일반적으로 헤더에 숨겨져 있는 한 번만 입력:

EnumFactory 입니다.h:

// expansion macro for enum value definition
#define ENUM_VALUE(name,assign) name assign,

// expansion macro for enum to string conversion
#define ENUM_CASE(name,assign) case name: return #name;

// expansion macro for string to enum conversion
#define ENUM_STRCMP(name,assign) if (!strcmp(str,#name)) return name;

/// declare the access function and define enum values
#define DECLARE_ENUM(EnumType,ENUM_DEF) \
  enum EnumType { \
    ENUM_DEF(ENUM_VALUE) \
  }; \
  const char *GetString(EnumType dummy); \
  EnumType Get##EnumType##Value(const char *string); \

/// define the access function names
#define DEFINE_ENUM(EnumType,ENUM_DEF) \
  const char *GetString(EnumType value) \
  { \
    switch(value) \
    { \
      ENUM_DEF(ENUM_CASE) \
      default: return ""; /* handle input error */ \
    } \
  } \
  EnumType Get##EnumType##Value(const char *str) \
  { \
    ENUM_DEF(ENUM_STRCMP) \
    return (EnumType)0; /* handle input error */ \
  } \

공장 출하 시 사용

someEnum을 클릭합니다.h:

#include "enumFactory.h"
#define SOME_ENUM(XX) \
    XX(FirstValue,) \
    XX(SecondValue,) \
    XX(SomeOtherValue,=50) \
    XX(OneMoreValue,=100) \

DECLARE_ENUM(SomeEnum,SOME_ENUM)

someEnum.cpp:

#include "someEnum.h"
DEFINE_ENUM(SomeEnum,SOME_ENUM)

이 기술은 XX 매크로가 더 많은 인수를 받아들이도록 쉽게 확장할 수 있습니다.또, 이 샘플에 기재되어 있는 3개의 매크로와 같이, XX를 대체할 매크로도 준비할 수 있습니다.

#include/#define/#definue를 사용한 X-Macros와의 비교

이것은 X-Macros가 언급한 것과 비슷하지만, 이 솔루션은 #definitioning을 필요로 하지 않는다는 점에서 더 우아하다고 생각합니다.이것에 의해, 공장내에 있는 복잡한 것을 숨길 수 있습니다.헤더 파일은, 새로운 enum을 정의할 필요가 있을 때는, 전혀 건드리지 않는 것입니다.니션은 훨씬 짧고 깨끗합니다.

몇 가지 확실한 답을 가지고 계시겠지만, C 프리프로세서의 # 연산자에 대해 알고 계십니까?

다음과 같은 작업을 수행할 수 있습니다.

#define MACROSTR(k) #k

typedef enum {
    kZero,
    kOne,
    kTwo,
    kThree
} kConst;

static char *kConstStr[] = {
    MACROSTR(kZero),
    MACROSTR(kOne),
    MACROSTR(kTwo),
    MACROSTR(kThree)
};

static void kConstPrinter(kConst k)
{
    printf("%s", kConstStr[k]);
}

내장된 솔루션은 없습니다.하는 것입니다.char*여기서 Enum의 int 값은 해당 Enum의 설명 이름을 포함하는 문자열로 인덱싱됩니다.enum) (번호 0은 0으로 시작하지 않습니다.)int매핑은 어레이 기반 매핑을 실행할 수 없을 정도로 높기 때문에 대신 해시 테이블을 사용할 수 있습니다.

C++ enum을 문자열로 변환해 보십시오.코멘트에는, 열거 항목의 값이 임의의 경우, 문제를 해결하는 개량점이 있습니다.

#define stringify( name ) # name

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

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

이 방법에 대한 자세한 설명

신규 고객을 위한 프리프로세서 지시 요령

// Define your enumeration like this (in say numbers.h);
ENUM_BEGIN( Numbers )
    ENUM(ONE),
    ENUM(TWO),
    ENUM(FOUR)
ENUM_END( Numbers )

// The macros are defined in a more fundamental .h file (say defs.h);
#define ENUM_BEGIN(typ) enum typ {
#define ENUM(nam) nam
#define ENUM_END(typ) };

// Now in one and only one .c file, redefine the ENUM macros and reinclude
//  the numbers.h file to build a string table
#undef ENUM_BEGIN
#undef ENUM
#undef ENUM_END
#define ENUM_BEGIN(typ) const char * typ ## _name_table [] = {
#define ENUM(nam) #nam
#define ENUM_END(typ) };
#undef NUMBERS_H_INCLUDED   // whatever you need to do to enable reinclusion
#include "numbers.h"

// Now you can do exactly what you want to do, with no retyping, and for any
//  number of enumerated types defined with the ENUM macro family
//  Your code follows;
char num_str[10];
int process_numbers_str(Numbers num) {
  switch(num) {
    case ONE:
    case TWO:
    case THREE:
    {
      strcpy(num_str, Numbers_name_table[num]); // eg TWO -> "TWO"
    } break;
    default:
      return 0; //no match
  return 1;
}

// Sweet no ? After being frustrated by this for years, I finally came up
//  with this solution for my most recent project and plan to reuse the idea
//  forever

여기에 몇 가지 기술을 접목함으로써 가장 간단한 형태를 생각해냈습니다.

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

C 또는 C++는 이 기능을 제공하지 않지만, 자주 필요했습니다.

비희소 에넘에 가장 적합하지만 다음 코드가 작동합니다.

typedef enum { ONE, TWO, THREE } Numbers;
char *strNumbers[] = {"one","two","three"};
printf ("Value for TWO is %s\n",strNumbers[TWO]);

'비희소'는 그런 타입이 아니다.

typedef enum { ONE, FOUR_THOUSAND = 4000 } Numbers;

그 안에 큰 공백이 있기 때문에.

이 방법의 장점은 enum과 스트링의 정의를 서로 가까이 배치한다는 것입니다. 함수에 switch 문이 있으면 그 정의가 시작됩니다.즉, 한쪽을 다른 한쪽 없이 변경할 가능성이 적다는 것을 의미합니다.

반드시 방법이 있습니다.X() 매크로를 사용합니다.이러한 매크로는 C 프리프로세서를 사용하여 소스 데이터 목록에서 Enum, 배열 및 코드 블록을 구성합니다.X() 매크로를 포함하는 #define에는 새 항목만 추가하면 됩니다.switch 문이 자동으로 확장됩니다.

예는 다음과 같이 기술할 수 있습니다.

 // Source data -- Enum, String
 #define X_NUMBERS \
    X(ONE,   "one") \
    X(TWO,   "two") \
    X(THREE, "three")

 ...

 // Use preprocessor to create the Enum
 typedef enum {
  #define X(Enum, String)       Enum,
   X_NUMBERS
  #undef X
 } Numbers;

 ...

 // Use Preprocessor to expand data into switch statement cases
 switch(num)
 {
 #define X(Enum, String) \
     case Enum:  strcpy(num_str, String); break;
 X_NUMBERS
 #undef X

     default: return 0; break;
 }
 return 1;

보다 효율적인 방법(X 매크로를 사용하여 문자열 배열 및 열거 인덱스를 작성하는 방법)이 있지만, 이것은 가장 간단한 데모입니다.

gcc를 사용하는 경우 다음을 사용할 수 있습니다.

const char * enum_to_string_map[]={ [enum1]='string1', [enum2]='string2'};

그럼 전화나 해

enum_to_string_map[enum1]

KISS. 에넘으로 다른 스위치/케이스도 모두 할 텐데 인쇄가 왜 다를까요?인쇄 루틴에서 케이스를 잊어버리는 것은, 케이스를 잊어버릴 수 있는 장소가 100개 정도 있는 것을 생각하면, 큰 문제가 되지 않습니다.-Wall을 컴파일하면 대소문자 일치가 경고됩니다."default"를 사용하지 마십시오.이것에 의해, 스윗치가 완전하게 되어 경고가 표시되지 않게 됩니다.대신 스위치를 종료하고 디폴트 케이스에 대처합니다.

const char *myenum_str(myenum e)
{
    switch(e) {
    case ONE: return "one";
    case TWO: return "two";
    }
    return "invalid";
}

boost:: 프리프로세서를 사용하면 다음과 같은 우아한 솔루션을 구현할 수 있습니다.

1단계: 헤더 파일 포함:

#include "EnumUtilities.h"

스텝 2: 다음 구문을 사용하여 열거 개체를 선언합니다.

MakeEnum( TestData,
         (x)
         (y)
         (z)
         );

3단계: 데이터 사용:

요소 수 가져오기:

td::cout << "Number of Elements: " << TestDataCount << std::endl;

연관된 문자열을 가져오는 중:

std::cout << "Value of " << TestData2String(x) << " is " << x << std::endl;
std::cout << "Value of " << TestData2String(y) << " is " << y << std::endl;
std::cout << "Value of " << TestData2String(z) << " is " << z << std::endl;

연관된 문자열에서 열거값 가져오기:

std::cout << "Value of x is " << TestData2Enum("x") << std::endl;
std::cout << "Value of y is " << TestData2Enum("y") << std::endl;
std::cout << "Value of z is " << TestData2Enum("z") << std::endl;

깔끔하고 컴팩트해 보여 추가 파일이 필요 없습니다.EnumUtilities.h에 기재한 코드는 다음과 같습니다.

#include <boost/preprocessor/seq/for_each.hpp>
#include <string>

#define REALLY_MAKE_STRING(x) #x
#define MAKE_STRING(x) REALLY_MAKE_STRING(x)
#define MACRO1(r, data, elem) elem,
#define MACRO1_STRING(r, data, elem)    case elem: return REALLY_MAKE_STRING(elem);
#define MACRO1_ENUM(r, data, elem)      if (REALLY_MAKE_STRING(elem) == eStrEl) return elem;


#define MakeEnum(eName, SEQ) \
    enum eName { BOOST_PP_SEQ_FOR_EACH(MACRO1, , SEQ) \
    last_##eName##_enum}; \
    const int eName##Count = BOOST_PP_SEQ_SIZE(SEQ); \
    static std::string eName##2String(const enum eName eel) \
    { \
        switch (eel) \
        { \
        BOOST_PP_SEQ_FOR_EACH(MACRO1_STRING, , SEQ) \
        default: return "Unknown enumerator value."; \
        }; \
    }; \
    static enum eName eName##2Enum(const std::string eStrEl) \
    { \
        BOOST_PP_SEQ_FOR_EACH(MACRO1_ENUM, , SEQ) \
        return (enum eName)0; \
    };

boost:: preprocessor 등 몇 가지 제한이 있습니다.이 경우 상수 목록은 64개 요소보다 클 수 없습니다.

같은 논리에 따라 sparse enum을 작성하는 것도 생각할 수 있습니다.

#define EnumName(Tuple)                 BOOST_PP_TUPLE_ELEM(2, 0, Tuple)
#define EnumValue(Tuple)                BOOST_PP_TUPLE_ELEM(2, 1, Tuple)
#define MACRO2(r, data, elem)           EnumName(elem) EnumValue(elem),
#define MACRO2_STRING(r, data, elem)    case EnumName(elem): return BOOST_PP_STRINGIZE(EnumName(elem));

#define MakeEnumEx(eName, SEQ) \
    enum eName { \
    BOOST_PP_SEQ_FOR_EACH(MACRO2, _, SEQ) \
    last_##eName##_enum }; \
    const int eName##Count = BOOST_PP_SEQ_SIZE(SEQ); \
    static std::string eName##2String(const enum eName eel) \
    { \
        switch (eel) \
        { \
        BOOST_PP_SEQ_FOR_EACH(MACRO2_STRING, _, SEQ) \
        default: return "Unknown enumerator value."; \
        }; \
    };  

이 경우 구문은 다음과 같습니다.

MakeEnumEx(TestEnum,
           ((x,))
           ((y,=1000))
           ((z,))
           );

사용방법은 위와 비슷합니다(eName##2Enum 함수는 제외).이러한 구문은 이전 구문에서 추론할 수 있습니다.

Mac과 Linux에서 테스트했습니다만, boost:: 프리프로세서가 완전하게 휴대할 수 없는 경우가 있습니다.

Mu Dynamics Research Labs - Blog Archive에서 아이디어를 확인하십시오.저는 올해 초에 이것을 발견했습니다.-어디서 접하게 되었는지 정확한 문맥을 잊어버렸습니다.-그리고 이 코드에 맞게 수정했습니다.우리는 전면에 E를 추가하는 것의 장점에 대해 토론할 수 있다.그것은 일반적인 해결책의 일부가 아니라, 특정의 문제에 적용할 수 있다.나는 이것을 내 'vignetts' 폴더에 보관했다 - 나중에 필요할 때를 대비해 흥미로운 코드 조각을 보관한다.아쉽게도 저는 그 당시 이 아이디어가 어디서 나왔는지 메모하지 못했습니다.

헤더: 페이스트1.h

/*
@(#)File:           $RCSfile: paste1.h,v $
@(#)Version:        $Revision: 1.1 $
@(#)Last changed:   $Date: 2008/05/17 21:38:05 $
@(#)Purpose:        Automated Token Pasting
*/

#ifndef JLSS_ID_PASTE_H
#define JLSS_ID_PASTE_H

/*
 * Common case when someone just includes this file.  In this case,
 * they just get the various E* tokens as good old enums.
 */
#if !defined(ETYPE)
#define ETYPE(val, desc) E##val,
#define ETYPE_ENUM
enum {
#endif /* ETYPE */

   ETYPE(PERM,  "Operation not permitted")
   ETYPE(NOENT, "No such file or directory")
   ETYPE(SRCH,  "No such process")
   ETYPE(INTR,  "Interrupted system call")
   ETYPE(IO,    "I/O error")
   ETYPE(NXIO,  "No such device or address")
   ETYPE(2BIG,  "Arg list too long")

/*
 * Close up the enum block in the common case of someone including
 * this file.
 */
#if defined(ETYPE_ENUM)
#undef ETYPE_ENUM
#undef ETYPE
ETYPE_MAX
};
#endif /* ETYPE_ENUM */

#endif /* JLSS_ID_PASTE_H */

소스 예시:

/*
@(#)File:           $RCSfile: paste1.c,v $
@(#)Version:        $Revision: 1.2 $
@(#)Last changed:   $Date: 2008/06/24 01:03:38 $
@(#)Purpose:        Automated Token Pasting
*/

#include "paste1.h"

static const char *sys_errlist_internal[] = {
#undef JLSS_ID_PASTE_H
#define ETYPE(val, desc) desc,
#include "paste1.h"
    0
#undef ETYPE
};

static const char *xerror(int err)
{
    if (err >= ETYPE_MAX || err <= 0)
        return "Unknown error";
    return sys_errlist_internal[err];
}

static const char*errlist_mnemonics[] = {
#undef JLSS_ID_PASTE_H
#define ETYPE(val, desc) [E ## val] = "E" #val,
#include "paste1.h"
#undef ETYPE
};

#include <stdio.h>

int main(void)
{
    int i;

    for (i = 0; i < ETYPE_MAX; i++)
    {
        printf("%d: %-6s: %s\n", i, errlist_mnemonics[i], xerror(i));
    }
    return(0);
}

반드시 세계에서 가장 깨끗한 C 프리프로세서를 사용하는 것은 아니지만 여러 번 재료를 쓸 수 있는 것은 아닙니다.

C 식별자 및 문자열 모두 만들기

열거형 인덱스가 0 기반인 경우 이름을 char* 배열에 넣고 열거형 값으로 인덱싱할 수 있습니다.

나는 간단한 템플릿 클래스를 만들었다.streamable_enum스트림 연산자를 사용합니다.<<그리고.>>를 기반으로 합니다.std::map<Enum, std::string>:

#ifndef STREAMABLE_ENUM_HPP
#define STREAMABLE_ENUM_HPP

#include <iostream>
#include <string>
#include <map>

template <typename E>
class streamable_enum
{
public:
    typedef typename std::map<E, std::string> tostr_map_t;
    typedef typename std::map<std::string, E> fromstr_map_t;

    streamable_enum()
    {}

    streamable_enum(E val) :
        Val_(val)
    {}

    operator E() {
        return Val_;
    }

    bool operator==(const streamable_enum<E>& e) {
        return this->Val_ == e.Val_;
    }

    bool operator==(const E& e) {
        return this->Val_ == e;
    }

    static const tostr_map_t& to_string_map() {
        static tostr_map_t to_str_(get_enum_strings<E>());
        return to_str_;
    }

    static const fromstr_map_t& from_string_map() {
        static fromstr_map_t from_str_(reverse_map(to_string_map()));
        return from_str_;
    }
private:
    E Val_;

    static fromstr_map_t reverse_map(const tostr_map_t& eToS) {
        fromstr_map_t sToE;
        for (auto pr : eToS) {
            sToE.emplace(pr.second, pr.first);
        }
        return sToE;
    }
};

template <typename E>
streamable_enum<E> stream_enum(E e) {
    return streamable_enum<E>(e);
}

template <typename E>
typename streamable_enum<E>::tostr_map_t get_enum_strings() {
    // \todo throw an appropriate exception or display compile error/warning
    return {};
}

template <typename E>
std::ostream& operator<<(std::ostream& os, streamable_enum<E> e) {
    auto& mp = streamable_enum<E>::to_string_map();
    auto res = mp.find(e);
    if (res != mp.end()) {
        os << res->second;
    } else {
        os.setstate(std::ios_base::failbit);
    }
    return os;
}

template <typename E>
std::istream& operator>>(std::istream& is, streamable_enum<E>& e) {
    std::string str;
    is >> str;
    if (str.empty()) {
        is.setstate(std::ios_base::failbit);
    }
    auto& mp = streamable_enum<E>::from_string_map();
    auto res = mp.find(str);
    if (res != mp.end()) {
        e = res->second;
    } else {
        is.setstate(std::ios_base::failbit);
    }
    return is;
}

#endif

사용방법:

#include "streamable_enum.hpp"

using std::cout;
using std::cin;
using std::endl;

enum Animal {
    CAT,
    DOG,
    TIGER,
    RABBIT
};

template <>
streamable_enum<Animal>::tostr_map_t get_enum_strings<Animal>() {
    return {
        { CAT, "Cat"},
        { DOG, "Dog" },
        { TIGER, "Tiger" },
        { RABBIT, "Rabbit" }
    };
}

int main(int argc, char* argv []) {
    cout << "What animal do you want to buy? Our offering:" << endl;
    for (auto pr : streamable_enum<Animal>::to_string_map()) {          // Use from_string_map() and pr.first instead
        cout << " " << pr.second << endl;                               // to have them sorted in alphabetical order
    }
    streamable_enum<Animal> anim;
    cin >> anim;
    if (!cin) {
        cout << "We don't have such animal here." << endl;
    } else if (anim == Animal::TIGER) {
        cout << stream_enum(Animal::TIGER) << " was a joke..." << endl;
    } else {
        cout << "Here you are!" << endl;
    }

    return 0;
}

다음은 다음 기능을 가진 매크로를 사용하는 솔루션입니다.

  1. enum의 각 값을 한 번만 쓰기 때문에 유지할 이중 목록이 없습니다.

  2. 나중에 #포함되는 별도의 파일에 열거값을 보관하지 마십시오.그러면 원하는 위치에 입력할 수 있습니다.

  3. enum 자체를 대체하지 않습니다. 여전히 enum 유형을 정의하고 싶습니다만, 그 외에도 모든 enum 이름을 대응하는 문자열에 매핑할 수 있도록 하고 싶습니다(레거시 코드에 영향을 주지 않도록).

  4. 검색은 고속이어야 합니다.따라서 스위치 케이스는 사용하지 않는 것이 좋습니다.

https://stackoverflow.com/a/20134475/1812866

Boost 같은 솔루션인 줄 알았어요.구조나 클래스 적응을 위한 퓨전이라면 어떤 시점에서는 에넘을 퓨전 시퀀스로 사용할 수도 있습니다.

그래서 에넘을 출력하기 위한 코드를 생성하기 위해 작은 매크로를 만들었습니다.이것은 완벽하지 않고 Boost와는 전혀 관계가 없습니다.Fusion에서 생성된 보일러 플레이트 코드는 Boost Fusion 매크로와 같이 사용할 수 있습니다.Boost가 필요로 하는 타입을 생성하고 싶다.Fusion은 구조 멤버의 이름을 인쇄할 수 있는 이 인프라스트럭처에 통합되지만, 이것은 나중에 발생합니다.현재로서는 매크로에 불과합니다.

#ifndef SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP
#define SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP

#include <swissarmyknife/detail/config.hpp>

#include <string>
#include <ostream>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/seq/for_each.hpp>


#define SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C(                     \
    R, unused, ENUMERATION_ENTRY)                                               \
    case ENUMERATION_ENTRY:                                                     \
      return BOOST_PP_STRINGIZE(ENUMERATION_ENTRY);                             \
    break;                                                                      

/**
 * \brief Adapts ENUM to reflectable types.
 *
 * \param ENUM_TYPE To be adapted
 * \param ENUMERATION_SEQ Sequence of enum states
 */
#define SWISSARMYKNIFE_ADAPT_ENUM(ENUM_TYPE, ENUMERATION_SEQ)                   \
    inline std::string to_string(const ENUM_TYPE& enum_value) {                 \
      switch (enum_value) {                                                     \
      BOOST_PP_SEQ_FOR_EACH(                                                    \
          SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C,                   \
          unused, ENUMERATION_SEQ)                                              \
        default:                                                                \
          return BOOST_PP_STRINGIZE(ENUM_TYPE);                                 \
      }                                                                         \
    }                                                                           \
                                                                                \
    inline std::ostream& operator<<(std::ostream& os, const ENUM_TYPE& value) { \
      os << to_string(value);                                                   \
      return os;                                                                \
    }

#endif

아래 오래된 답변은 상당히 좋지 않습니다, 그것을 사용하지 말아 주세요.:)

오래된 답변:

에넘 선언 구문을 크게 변경하지 않고 이 문제를 해결할 수 있는 방법을 찾고 있습니다.프리프로세서를 사용하여 문자열화된 열거형 선언에서 문자열을 가져오는 솔루션을 찾았습니다.

다음과 같이 non-sparse enum을 정의할 수 있습니다.

SMART_ENUM(State, 
    enum State {
        RUNNING,
        SLEEPING, 
        FAULT, 
        UNKNOWN
    })

다양한 방법으로 대화할 수 있습니다.

// With a stringstream
std::stringstream ss;
ss << State::FAULT;
std::string myEnumStr = ss.str();

//Directly to stdout
std::cout << State::FAULT << std::endl;

//to a string
std::string myStr = State::to_string(State::FAULT);

//from a string
State::State myEnumVal = State::from_string(State::FAULT);

다음의 정의에 근거합니다.

#define SMART_ENUM(enumTypeArg, ...)                                                     \
namespace enumTypeArg {                                                                  \
    __VA_ARGS__;                                                                         \
    std::ostream& operator<<(std::ostream& os, const enumTypeArg& val) {                 \
            os << swissarmyknife::enums::to_string(#__VA_ARGS__, val);                   \
            return os;                                                                   \
    }                                                                                    \
                                                                                     \
    std::string to_string(const enumTypeArg& val) {                                      \
            return swissarmyknife::enums::to_string(#__VA_ARGS__, val);                  \
    }                                                                                    \
                                                                                     \
    enumTypeArg from_string(const std::string &str) {                                    \
            return swissarmyknife::enums::from_string<enumTypeArg>(#__VA_ARGS__, str);   \
    }                                                                                    \
}                                                                                        \


namespace swissarmyknife { namespace enums {

    static inline std::string to_string(const std::string completeEnumDeclaration, size_t enumVal) throw (std::runtime_error) {
        size_t begin = completeEnumDeclaration.find_first_of('{');
        size_t end = completeEnumDeclaration.find_last_of('}');
        const std::string identifiers = completeEnumDeclaration.substr(begin + 1, end );

        size_t count = 0;
        size_t found = 0;
        do {
            found = identifiers.find_first_of(",}", found+1);

            if (enumVal == count) {
                std::string identifiersSubset = identifiers.substr(0, found);
                size_t beginId = identifiersSubset.find_last_of("{,");
                identifiersSubset = identifiersSubset.substr(beginId+1);
                boost::algorithm::trim(identifiersSubset);
                return identifiersSubset;
            }

            ++count;
        } while (found != std::string::npos);

        throw std::runtime_error("The enum declaration provided doesn't contains this state.");
    }                                                  

    template <typename EnumType>
    static inline EnumType from_string(const std::string completeEnumDeclaration, const std::string &enumStr) throw (std::runtime_error) {
        size_t begin = completeEnumDeclaration.find_first_of('{');
        size_t end = completeEnumDeclaration.find_last_of('}');
        const std::string identifiers = completeEnumDeclaration.substr(begin + 1, end );

        size_t count = 0;
        size_t found = 0;
        do {
            found = identifiers.find_first_of(",}", found+1);

            std::string identifiersSubset = identifiers.substr(0, found);
            size_t beginId = identifiersSubset.find_last_of("{,");
            identifiersSubset = identifiersSubset.substr(beginId+1);
            boost::algorithm::trim(identifiersSubset);

            if (identifiersSubset == enumStr) {
                return static_cast<EnumType>(count);
            }

            ++count;
        } while (found != std::string::npos);

        throw std::runtime_error("No valid enum value for the provided string");
    }                      

}}

sparse enum 지원이 필요한 경우 및 시간이 더 필요한 경우 boost를 사용하여 to_stringfrom_string 구현을 개선합니다.:xpressive. 단, 중요한 템플릿이 실행되기 때문에 컴파일 시간이 소요되며 생성되는 실행파일은 매우 커질 수 있습니다.그러나 이것은 이 보기 흉한 수동 문자열 조작 코드보다 읽기 쉽고 유지보수가 용이하다는 장점이 있습니다.:D

그 이외의 경우에는 항상 boost::bimap을 사용하여 enums 값과 문자열 간의 매핑을 수행했지만 수동으로 유지해야 합니다.

저는 일반적인 이유로 매크로를 사용하지 않는 것을 선호하기 때문에 열거형 선언 매크로를 자유롭게 유지할 수 있는 장점이 있는 보다 제한된 매크로 솔루션을 사용했습니다.단점으로는 각 열거형에 대해 매크로 정의를 복사 붙여넣어야 하며 열거형에 값을 추가할 때 매크로 호출을 명시적으로 추가해야 한다는 점이 있습니다.

std::ostream& operator<<(std::ostream& os, provenance_wrapper::CaptureState cs)
{
#define HANDLE(x) case x: os << #x; break;
    switch (cs) {
    HANDLE(CaptureState::UNUSED)
    HANDLE(CaptureState::ACTIVE)
    HANDLE(CaptureState::CLOSED)
    }
    return os;
#undef HANDLE
}

이 문맥에서 놓친 보다 간단하고 명확한 접근법이 있습니다.

    #define ENUM_PUSH(ENUM)   ENUM,
    #define STRING_PUSH(STR)  #STR,
    
    #define FETCH_MSG(X)      \
            X(string1)        \
            X(string2)        \
    
    static const char * msgStr[] = {
        FETCH_MSG(STRING_PUSH)
    };
    
    enum msg {
        FETCH_MSG(ENUM_PUSH)
    };
    
    static enum msg message;
    
    
    void iterate(void) {
        switch (message) {
        case string1:
// do your thing here
            break;
        case string2:
            break;
        }
    }

유일한 단점은 C/C++ 컴파일러에서는 허용되지만 마지막 셀은 쉼표로 지정된다는 것입니다.

언급URL : https://stackoverflow.com/questions/147267/easy-way-to-use-variables-of-enum-types-as-string-in-c