이와 같은 열거 형이 있으면
enum Errors
{ErrorA=0, ErrorB, ErrorC};
그런 다음 콘솔로 인쇄하고 싶습니다.
Errors anError = ErrorA;
cout<<anError;/// 0 will be printed
하지만 내가 원하는 것은 "ErrorA"라는 텍스트입니다. if / switch를 사용하지 않고 할 수 있습니까?
이에 대한 해결책은 무엇입니까?
이와 같은 열거 형이 있으면
enum Errors
{ErrorA=0, ErrorB, ErrorC};
그런 다음 콘솔로 인쇄하고 싶습니다.
Errors anError = ErrorA;
cout<<anError;/// 0 will be printed
하지만 내가 원하는 것은 "ErrorA"라는 텍스트입니다. if / switch를 사용하지 않고 할 수 있습니까?
이에 대한 해결책은 무엇입니까?
enum class
: stackoverflow.com/questions/11421432/...
답변:
지도 사용 :
#include <iostream>
#include <map>
#include <string>
enum Errors {ErrorA=0, ErrorB, ErrorC};
std::ostream& operator<<(std::ostream& out, const Errors value){
static std::map<Errors, std::string> strings;
if (strings.size() == 0){
#define INSERT_ELEMENT(p) strings[p] = #p
INSERT_ELEMENT(ErrorA);
INSERT_ELEMENT(ErrorB);
INSERT_ELEMENT(ErrorC);
#undef INSERT_ELEMENT
}
return out << strings[value];
}
int main(int argc, char** argv){
std::cout << ErrorA << std::endl << ErrorB << std::endl << ErrorC << std::endl;
return 0;
}
선형 검색과 함께 구조 배열 사용 :
#include <iostream>
#include <string>
enum Errors {ErrorA=0, ErrorB, ErrorC};
std::ostream& operator<<(std::ostream& out, const Errors value){
#define MAPENTRY(p) {p, #p}
const struct MapEntry{
Errors value;
const char* str;
} entries[] = {
MAPENTRY(ErrorA),
MAPENTRY(ErrorB),
MAPENTRY(ErrorC),
{ErrorA, 0}//doesn't matter what is used instead of ErrorA here...
};
#undef MAPENTRY
const char* s = 0;
for (const MapEntry* i = entries; i->str; i++){
if (i->value == value){
s = i->str;
break;
}
}
return out << s;
}
int main(int argc, char** argv){
std::cout << ErrorA << std::endl << ErrorB << std::endl << ErrorC << std::endl;
return 0;
}
스위치 / 케이스 사용 :
#include <iostream>
#include <string>
enum Errors {ErrorA=0, ErrorB, ErrorC};
std::ostream& operator<<(std::ostream& out, const Errors value){
const char* s = 0;
#define PROCESS_VAL(p) case(p): s = #p; break;
switch(value){
PROCESS_VAL(ErrorA);
PROCESS_VAL(ErrorB);
PROCESS_VAL(ErrorC);
}
#undef PROCESS_VAL
return out << s;
}
int main(int argc, char** argv){
std::cout << ErrorA << std::endl << ErrorB << std::endl << ErrorC << std::endl;
return 0;
}
#p
p를 묶는 전 처리기입니다. 따라서 호출 PROCESS_VAL(ErrorA)
하면 case(ErrorA): s = "ErrorA"; break;
.
enum
enum
값이 일치하는 문자열의 배열 또는 벡터를 사용합니다.
char *ErrorTypes[] =
{
"errorA",
"errorB",
"errorC"
};
cout << ErrorTypes[anError];
편집 : 위의 솔루션은 열거 형이 연속적 일 때 적용 할 수 있습니다. 즉, 0에서 시작하고 할당 된 값이 없습니다. 질문의 열거 형과 완벽하게 작동합니다.
enum이 0에서 시작하지 않는 경우를 추가로 증명하려면 다음을 사용하십시오.
cout << ErrorTypes[anError - ErrorA];
다음은 Boost.Preprocessor를 기반으로 한 예입니다.
#include <iostream>
#include <boost/preprocessor/punctuation/comma.hpp>
#include <boost/preprocessor/control/iif.hpp>
#include <boost/preprocessor/comparison/equal.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/size.hpp>
#include <boost/preprocessor/seq/seq.hpp>
#define DEFINE_ENUM(name, values) \
enum name { \
BOOST_PP_SEQ_FOR_EACH(DEFINE_ENUM_VALUE, , values) \
}; \
inline const char* format_##name(name val) { \
switch (val) { \
BOOST_PP_SEQ_FOR_EACH(DEFINE_ENUM_FORMAT, , values) \
default: \
return 0; \
} \
}
#define DEFINE_ENUM_VALUE(r, data, elem) \
BOOST_PP_SEQ_HEAD(elem) \
BOOST_PP_IIF(BOOST_PP_EQUAL(BOOST_PP_SEQ_SIZE(elem), 2), \
= BOOST_PP_SEQ_TAIL(elem), ) \
BOOST_PP_COMMA()
#define DEFINE_ENUM_FORMAT(r, data, elem) \
case BOOST_PP_SEQ_HEAD(elem): \
return BOOST_PP_STRINGIZE(BOOST_PP_SEQ_HEAD(elem));
DEFINE_ENUM(Errors,
((ErrorA)(0))
((ErrorB))
((ErrorC)))
int main() {
std::cout << format_Errors(ErrorB) << std::endl;
}
DEFINE_ENUM
하면 오류가 발생 multiple definition of `format_ProgramStatus(ProgramStatus)'
합니다.
enum
외부 파일에 항목 을 나열하려는 경우 더 간단한 전 처리기 트릭을 사용할 수 있습니다 .
/* file: errors.def */
/* syntax: ERROR_DEF(name, value) */
ERROR_DEF(ErrorA, 0x1)
ERROR_DEF(ErrorB, 0x2)
ERROR_DEF(ErrorC, 0x4)
그런 다음 소스 파일에서 파일을 포함 파일처럼 취급하지만 수행 할 작업을 정의합니다 ERROR_DEF
.
enum Errors {
#define ERROR_DEF(x,y) x = y,
#include "errors.def"
#undef ERROR_DEF
};
static inline std::ostream & operator << (std::ostream &o, Errors e) {
switch (e) {
#define ERROR_DEF(x,y) case y: return o << #x"[" << y << "]";
#include "errors.def"
#undef ERROR_DEF
default: return o << "unknown[" << e << "]";
}
}
cscope와 같은 소스 검색 도구를 사용하는 경우 외부 파일에 대해 알려야합니다.
여기에 도움이 될 수있는 토론이 있습니다. C ++ 열거 형을 문자열로 변환하는 간단한 방법이 있습니까?
업데이트 : 여기에서 만나는 각 명명 된 열거 형에 대해 operator <<를 만드는 Lua 용 스크립트입니다. 덜 간단한 경우에 작동하도록하려면 약간의 작업이 필요할 수 있습니다 [1].
function make_enum_printers(s)
for n,body in string.gmatch(s,'enum%s+([%w_]+)%s*(%b{})') do
print('ostream& operator<<(ostream &o,'..n..' n) { switch(n){')
for k in string.gmatch(body,"([%w_]+)[^,]*") do
print(' case '..k..': return o<<"'..k..'";')
end
print(' default: return o<<"(invalid value)"; }}')
end
end
local f=io.open(arg[1],"r")
local s=f:read('*a')
make_enum_printers(s)
이 입력이 주어지면 :
enum Errors
{ErrorA=0, ErrorB, ErrorC};
enum Sec {
X=1,Y=X,foo_bar=X+1,Z
};
다음을 생성합니다.
ostream& operator<<(ostream &o,Errors n) { switch(n){
case ErrorA: return o<<"ErrorA";
case ErrorB: return o<<"ErrorB";
case ErrorC: return o<<"ErrorC";
default: return o<<"(invalid value)"; }}
ostream& operator<<(ostream &o,Sec n) { switch(n){
case X: return o<<"X";
case Y: return o<<"Y";
case foo_bar: return o<<"foo_bar";
case Z: return o<<"Z";
default: return o<<"(invalid value)"; }}
그래서 그것은 아마도 당신을위한 시작일 것입니다.
[1] 다른 또는 비 네임 스페이스 범위의 열거 형, komma를 포함하는 이니셜 라이저 표현식이있는 열거 형 등.
열거 형을 정의 할 때마다 문자열 배열을 사용합니다.
Profile.h
#pragma once
struct Profile
{
enum Value
{
Profile1,
Profile2,
};
struct StringValueImplementation
{
const wchar_t* operator[](const Profile::Value profile)
{
switch (profile)
{
case Profile::Profile1: return L"Profile1";
case Profile::Profile2: return L"Profile2";
default: ASSERT(false); return NULL;
}
}
};
static StringValueImplementation StringValue;
};
Profile.cpp
#include "Profile.h"
Profile::StringValueImplementation Profile::StringValue;
이것은 좋은 방법입니다.
enum Rank { ACE = 1, DEUCE, TREY, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING };
문자 배열의 배열로 인쇄
const char* rank_txt[] = {"Ace", "Deuce", "Trey", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Four", "King" } ;
이렇게
std::cout << rank_txt[m_rank - 1]
#include <iostream>
using std::cout;
using std::endl;
enum TEnum
{
EOne,
ETwo,
EThree,
ELast
};
#define VAR_NAME_HELPER(name) #name
#define VAR_NAME(x) VAR_NAME_HELPER(x)
#define CHECK_STATE_STR(x) case(x):return VAR_NAME(x);
const char *State2Str(const TEnum state)
{
switch(state)
{
CHECK_STATE_STR(EOne);
CHECK_STATE_STR(ETwo);
CHECK_STATE_STR(EThree);
CHECK_STATE_STR(ELast);
default:
return "Invalid";
}
}
int main()
{
int myInt=12345;
cout << VAR_NAME(EOne) " " << VAR_NAME(myInt) << endl;
for(int i = -1; i < 5; i)
cout << i << " " << State2Str((TEnum)i) << endl;
return 0;
}
stl 맵 컨테이너를 사용할 수 있습니다 ....
typedef map<Errors, string> ErrorMap;
ErrorMap m;
m.insert(ErrorMap::value_type(ErrorA, "ErrorA"));
m.insert(ErrorMap::value_type(ErrorB, "ErrorB"));
m.insert(ErrorMap::value_type(ErrorC, "ErrorC"));
Errors error = ErrorA;
cout << m[error] << endl;
switch(n) { case XXX: return "XXX"; ... }
가요? O (1) 조회가 있고 초기화 할 필요가없는 것은 무엇입니까? 아니면 열거 형은 런타임 중에 어떻게 든 변경됩니까?
이 문제에 대해 다음과 같은 도움말 기능을 수행합니다.
const char* name(Id id) {
struct Entry {
Id id;
const char* name;
};
static const Entry entries[] = {
{ ErrorA, "ErrorA" },
{ ErrorB, "ErrorB" },
{ 0, 0 }
}
for (int it = 0; it < gui::SiCount; ++it) {
if (entries[it].id == id) {
return entries[it].name;
}
}
return 0;
}
선형 검색은 일반적으로 std::map
이와 같은 소규모 컬렉션 보다 효율적 입니다.
이 솔루션은 데이터 구조를 사용하거나 다른 파일을 만들 필요가 없습니다.
기본적으로 모든 열거 형 값을 #define에 정의한 다음 << 연산자에서 사용합니다. @jxh의 답변과 매우 유사합니다.
최종 반복을위한 ideone 링크 : http://ideone.com/hQTKQp
전체 코드 :
#include <iostream>
#define ERROR_VALUES ERROR_VALUE(NO_ERROR)\
ERROR_VALUE(FILE_NOT_FOUND)\
ERROR_VALUE(LABEL_UNINITIALISED)
enum class Error
{
#define ERROR_VALUE(NAME) NAME,
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) case Error::NAME: return os << "[" << errVal << "]" #NAME;
ERROR_VALUES
#undef ERROR_VALUE
default:
// If the error value isn't found (shouldn't happen)
return os << errVal;
}
}
int main() {
std::cout << "Error: " << Error::NO_ERROR << std::endl;
std::cout << "Error: " << Error::FILE_NOT_FOUND << std::endl;
std::cout << "Error: " << Error::LABEL_UNINITIALISED << std::endl;
return 0;
}
산출:
Error: [0]NO_ERROR
Error: [1]FILE_NOT_FOUND
Error: [2]LABEL_UNINITIALISED
이 방법의 좋은 점은 필요하다고 생각되는 경우 각 오류에 대해 고유 한 사용자 지정 메시지를 지정할 수도 있다는 것입니다.
#include <iostream>
#define ERROR_VALUES ERROR_VALUE(NO_ERROR, "Everything is fine")\
ERROR_VALUE(FILE_NOT_FOUND, "File is not found")\
ERROR_VALUE(LABEL_UNINITIALISED, "A component tried to the label before it was initialised")
enum class Error
{
#define ERROR_VALUE(NAME,DESCR) NAME,
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,DESCR) case Error::NAME: return os << "[" << errVal << "]" #NAME <<"; " << DESCR;
ERROR_VALUES
#undef ERROR_VALUE
default:
return os << errVal;
}
}
int main() {
std::cout << "Error: " << Error::NO_ERROR << std::endl;
std::cout << "Error: " << Error::FILE_NOT_FOUND << std::endl;
std::cout << "Error: " << Error::LABEL_UNINITIALISED << std::endl;
return 0;
}
산출:
Error: [0]NO_ERROR; Everything is fine
Error: [1]FILE_NOT_FOUND; File is not found
Error: [2]LABEL_UNINITIALISED; A component tried to the label before it was initialised
오류 코드 / 설명을 매우 설명 적으로 만들고 싶다면 프로덕션 빌드에 사용하지 않는 것이 좋습니다. 값만 인쇄되도록 끄는 것은 쉽습니다.
inline std::ostream& operator<<(std::ostream& os, Error err)
{
int errVal = static_cast<int>(err);
switch (err)
{
#ifndef PRODUCTION_BUILD // Don't print out names in production builds
#define ERROR_VALUE(NAME,DESCR) case Error::NAME: return os << "[" << errVal << "]" #NAME <<"; " << DESCR;
ERROR_VALUES
#undef ERROR_VALUE
#endif
default:
return os << errVal;
}
}
산출:
Error: 0
Error: 1
Error: 2
이 경우 오류 번호 525를 찾는 것은 PITA입니다. 다음과 같이 초기 열거 형에 수동으로 숫자를 지정할 수 있습니다.
#define ERROR_VALUES ERROR_VALUE(NO_ERROR, 0, "Everything is fine")\
ERROR_VALUE(FILE_NOT_FOUND, 1, "File is not found")\
ERROR_VALUE(LABEL_UNINITIALISED, 2, "A component tried to the label before it was initialised")\
ERROR_VALUE(UKNOWN_ERROR, -1, "Uh oh")
enum class Error
{
#define ERROR_VALUE(NAME,VALUE,DESCR) NAME=VALUE,
ERROR_VALUES
#undef ERROR_VALUE
};
inline std::ostream& operator<<(std::ostream& os, Error err)
{
int errVal = static_cast<int>(err);
switch (err)
{
#ifndef PRODUCTION_BUILD // Don't print out names in production builds
#define ERROR_VALUE(NAME,VALUE,DESCR) case Error::NAME: return os << "[" #VALUE "]" #NAME <<"; " << DESCR;
ERROR_VALUES
#undef ERROR_VALUE
#endif
default:
return os <<errVal;
}
}
ERROR_VALUES
#undef ERROR_VALUE
#endif
default:
{
// If the error value isn't found (shouldn't happen)
return os << static_cast<int>(err);
break;
}
}
}
산출:
Error: [0]NO_ERROR; Everything is fine
Error: [1]FILE_NOT_FOUND; File is not found
Error: [2]LABEL_UNINITIALISED; A component tried to the label before it was initialised
Error: [-1]UKNOWN_ERROR; Uh oh
이건 어때?
enum class ErrorCodes : int{
InvalidInput = 0
};
std::cout << ((int)error == 0 ? "InvalidInput" : "") << std::endl;
등 ... 나는 이것이 고도로 고안된 예라는 것을 알고 있지만 적용 가능하고 필요한 곳에 응용 프로그램이 있으며 스크립트를 작성하는 것보다 확실히 짧다고 생각합니다.
전처리기를 사용하십시오.
#define VISIT_ERROR(FIRST, MIDDLE, LAST) \
FIRST(ErrorA) MIDDLE(ErrorB) /* MIDDLE(ErrorB2) */ LAST(ErrorC)
enum Errors
{
#define ENUMFIRST_ERROR(E) E=0,
#define ENUMMIDDLE_ERROR(E) E,
#define ENUMLAST_ERROR(E) E
VISIT_ERROR(ENUMFIRST_ERROR, ENUMMIDDLE_ERROR, ENUMLAST_ERROR)
// you might undefine the 3 macros defined above
};
std::string toString(Error e)
{
switch(e)
{
#define CASERETURN_ERROR(E) case E: return #E;
VISIT_ERROR(CASERETURN_ERROR, CASERETURN_ERROR, CASERETURN_ERROR)
// you might undefine the above macro.
// note that this will produce compile-time error for synonyms in enum;
// handle those, if you have any, in a distinct macro
default:
throw my_favourite_exception();
}
}
이 접근 방식의 장점은 다음과 같습니다.-여전히 이해하기 쉽지만-문자열뿐만 아니라 다양한 방문이 가능합니다.
첫 번째를 삭제하려면 FOREACH () 매크로를 직접 만든 다음 #define ERROR_VALUES() (ErrorA, ErrorB, ErrorC)
FOREACH () 관점에서 방문자를 작성하십시오. 그런 다음 코드 검토를 통과 해보십시오. :).