Question 1: functionxxx_as_string() is used below. How else could it be more elegantly named?
Question 2: Is the static char* array method adopted below the only solution? Best solution? Any suggestions?
Generally, I have this issue where my list of enums will be from 3 - say 50 items, mostly less than 20 items and they are fairly static.
#include <iostream>
enum thing_type
{
DTypeAnimal,
DTypeMineral,
DTypeVegetable,
DTypeUnknown
};
class thing {
public:
thing(thing_type type) : type_(type) {}
const thing_type get_state() const { return type_; }
const char* get_state_as_string() const {
static const char* ttype[] = {
"Animal",
"Mineral",
"Vegetable",
"Unknown"
};
return ttype[type_];
}
private:
thing_type type_;
};
int main() {
thing th(DTypeMineral);
std::cout << "this thing is a " << th.get_state_as_string() << std::endl;
return 0;
}
I am preferring to remove all the printing stuff from the class interface and use the operator<< overloading idea in 200_success answer like this:
#include <iostream>
enum thing_type
{
DTypeUnknown,
DTypeAnimal,
DTypeMineral,
DTypeVegetable
};
const char* type2string(thing_type ttype) {
static const char* thtype[] = {
"Unknown",
"Animal",
"Mineral",
"Vegetable"
};
return ttype < sizeof(thtype) ? thtype[ttype] : thtype[0];
}
std::ostream& operator<<(std::ostream& os, const thing_type type) {
os << type2string(type);
return os;
}
class thing {
public:
thing(thing_type type) : type_(type) {}
const thing_type get_type() const { return type_; }
private:
thing_type type_;
};
std::ostream& operator<<(std::ostream& os, const thing& th) {
os << "This is a " << type2string(th.get_type());
return os;
}
int main() {
thing th(DTypeMineral);
std::cout << th << std::endl;
return 0;
}
3 Answers 3
Question 1: functionxxx_as_string() is used below. How else could it be more elegantly named?
How about:
std::ostream& operator<<(std::ostream& str, thing const& data);
Question 2: Is the static char* array method adopted below the only solution? Best solution? Any suggestions?
You will need a static char array (or equivalent (like a switch)) somewhere as there is no built in way to convert enum values (which are integers) to a string.
-
\$\begingroup\$ I feel a bit bad about changing my mind but only just seen byour answer. Agree overloading operator<< is better approach. See my update above. \$\endgroup\$arcomber– arcomber2013年09月13日 09:17:53 +00:00Commented Sep 13, 2013 at 9:17
//I lost original answer I wanted to post on stackoverflow...
Question 1: functionxxx_as_string() is used below. How else could it be more elegantly named?
const char *to_string(thing_type type);
It doesn't have to be class member.
Question 2: Is the static char* array method adopted below the only solution? Best solution? Any suggestions?
Not the only solution, may or may not be the best solution.
Potential problems - if your enum has "holes" enum Asdf{a = 1, b = 48, c}
, array of names won't work.
You'll need to use array of pairs, std::map or plain old switch/case.
//warning: I haven't compiled the code, there might be typos.
array of pairs:
const char *tOstring(thing_type type){
struct Data{
thing_type type;
const char *name;
};
static const Data data[] = {
{thing, "thing"},
....
{unusued_value, 0)
};
//if you use binary search here, it'll be faster
for (const Data* cur = data[]; data->name; data++){
if (cur->type == type)
return cur->name;
}
return 0;
}
std::map:
typedef std::map<thing_type, std::string> NameMap;
NameMap nameMap;//somewhere
std::string toString(thing_type type){
NameMap::const_iterator found = nameMap.find(type);
if (found == nameMap.end())
return std::string; //or throw exception
return found.second;
}
You can make array of pairs static const, but std::map will need to be initialized somewhere. However, with std::map you can add/remove values at runtime. static const array of pairs and switch/case can't be changed at runtime.
Any suggestions?
You should probably use macros to initialize your array. Practical example (using switch/case):
const char* glErrorString(GLuint error){
switch (error){
#define BRANCH(p) case(p): return #p;
BRANCH(GL_NO_ERROR)
BRANCH(GL_INVALID_ENUM)
BRANCH(GL_INVALID_VALUE)
BRANCH(GL_INVALID_OPERATION)
BRANCH(GL_STACK_OVERFLOW)
BRANCH(GL_STACK_UNDERFLOW)
BRANCH(GL_OUT_OF_MEMORY)
BRANCH(GL_TABLE_TOO_LARGE )
default:
return "Unknown error\n";
#undef BRANCH
};
}
You should use one of the preprocessor macro-based solutions proposed in StackOverflow Question 147267. For example, here is a solution using X() macros. I've also rewritten your get_state_as_string()
as a stringifier for iostream.
#include <iostream>
#define X_THINGS \
X(DTypeAnimal, "Animal") \
X(DTypeMineral, "Mineral") \
X(DTypeVegetable, "Vegetable") \
X(DTypeUnknown, "Unknown")
typedef enum {
# define X(Enum, String) Enum,
X_THINGS
# undef X
} thing_type;
class Thing {
public:
Thing(thing_type type) : type_(type) {}
const thing_type get_state() const { return type_; }
friend std::ostream &operator<<(std::ostream&, const Thing&);
private:
thing_type type_;
};
std::ostream &operator<<(std::ostream &os, const Thing &t) {
switch (t.get_state()) {
# define X(Enum, String) \
case Enum: os << String; break;
X_THINGS
# undef X
}
return os;
}
int main() {
Thing th(DTypeMineral);
std::cout << "this thing is a " << th << std::endl;
return 0;
}
-
\$\begingroup\$ I'd paste at least one of the examples here to make this answer more valuable. \$\endgroup\$Jamal– Jamal2013年09月12日 12:18:19 +00:00Commented Sep 12, 2013 at 12:18
-
\$\begingroup\$ Yes I don't like all the as_string functions. I prefer overloading operator<< - and taking the printing stuff out of the class. \$\endgroup\$arcomber– arcomber2013年09月12日 17:49:32 +00:00Commented Sep 12, 2013 at 17:49
-
4\$\begingroup\$ Since this is C++ proposing a C solution is probably not a good idea. The same affective can be done much better using language features in C++. \$\endgroup\$Loki Astari– Loki Astari2013年09月13日 03:06:36 +00:00Commented Sep 13, 2013 at 3:06
enum light { red, yellow, green, stop = 0, go = 2 };
. That makes the name for a given value plural. \$\endgroup\$