Is there a common way in C++ to translate an error code to a string to display it?
I saw somewhere a err2msg
function, with a big switch, but is that really the best way?
Is there a common way in C++ to translate an error code to a string to display it?
I saw somewhere a err2msg
function, with a big switch, but is that really the best way?
In windows you can use FormatMessage(...)
function either with error code return by GetLastError()
function or directly to the suspected area.
Please see below links for examples.
http://msdn.microsoft.com/en-us/library/ms679351(v=VS.85).aspx
http://msdn.microsoft.com/en-us/library/ms680582(v=VS.85).aspx
I hope this will help you.
The big switch is not that bad for this. To get a string for an error code is almost always not performance critical.
You should keep in mind that these error strings are probably not what you want to show your users. The messeges for the user should be kept in resources for easier translation.
strings for error codes are for logs or diagnostics and need no translation.
You can use this trick to define your error codes and the strings in parrallel:
#if defined(ERROR_BUILD_ARRAY)
#define ERROR_START \
static const err_defn error_table[] = { \
{ WARNING, "Warning" },
#define ERRDEF(num, offset, str) { num, str },
#define ERROR_END { 0, NULL } };
#elif !defined(ERROR_ENUM_DEFINED)
#define ERROR_START \
typedef enum svn_errno_t { \
WARNING = OS_START_USERERR + 1,
#define ERRDEF(num, offset, str) /** str */ num = offset,
#define ERROR_END ERR_LAST } svn_errno_t;
#define ERROR_ENUM_DEFINED
ERROR_START
ERRDEF(ERR_BAD_BAD,
ERR_BAD_CATEGORY_START + 0,
"Bad error")
ERRDEF(ERR_BAD_FILENAME,
ERR_BAD_CATEGORY_START + 1,
"Bogus filename")
ERROR_END
(Copied from subversion sources)
Since C++ does not allow automatic 'translation' from enum values to enum names or similar, you need a function to do this. Since your error codes are not somehow defined in your O/S you need to translate it by yourself.
One approach is a big switch statement. Another is a table search or table lookup. What's best depends on error code set.
table search can be defined in this way:
struct {
int value;
char* name;
} error_codes[] = {
{ ERR_OK, "ERR_OK" },
{ ERR_RT_OUT_OF_MEMORY, "ERR_RT_OUT_OF_MEMORY" },
{ 0, 0 }
};
char* err2msg(int code)
{
for (int i = 0; error_codes[i].name; ++i)
if (error_codes[i].value == code)
return error_codes[i].name;
return "unknown";
}
I tend to avoid the switch since it's usually a big piece of code. I prefer a table lookup along the lines of:
In btree.h:
enum btreeErrors {
ZZZ_ERR_MIN = -1,
OKAY,
NO_MEM,
DUPLICATE_KEY,
NO_SUCH_KEY,
ZZZ_ERR_MAX };
In btree.c:
static const char *btreeErrText[] = {
"Okay",
"Ran out of memory",
"Tried to insert duplicate key",
"No key found",
"Coding error - invalid error code, find and destroy developer!"
};
const char *btreeGetErrText (enum btreeErrors err) {
if ((err <= ZZZ_ERR_MIN) || (err >= ZZZ_ERR_MAX))
err = ZZZ_ERR_MAX;
return btreeErrText[err];
}
Not that it usually matters since errors should be the exception rather than the rule, but table lookups are generally faster than running big switch statements (unless they get heavily optimised).
As far as I am concerned, error codes are just a subset of enums. Since we are not blessed in C++ with pretty enums (which makes logs somehow quite hard to parse), error codes are no more easier.
The solution is pretty simple for error codes though:
class ErrorCode
{
public:
ErrorCode(): message(0) {}
explicit ErrorCode(char const* m): message(m) {}
char const* c_str() const { return message; }
std::string toString() const
{
return message ? std::string(message) : std::string();
}
private:
char const* message;
};
std::ostream& operator<<(std::ostream& out, ErrorCode const& ec)
{
return out << ec.c_str();
}
Of course you can supply the traditional ==
, !=
, <
, etc...
The idea is to return pointers to the text instead of error codes (though wrapped in a class for type safety).
Usage:
// someErrors.h
extern ErrorCode const ErrorOutOfMemory;
// someErrors.cpp
ErrorCode const ErrorOutOfMemory = ErrorCode("OUT OF MEMORY");
Similar to harper's idea, but a bit more generalized:
typedef std::map<int, const char*> error_code_tbl_t;
typedef error_code_tbl_t::value_type error_code_entry_t;
const error_code_entry_t error_code_tbl_[] = {
{ ERR_OK , "ERR_OK" },
{ ERR_RT_OUT_OF_MEMORY, "ERR_RT_OUT_OF_MEMORY" },
// ...
};
const error_code_tbl_t error_code_tbl( begin(error_code_tbl_)
, end (error_code_tbl_) );
const char* err2msg(int code)
{
const error_code_tbl_t::const_iterator it = error_code_tbl.find(code);
if(it == error_code_tbl.end())
return "unknown";
return it->second;
}
(Those begin()
and end()
functions can be found here.)