Если у меня есть перечисление как это
enum Errors
{ErrorA=0, ErrorB, ErrorC};
Затем я хочу распечатать к консоли
Errors anError = ErrorA;
cout<<anError;/// 0 will be printed
но то, что я хочу, является текстом "ErrorA", я могу сделать это, не используя если/переключатель?
И каково Ваше решение для этого?
Использование карты:
#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;
}
Использование переключателя / case:
#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;
}
Вот пример, основанный на 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;
}
Здесь было обсуждение, которое может помочь: Есть ли простой способ преобразовать перечисление C ++ в строку?
ОБНОВЛЕНИЕ: Вот сценарий для 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] перечисления в различных областях или областях, не относящихся к пространству имен, перечисления с выражениями инициализатора, которые содержат комму, и т. Д.
Использовать массив или вектор строк с совпадающими значениями:
char *ErrorTypes[] =
{
"errorA",
"errorB",
"errorC"
};
cout << ErrorTypes[anError];
EDIT: Решение выше применимо, когда перечисление является непрерывным, т.е. начинается с 0 и нет назначенных значений. Оно будет отлично работать с перечислением в вопросе.
Для дальнейшего доказательства для случая, когда перечисление не начинается с 0, используйте:
cout << ErrorTypes[anError - ErrorA];
Я использую массив строк всякий раз, когда определяю перечисление:
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;
Вы можете использовать контейнер 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;
Для этой проблемы я использую такую вспомогательную функцию:
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
для небольших коллекций, подобных этой.