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

StackOverflow https://stackoverflow.com/questions/147267

  •  02-07-2019
  •  | 
  •  

문제

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

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

모든 경우를 정의하는 대신 위에서하려는 것처럼 열거 변수를 사용하여 설정하는 방법이 있습니까?

도움이 되었습니까?

해결책

내장 솔루션이 없습니다. 가장 쉬운 방법은 배열입니다 char* 열거의 int 값은 해당 열거의 설명 이름을 포함하는 문자열로 인덱싱됩니다. 희소가있는 경우 enum (0에서 시작하지 않거나 번호 매기기에 틈이있는 것) 일부 중 일부는 int 매핑은 배열 기반 매핑을 실용적으로 만들 수있을 정도로 높습니다. 대신 해시 테이블을 사용할 수 있습니다.

다른 팁

기술의 기술 C 식별자와 문자열을 모두 만들 수 있습니까? 여기에서 사용할 수 있습니다.

이러한 사전 처리기와 마찬가지로 평소와 같이, 전처리 부분을 쓰고 이해하는 것은 어려울 수 있으며, 매크로를 다른 매크로로 전달하는 것이 포함되며 # 및 ## 연산자를 사용하는 것이 포함되지만 사용하는 것은 매우 쉽습니다. 나는이 스타일이 긴 열거에 매우 유용하다는 것을 알게되며, 동일한 목록을 두 번 유지하는 것이 정말로 번거 롭을 수 있습니다.

공장 코드 - 한 번만 입력하고 일반적으로 헤더에 숨겨져 있습니다.

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 매크로가 더 많은 인수를 받아들이도록 기술을 쉽게 확장 할 수 있으며,이 샘플에서 제공 한 세 가지와 유사하게 다른 요구를 위해 XX를 대체하기 위해 더 많은 매크로를 준비 할 수도 있습니다.

#include / #define / #undef를 사용하여 X-Macros와 비교합니다

이것은 다른 사람들이 언급 한 x -macros와 유사하지만,이 솔루션은 #undefing이 필요하지 않다는 점에서 더 우아하다고 생각합니다. 이는 복잡한 물건을 더 숨길 수 있습니다. 새로운 열거를 정의해야 할 때 전혀 만지지 않는 것입니다. 따라서 새로운 열거 정의는 훨씬 짧고 깨끗합니다.

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

이 작업을 수행하는 방법이 분명히 있습니다. x () 매크로. 이 매크로는 C 프리 프로세서를 사용하여 소스 데이터 목록에서 열거, 배열 및 코드 블록을 구성합니다. x () 매크로가 포함 된 #define에 새 항목을 추가하면됩니다. 스위치 명령문이 자동으로 확장됩니다.

예제는 다음과 같이 쓸 수 있습니다.

 // 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 매크로를 사용하여 문자열 배열 및 열거 인덱스를 생성). 이것은 가장 간단한 데모입니다.

나는 당신이 몇 가지 좋은 답변을 가지고 있다는 것을 알고 있지만, 당신은 c preprocessor의 # 연산자에 대해 알고 있습니까?

이를 수행 할 수 있습니다.

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

키스. 열거로 모든 종류의 다른 스위치/케이스 작업을 수행 할 것이므로 인쇄가 다른 이유는 무엇입니까? 인쇄 루틴에서 케이스를 잊어 버리는 것은 케이스를 잊을 수있는 다른 장소가 100 개 정도 있다고 생각할 때 큰 문제가되지 않습니다. 비 유명한 케이스 일치를 경고하는 단지 -wall 만 컴파일됩니다. "기본값"을 사용하지 마십시오. 스위치가 철저하게 만들어지고 경고를받지 않기 때문입니다. 대신, 스위치를 종료하고 같은 기본 케이스를 처리하십시오 ...

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

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;

그것은 큰 차이가 있기 때문에.

이 방법의 장점은 열거와 문자열의 정의를 서로 가까이에 두는 것입니다. 함수에 스위치 명령문이 있으면 경련이 발생합니다. 이것은 당신이 다른 하나없이 하나를 바꿀 가능성이 적다는 것을 의미합니다.

