Translate error codes to string to display
C++中有没有一种常见的方法将错误代码翻译成字符串来显示它呢?
我在某个地方看到了一个带有大开关的
由于C++不允许从EnUM值自动转换为枚举名或类似的名称,所以需要一个函数来完成这个操作。由于您的操作系统中没有定义错误代码,因此您需要自己对其进行翻译。
一种方法是大开关语句。另一种是表搜索或表查找。什么最好取决于错误代码集。
表搜索可以这样定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | struct { int value; const char* name; } error_codes[] = { { ERR_OK,"ERR_OK" }, { ERR_RT_OUT_OF_MEMORY,"ERR_RT_OUT_OF_MEMORY" }, { 0, 0 } }; const 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"; } |
在Windows中,您可以使用
请参阅下面的链接以获取示例。
http://msdn.microsoft.com/en-us/library/ms679351(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/ms680582(v=vs.85).aspx
我希望这对你有帮助。
类似于哈珀的想法,但更广义一点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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; } |
(这些
就我而言,错误代码只是枚举的一个子集。因为在C++中,我们不使用漂亮的枚举(这使得日志很难解析),所以错误代码就不那么容易了。
但是,对于错误代码,解决方案非常简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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(); } |
当然可以提供传统的
- 很简单!
- 它很快(代码是字符串,不涉及查找)
- 它是类型安全的(你不能意外地把它和另一个类型混淆)
其思想是返回指向文本的指针,而不是错误代码(尽管为了类型安全而包装在类中)。
用途:
1 2 3 4 5 | // someErrors.h extern ErrorCode const ErrorOutOfMemory; // someErrors.cpp ErrorCode const ErrorOutOfMemory = ErrorCode("OUT OF MEMORY"); |
大开关对这件事并不坏。获取错误代码的字符串几乎总是不关键性能的。
您应该记住,这些错误字符串可能不是您想要向用户显示的内容。为了便于翻译,用户的信息应该保存在资源中。
错误代码字符串用于日志或诊断,不需要翻译。
您可以使用这个技巧来定义错误代码和parallel中的字符串:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | #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 |
(从子版本源复制)
我倾向于避免切换,因为它通常是一段很大的代码。我更喜欢沿以下行进行表查找:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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]; } |
这并不重要,因为错误应该是异常而不是规则,但是表查找通常比运行大型switch语句更快(除非它们得到了高度优化)。
我想要一种方法让错误代码(int)和字符串描述(任何字符串)声明在一个且只有一个位置,上面的例子都不允许(err_ok必须声明在某个位置,然后"err_ok"映射到其他位置)。
所以我声明了一个简单的类,它存储int和string,并为int->string转换维护一个静态映射。我还添加了一个"auto-cast to"int函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | 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; } } }; |
然后,只需声明以下错误代码:
1 2 3 4 | 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" ); |
然后,任何返回int的函数都可以返回1
1 | return ERROR_SYSTEM_NOT_INITIALIZED; |
而且,在调用时,库的客户端程序将得到"系统尚未初始化"
1 | Error::GetErrorString( 1 ); |
我看到的唯一限制是,如果许多.cpp都包含声明静态错误对象的.h文件,则会多次创建静态错误对象(这就是为什么我在构造函数中执行调试测试以检查映射的一致性)。如果您没有成千上万的错误代码,那么这应该是一个问题(可能还有一个解决方法…)
牛仔