关于c ++:将错误代码转换为要显示的字符串


Translate error codes to string to display

C++中有没有一种常见的方法将错误代码翻译成字符串来显示它呢?

我在某个地方看到了一个带有大开关的err2msg函数,但这真的是最好的方法吗?


由于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中,您可以使用FormatMessage(...)函数,或者使用GetLastError()函数返回的错误代码,或者直接使用可疑区域。

请参阅下面的链接以获取示例。

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;
}

(这些begin()end()函数可以在这里找到。)


就我而言,错误代码只是枚举的一个子集。因为在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文件,则会多次创建静态错误对象(这就是为什么我在构造函数中执行调试测试以检查映射的一致性)。如果您没有成千上万的错误代码,那么这应该是一个问题(可能还有一个解决方法…)

牛仔