노력하다 C ++ 열거를 문자열로 변환합니다. 그만큼 코멘트 열거 품목에 임의의 값이있을 때 문제를 해결하는 개선 사항이 있습니다.

사용 부스트 :: 사전 처리기 다음과 같은 우아한 솔루션을 가능하게합니다.

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;

추가 파일이 포함되어 있지 않고 깨끗하고 컴팩트하게 보입니다. 내가 열거성 내에 쓴 코드는 다음과 같습니다.

#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 개 요소보다 클 수 없습니다.

동일한 논리에 따라 희소 열거를 만들어 낼 수도 있습니다.

#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 :: Preprocessor가 완전히 휴대 할 수 없을 수도 있습니다.

여기의 기술 중 일부를 병합하여 가장 간단한 형태를 생각해 냈습니다.

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

GCC를 사용하는 경우 사용할 수 있습니다.

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

그런 다음 예를 들어 전화하십시오

enum_to_string_map[enum1]

아이디어를 확인하십시오 MU Dynamics Research Labs- 블로그 아카이브. 나는 올해 초 이것을 발견했다 - 나는 그것을 발견 한 정확한 맥락을 잊어 버렸다. 우리는 정면에 e를 추가하는 장점에 대해 토론 할 수 있습니다. 해결 된 특정 문제에는 적용 할 수 있지만 일반적인 솔루션의 일부는 아닙니다. 나는 이것을 내 'vignettes'폴더에 숨겼습니다. 여기서 나중에 원할 경우 흥미로운 코드 스크랩을 보관합니다. 나는 당시이 아이디어가 어디에서 왔는지 메모하지 않았다고 말하는 것이 부끄럽다.

헤더 : 페이스트 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 프리 프로세서를 세계에서 가장 깨끗하게 사용하는 것은 아니지만 재료를 여러 번 쓰는 것을 방지합니다.

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

#define stringify( name ) # name

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

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

이 방법에 대한 추가 논의

신규 이민자를위한 전처리 지침 요령

간단한 템플릿 클래스를 만들었습니다 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. 열거의 각 값 만 한 번만 작성하므로 유지할 이중 목록이 없습니다.

  2. 나중에 #included 인 별도의 파일에 열거 값을 유지하지 않으므로 원하는 곳에 글을 쓸 수 있습니다.

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

  4. 검색은 빠르야하므로 스위치 케이스가 없어야합니다.

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

나는 structs와 classes를 적용하기위한 푸스 퓨전과 같은 솔루션이 좋을 것이라고 생각했지만, 열거를 융합 시퀀스로 사용하는 것이 어느 시점에서도 좋았습니다.

그래서 나는 열거를 인쇄하기위한 코드를 생성하기 위해 작은 매크로를 만들었습니다. 이것은 완벽하지 않으며 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

아래의 오래된 대답은 꽤 나쁘다. 그것을 사용하지 마십시오. :)

오래된 답변 :

나는 열거 선언문 구문을 너무 많이 바꾸지 않고이 문제를 해결하는 방법을 찾고 있습니다. 나는 사전 처리기를 사용하여 줄무늬 열거 선언에서 문자열을 검색하는 솔루션에 왔습니다.

이와 같이 비 스피트 열거를 정의 할 수 있습니다.

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

}}

희소 열거에 대한 지원이 필요할 때 더 많은 시간을 가질 때 to_string 그리고 From_string Boost :: xpressive를 사용한 구현이지만, 이는 수행 된 템플릿이 수행되고 생성 된 실행 파일로 인해 컴파일 타임의 비용이 많이 듭니다. 그러나 이것은이 추악한 수동 문자열 조작 코드보다 더 읽기 쉽고 메인 가능 할 것이라는 장점이 있습니다. :디

그렇지 않으면 항상 Boost :: Bimap을 사용하여 열거 값과 문자열 사이에 그러한 매핑을 수행했지만 수동으로 유지해야합니다.

모든 일반적인 이유로 매크로를 사용하지 않기 때문에 열거 선언을 매크로를 무료로 유지할 수있는 이점이있는 더 제한된 매크로 솔루션을 사용했습니다. 단점에는 각 열거에 대한 매크로 정의를 복사해야하고 열거에 값을 추가 할 때 매크로 호출을 명시 적으로 추가해야합니다.

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
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top