There are a couple of ways I deal with this kind of problem:
* Use custom exceptions for error codes instead. I've noticed that I rarely need to know why it failed, just that it failed (and in what context) so I can quit. So my errordetection is pretty much throw an exception(with a string describing why it failed and perhaps a cause).
* Add each value to a enum-string map and do lookups when you want to print it
* Skip the pure enum and make a class instead (kinda like "making a map" but this can be automated)
Speaking of automated enums it's time for a shameless plug ;) . Here is a simple tool that makes an enum class, this was written a long time ago(and I'm considering rewriting it) but it does the job. Stuff that needs to be fixed is (besides a cleaner source) more options, and easier control over the input, and (probably) a gui.
Usage:
// Day.enumSundayMondayTuesdayWednesdayThursdayFridaySaturday// console commandenum Day// Output Day.hpp// The Day.cpp is boring so I wont show it#ifndef DAY_HPP#define DAY_HPP#include <map>#include <string>#include <sstream>class Day {public: enum Data { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, COUNT }; Day(Data pd); explicit Day(const std::string& pString); // assignment void operator=(const Day& pOther); void operator=(const Data& pOther); // equality bool operator==(const Day& pOther) const; bool operator==(const Data& pOther) const; // inequality bool operator!=(const Day& pOther) const; bool operator!=(const Data& pOther) const; static const char* asString(Data d); static const Data asData(const std::string& pString); static bool isValid(Data d); const char* toString() const; static bool isEqual(const Data& a, const Data& b); operator Data() const;public: typedef std::map<std::string, Data> LookupMap; static LookupMap sStringToDataMap;private: Day(); Data d; typedef std::pair<std::string, Data> LookupPair; static const LookupMap buildMap();};std::ostream& operator<<( std::ostream& os, const Day& e );bool operator!=(const Day::Data& pOther, const Day& e );bool operator==(const Day::Data& pOther, const Day& e);#endif //DAY_HPP
Source:
#include <iostream>#include <fstream>#include <sstream>#include <list>#include <map>#include <algorithm>#include <cctype>#include <string>using namespace std;#include <cassert>#include "boost/tokenizer.hpp"struct Enum { string name; string stringName; map<string,string> values;};struct Define { string name; string returnType; string header; string toData; string fromData;};list<Enum> enums;map<string, Define> defines;string theNamespace;void splitString(const std::string& str, const std::string& splitString, std::vector<std::string>* numbers) { assert(numbers); boost::char_separator<char> sep(splitString.c_str()); boost::tokenizer< boost::char_separator<char> > tok(str, sep); std::copy(tok.begin(), tok.end(), std::back_inserter(*numbers));}std::string trim(const std::string& s,const std::string& drop=" \n\t") { std::string r = s; r=r.erase(s.find_last_not_of(drop)+1); return r.erase(0,r.find_first_not_of(drop));}bool beginsWith(const string& line, const string& test) { if( test.length() > line.length() ) return false; const string sub = line.substr(0, test.length()); return sub == test;}string toUpper(const string& pString) { string upperString = pString; transform(upperString.begin(), upperString.end(), upperString.begin(), (int(*)(int)) toupper); return upperString;}string toLower(const string& pString) { string lowerString = pString; transform(lowerString.begin(), lowerString.end(), lowerString.begin(), (int(*)(int)) tolower); return lowerString;}string stringReplace(const string& find, const string& replaceWith, const string& str) { string temp = str; string::size_type res = temp.find_first_of(find); while( res != string::npos ) { temp.replace(res, replaceWith.length(), replaceWith); temp.erase(res, find.length()); temp.insert(res, replaceWith); res = temp.find_first_of(find, res); } return temp;}string toIdent(const string& pString) { string newString = pString; newString = stringReplace(" ", "_", newString); newString = stringReplace("'", "", newString); newString = stringReplace("\"", "", newString); newString = stringReplace("!", "", newString); newString = stringReplace("?", "", newString); newString = stringReplace("/", "_", newString); newString = stringReplace("\\", "_", newString); return newString;}void writeHeader(const string& name) { ofstream f((name + ".hpp").c_str()); const string define = toUpper(name) + "_HPP"; f << "#ifndef " << define << endl << "#define " << define << endl << "" << endl << "#include <map>" << endl << "#include <string>" << endl << "#include <sstream>" << endl<< endl; if( theNamespace != "" ) { f << "namespace " << theNamespace << " {" << endl << endl; }f << "class "<<name<<" {" << endl << "public:" << endl << " enum Data {" << endl; for(list<Enum>::iterator e=enums.begin(); e!= enums.end(); ++e) { f << "\t\t" << e->name << "," << endl; } f << " COUNT" << endl << " };" << endl << "" << endl << " "<<name<<"(Data pd);" << endl << " explicit "<<name<<"(const std::string& pString);" << endl << "" << endl << " // assignment" << endl << " void operator=(const "<<name<<"& pOther);" << endl << " void operator=(const Data& pOther);" << endl << "" << endl << " // equality" << endl << " bool operator==(const "<<name<<"& pOther) const;" << endl << " bool operator==(const Data& pOther) const;" << endl << "" << endl << " // inequality" << endl << " bool operator!=(const "<<name<<"& pOther) const;" << endl << " bool operator!=(const Data& pOther) const;" << endl << "" << endl << " static const char* asString(Data d);" << endl << " static const Data asData(const std::string& pString);" << endl << " static bool isValid(Data d);" << endl << " const char* toString() const;" << endl << " static bool isEqual(const Data& a, const Data& b);" << endl << "" << endl <<" operator Data() const;" << endl << "" << endl << "public:" << endl << " typedef std::map<std::string, Data> LookupMap;" << endl << " static LookupMap sStringToDataMap;" << endl << "private:" << endl << " "<<name<<"();" << endl << " Data d;" << endl << "" << endl << " typedef std::pair<std::string, Data> LookupPair;" << endl << "" << endl << " static const LookupMap buildMap();" << endl << "" << endl << "};" << endl << "" << endl; string namespaceName = name; if( theNamespace != "" ) { f << "} // " << theNamespace << endl << endl; namespaceName = theNamespace + "::" + namespaceName; } f << "std::ostream& operator<<( std::ostream& os, const "<<namespaceName<<"& e );" << endl << "bool operator!=(const "<<namespaceName<<"::Data& pOther, const "<<namespaceName<<"& e );" << endl << "bool operator==(const "<<namespaceName<<"::Data& pOther, const "<<namespaceName<<"& e);" << endl <<"" << endl <<"#endif //" << define << endl;}void writeSource(const string& name) { ofstream f((name + ".cpp").c_str()); f << "#include \"" << name << ".hpp\"" << endl <<"" << endl <<"#include <cassert>" << endl <<"" << endl; string namespaceName = name; if( theNamespace != "" ) { f << "namespace " << theNamespace << " {" << endl << endl; namespaceName = theNamespace + "::" + namespaceName; } f << name<<"::"<<name<<"(Data pd) :d(pd) {" << endl <<" assert("<<name<<"::isValid(pd));" << endl <<"}" << endl <<"" << endl <<name<<"::"<<name<<"(const std::string& pString) :d( asData(pString) ) {" << endl <<"}" << endl <<"" << endl <<"// assignment" << endl <<"void "<<name<<"::operator=(const "<<name<<"& pOther) {" << endl <<" assert("<<name<<"::isValid(pOther.d));" << endl <<" d = pOther.d;" << endl <<"}" << endl <<"" << endl <<"void "<<name<<"::operator=(const Data& pOther) {" << endl <<" assert("<<name<<"::isValid(pOther));" << endl <<" d = pOther;" << endl <<"}" << endl <<"" << endl <<"// equality" << endl <<"bool "<<name<<"::operator==(const "<<name<<"& pOther) const {" << endl <<" return "<<name<<"::isEqual(d, pOther.d);" << endl <<"}" << endl <<"" << endl <<"bool "<<name<<"::operator==(const Data& pOther) const {" << endl <<" return "<<name<<"::isEqual(d, pOther);" << endl <<"}" << endl <<"" << endl <<"// inequality" << endl <<"bool "<<name<<"::operator!=(const "<<name<<"& pOther) const {" << endl <<" return !"<<name<<"::isEqual(d, pOther.d);" << endl <<"}" << endl <<"" << endl <<"bool "<<name<<"::operator!=(const Data& pOther) const {" << endl <<" return !"<<name<<"::isEqual(d, pOther);" << endl <<"}" << endl <<"" << endl <<"const char* "<<name<<"::asString(Data d) {" << endl <<" switch(d) {" << endl;for(list<Enum>::iterator e=enums.begin(); e!= enums.end(); ++e) {f << " case "<< e->name << ":" << endl <<" return \"" << e->stringName << "\";" << endl;}f << " default:" << endl <<" return \"Unknown\";" << endl <<" }" << endl <<"}" << endl <<"" << endl <<"const "<<name<< "::Data "<<name<<"::asData(const std::string& pString) {" << endl <<" LookupMap::const_iterator result = sStringToDataMap.find(pString);" << endl <<" if( result == sStringToDataMap.end() ) {" << endl <<" std::ostringstream message;" << endl <<" message << pString << \" is not a valid "<<name<<"\";" << endl <<" throw std::runtime_error(message.str());" << endl <<" }" << endl <<"" << endl <<" // this should not assert" << endl <<" assert( isValid(result->second) );" << endl <<" return result->second;" << endl <<"}" << endl <<"" << endl <<"bool "<<name<<"::isValid(Data d) {" << endl <<" switch(d) {" << endl;for(list<Enum>::iterator e=enums.begin(); e!= enums.end(); ++e) { f << " case " << e->name << ":" << endl;}f << " return true;" << endl <<" default:" << endl <<" return false;;" << endl <<" }" << endl <<"}" << endl <<"" << endl <<"const char* "<<name<<"::toString() const {" << endl <<" return asString(d);" << endl <<"}" << endl <<"" << endl <<"bool "<<name<<"::isEqual(const Data& a, const Data& b) {" << endl <<" assert("<<name<<"::isValid(a));" << endl <<" assert("<<name<<"::isValid(b));" << endl <<" return a == b;" << endl <<"}" << endl <<"" << endl <<name<<"::operator Data() const {" << endl << " return d;" << endl <<"}" << endl <<"" << endl <<""<<name<<"::"<<name<<"() {" << endl <<"}" << endl <<"const "<<name<<"::LookupMap "<<name<<"::buildMap() {" << endl <<" LookupMap map;" << endl;for(list<Enum>::iterator e=enums.begin(); e!= enums.end(); ++e) { std::string result = e->stringName; std::transform(result.begin(), result.end(), result.begin(), tolower); f << " map.insert(LookupPair(\"" << result << "\", " << e->name << ") );" << endl;}f << " return map;" << endl <<"}" << endl <<"" << endl <<""<<name<<"::LookupMap "<<name<<"::sStringToDataMap = "<<name<<"::buildMap();" << endl <<"" << endl; if( theNamespace != "" ) { f << "} // " << theNamespace << endl << endl; } f << "std::ostream& operator<<( std::ostream& os, const "<<namespaceName<<"& e ) {" << endl <<" os << e.toString();" << endl <<" return os;" << endl <<"}" << endl <<"bool operator!=(const "<<namespaceName<<"::Data& pOther, const "<<namespaceName<<"& e ) {" << endl <<" return e != pOther;" << endl <<"}" << endl <<"" << endl <<"bool operator==(const "<<namespaceName<<"::Data& pOther, const "<<namespaceName<<"& e) {" << endl <<" return e == pOther;" << endl <<"}" << endl;}void writeConvertions(const string& name) { for(map<string, Define>::iterator d=defines.begin(); d!=defines.end(); ++d) { const string file = "convert" + name + "Between" + d->second.name; // header { ofstream f( (file + ".hpp").c_str()); const string define = toUpper(file) + "_HPP"; f << "#ifndef " << define << endl; f << "#define " << define << endl << endl; f << "#include \"" << name << ".hpp\"" << endl; if( d->second.header != "no_header" ) { f << "#include " << d->second.header << endl; } f << endl; if( theNamespace != "" ) { f << "namespace " << theNamespace << " {" << endl; } f << "namespace convert {" << endl; if( d->second.fromData != "_none" ) { f << " const " << d->second.returnType << " " << name << "To" << d->second.name << "(" << name<< "::Data d);" << endl; } if( d->second.toData != "_none" ) { f << " " << name<< "::Data " << d->second.name << "To" << name << "(const " << d->second.returnType << " d);" << endl; } f << "} // convert" << endl << endl; if( theNamespace != "" ) { f << "} //" << theNamespace << endl << endl; } f << "#endif // " << define << endl; } // source { ofstream f( (file + ".cpp").c_str()); f << "#include <sstream>" << endl; f << "#include \"" << file << ".hpp\"" << endl << endl; if( theNamespace != "" ) { f << "namespace " << theNamespace << " {" << endl; } f << "namespace convert {" << endl; if( d->second.fromData != "_none" ) { f << " const " << d->second.returnType << " " << name << "To" << d->second.name << "(" << name<< "::Data d) {" << endl; f << " switch(d) {" << endl; for(list<Enum>::iterator e=enums.begin(); e!= enums.end(); ++e) { map<string, string>::iterator res = e->values.find(d->second.name); if( res != e->values.end() ) { f << " case " << name<< "::" << e->name << ":" << endl; f << " return " << res->second << ";" << endl; } } f << " default:" << endl; if( d->second.fromData == "exception" ) { f << " {" << endl << " std::ostringstream message;" << endl << " message << " << name << "::asString(d) << \" is not a valid " << d->second.name <<"\";" << endl << " throw std::runtime_error(message.str());" << endl << " }" << endl; } else { f << " return " << d->second.fromData << ";" << endl; } f << " }" << endl; f << " }" << endl; } if( d->second.toData != "_none" ) { f << " " << endl; f << " " << name<< "::Data " << d->second.name << "To" << name << "(const " << d->second.returnType << " d) {" << endl; f << " switch(d) {" << endl; for(list<Enum>::iterator e=enums.begin(); e!= enums.end(); ++e) { map<string, string>::iterator res = e->values.find(d->second.name); if( res != e->values.end() ) { f << " case " << res->second << ":" << endl; f << " return " << name<< "::" << e->name << ";" << endl; } } f << " default:" << endl; if( d->second.toData == "exception" ) { f << " {" << endl << " std::ostringstream message;" << endl << " message << d << \" of type " << d->second.name << " is not a valid " << name <<"\";" << endl << " throw std::runtime_error(message.str());" << endl << " }" << endl; } else { f << " return " << d->second.toData << ";" << endl; } f << " }" << endl; f << " }" << endl; } f << "} // convert" << endl << endl; if( theNamespace != "" ) { f << "} //" << theNamespace << endl << endl; } } }}unsigned long lineno = 0;unsigned long errorCount = 0;string file = "";string line;void parseError(const string& reason) { cerr << file << "(" << lineno << "): " << reason << endl << line << endl; ++errorCount;}int main(int nArgs, char **args) { cerr << "C++ enum builer made by sirGustav" << endl; if( nArgs == 2 ) { const string name = args[1]; file = name + ".enum"; cout << file << endl; ifstream f(file.c_str()); if( !f.good() ) { cerr << "Failed to open " << file << "!"; cin.get(); return 0; } lineno = 0; errorCount = 0; while( getline(f, line) ) { ++lineno; line = trim(line); if( line.length() > 0 && line[0] != '#' ) { if( beginsWith(line, "namespace! ") ) { vector<string> commands; splitString(line, "!", &commands); if( commands.size()==2 ) { theNamespace = trim( commands[1] ); } else { parseError("Too few or to many namespace parameters"); } } else if( beginsWith(line, "convert!") ) { vector<string> commands; splitString(line, "!", &commands); if( commands.size()==2 ) { Define def; vector<string> data; splitString(commands[1], ";", &data); for(vector<string>::iterator d=data.begin(); d!=data.end(); ++d) { vector<string> values; splitString(*d, "=", &values); if( values.size() == 2 ) { const string name = toLower(trim(values[0])); const string value = trim(values[1]); if( name=="name" ) { def.name = value; } else if( name=="return" ) { def.returnType = value; } else if( name=="header" ) { def.header = value; } else if( name=="from" ) { def.fromData = value; } else if( name=="to" ) { def.toData = value; //} else if( name=="name" ) { } else { parseError(name + " is a bad name"); } } else { parseError("Bad assignment structure"); } } defines[def.name] = def; } else { parseError("line should only contain one !"); break; } } else { vector<string> commands; splitString(line, ":", &commands); if( commands.empty() ) { parseError("Not enough commands in data"); } else { Enum e; e.stringName = trim(commands[0]); e.name = toIdent(e.stringName); if( commands.size() > 1 ) { vector<string> data; splitString(commands[1], ";", &data); for(vector<string>::iterator d=data.begin(); d!=data.end(); ++d) { vector<string> values; splitString(*d, "=", &values); if( values.size() == 2 ) { const string type = trim(values[0]); map<string, Define>::iterator res = defines.find(type); if( res == defines.end() ) { parseError(type + " is not a valid type"); } else { e.values[type]=trim(values[1]); } } else { parseError("Bad assignment code"); } } } enums.push_back(e); } } } } if( errorCount > 0 ) { cerr << endl << errorCount << " error(s) detected, please fix" << endl; } else { cout << "Writing files..." << endl; writeHeader(name); writeSource(name); writeConvertions(name); } } else { cerr << "Usage " << args[0] << " [filename.enum]" << endl; cerr << "A file called Day.enum exists, you should execute " << args[0] << " Day"<< endl; } return 0;}
hth