Error class / Showing variable names

Started by
1 comment, last by sirGustav 16 years, 4 months ago
Ive created an enum with some error codes and they get used during functions as return values if something goes wrong. They tell me exactly where in the code it happened and what/how it goes wrong. Now in a console app its easy enough to cout the error code. In a windows app, I try to get it to popup a message box with the error info. It doesnt give me the right codes, just random numbers. In both cases Id like it to show the enum symbol instead of the integer value. Any way to do this?

enum ErrorCode
{
   ...
   DDrawOK		= 0x00000F00,    // I want it to show DDrawOK
   DDrawCreateFailed,                    // instead of 3840
   DDrawQueryFailed
   ...
};
And the windows error function

void CheckError(ErrorCode error)
{
	if (error != DDrawOK)
	{
		MessageBox(NULL, (LPCWSTR)error,
		L"error code #",
		MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);
	}
}
It would get used like this, with DDrawCreate() returning an ErrorCode...

CheckError(Create());
Ive tried a couple other ways, but Im open to suggestions. I know some of you out there do this kind of thing. Thanks
Advertisement
C++ has no native functionality for automatically obtaining an identifier name at runtime. One solution is that you could use DXGetErrorDescription() or DXGetErrorString() to map error codes to strings. Both functions support Windows API errors as well as DirectX errors. Then map the values of your enum to the appropriate Windows or DirectX error codes (or skip the enum entirely).
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

This topic is closed to new replies.

Advertisement