问题
What would be a more efficient way of mapping error codes from enumeration to a string? (in C++)
For example, now I'm doing something like this:
std::string ErrorCodeToString(enum errorCode)
{
switch (errorCode)
{
case ERROR_ONE: return "ERROR_ONE";
case ERROR_TWO: return "ERROR_TWO";
...
default:
break;
}
return "UNKNOWN";
}
Would it be more efficient in any way if I would do something like this?:
#define ToStr( name ) # name;
std::string MapError(enum errorCode)
{
switch (errorCode)
{
case ERROR_ONE: return ToStr(ERROR_ONE);
case ERROR_TWO: return ToStr(ERROR_TWO);
...
default:
break;
}
return "UNKNOWN";
}
Maybe anyone have any suggestions or thoughts on this? Thanks.
回答1:
If you are going to use a macro, why not go all the way:
std::string MapError(enum errorCode)
{
#define MAP_ERROR_CODE(code) case code: return #code ;
switch (errorCode)
{
MAP_ERROR_CODE(ERROR_ONE)
MAP_ERROR_CODE(ERROR_TWO)
...
}
#undef MAP_ERROR_CODE
return "UNKNOWN";
}
回答2:
I wanted a way to have error code (int) and string description (any string) be declared in one and only one single place and none of the examples above allows that.
So I declared a simple class storing both int and string and maintaining a static map for int->string conversion. I also added an "auto-cast to" int function:
class Error
{
public:
Error( int _value, const std::string& _str )
{
value = _value;
message = _str;
#ifdef _DEBUG
ErrorMap::iterator found = GetErrorMap().find( value );
if ( found != GetErrorMap().end() )
assert( found->second == message );
#endif
GetErrorMap()[value] = message;
}
// auto-cast Error to integer error code
operator int() { return value; }
private:
int value;
std::string message;
typedef std::map<int,std::string> ErrorMap;
static ErrorMap& GetErrorMap()
{
static ErrorMap errMap;
return errMap;
}
public:
static std::string GetErrorString( int value )
{
ErrorMap::iterator found = GetErrorMap().find( value );
if ( found == GetErrorMap().end() )
{
assert( false );
return "";
}
else
{
return found->second;
}
}
};
Then, you simply declare your error codes as below:
static Error ERROR_SUCCESS( 0, "The operation succeeded" );
static Error ERROR_SYSTEM_NOT_INITIALIZED( 1, "System is not initialised yet" );
static Error ERROR_INTERNAL( 2, "Internal error" );
static Error ERROR_NOT_IMPLEMENTED( 3, "Function not implemented yet" );
Then, any function returning int can do to return 1
return ERROR_SYSTEM_NOT_INITIALIZED;
And, client programs of your library will get "System is not initialised yet" when calling
Error::GetErrorString( 1 );
or:
Error::GetErrorString( ERROR_SYSTEM_NOT_INITIALIZED );
The only limitation I see is that static Error objects are created many times if .h file declaring them is included by many .cpp (that's why I do a _DEBUG test in constructor to check consistency of the map). If you don't have thousands of error code, it should not be a problem (and there may be a workaround...)
回答3:
enum errors {
error_zero,
error_one,
error_two
};
namespace {
const char *error_names[] = {
"Error one",
"Error two",
"Error three"
};
}
std::string map_error(errors err) {
return error_names[err];
}
回答4:
Your suggested alternative isn't any more efficient, but you could improve things in two ways:
- You clearly have a duplication between the
errorCode
enum, and this function. - You also have some sort of duplication in your function since the enum value has the same name as the string gives.
You can fix both with a little preprocessor magic:
// This is your definition of available error codes
#define ERROR_CODES \
ERROR_CODE(ERROR_ONE) \
ERROR_CODE(ERROR_TWO) \
ERROR_CODE(ERROR_THREE)
// Define ERROR_CODE macro appropriately to get a nice enum definition
#define ERROR_CODE(a) ,a
enum ErrorCode {
None,
ERROR_CODES
};
#undef ERROR_CODE
// Define ERROR_CODE macro differently here to get the enum -> string mapping
std::string MapError(enum errorCode)
{
#define ERROR_CODE(a) case a: return #a;
switch (errorCode)
{
case None: return "None";
ERROR_CODES
}
}
回答5:
No after the preprocessor passes over your code the two will be exactly the same. Only thing is the second approach would be less error-prone to typos.
I would say what you have implemented is already a good solution.
回答6:
I know this is an old thread, but I did like Frerich Raabe's approach and got it to work in VS without errors:
#define ERROR_CODES \
ERROR_CODE(NO_ERROR) \
ERROR_CODE(ERROR_ONE) \
ERROR_CODE(ERROR_TWO) \
ERROR_CODE(ERROR_THREE) \
ERROR_CODE(ERROR_FOUR)
#define ERROR_CODE(code) code,
typedef enum { ERROR_CODES } ErrorCodes;
#undef ERROR_CODE
const char *MapError(const int errorCode)
{
#define ERROR_CODE(code) case code: return #code;
switch (errorCode)
{
ERROR_CODES
default: return "UNKNOWN ERROR";
};
#undef ERROR_CODE
}
来源:https://stackoverflow.com/questions/14279420/mapping-error-codes-to-string-in-c