【问题标题】:Mapping error codes to string in C++将错误代码映射到 C++ 中的字符串
【发布时间】:2012-12-26 02:37:30
【问题描述】:

将错误代码从枚举映射到字符串的更有效方法是什么? (在 C++ 中)

例如,现在我正在做这样的事情:

std::string ErrorCodeToString(enum errorCode)
{
   switch (errorCode)
   {
      case ERROR_ONE:   return "ERROR_ONE";
      case ERROR_TWO:   return "ERROR_TWO";
      ...
      default:
         break;
   }

   return "UNKNOWN";
}

如果我做这样的事情会更有效吗?:

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

也许有人对此有任何建议或想法? 谢谢。

【问题讨论】:

  • 不,这不是更有效。他们是一样的。你知道std::exception 和 C++ 中的异常处理吗?另外,您是否预计会有这么多错误以至于发现它们的效率很重要?
  • 如果您考虑运行时性能,这两者绝对相同。
  • 我是不是没听懂问题?
  • 我会省略 default: 分支;如果你这样做了,那么如果你忘记包含所有代码,你应该会得到一个编译器警告。
  • 如果您有很好的错误代码原因(例如,如果 - 在糟糕的设计中 - 一个函数由于性能关键代码中的不同原因而经常失败,并且您希望自动捕获原因),我建议使用std::error_code 用于处理代码而不是自定义方式:akrzemi1.wordpress.com/2017/07/12/your-own-error-code

标签: c++ string error-code


【解决方案1】:

如果你要使用宏,为什么不一路走下去:

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】:

我想要一种方法,将错误代码 (int) 和字符串描述(任何字符串)声明在一个且仅一个位置,而上述示例均不允许这样做。

所以我声明了一个简单的类,它同时存储了 int 和 string,并为 int->string 转换维护了一个静态映射。我还添加了一个“自动转换为”int 函数:

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

然后,您只需将错误代码声明如下:

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

return ERROR_SYSTEM_NOT_INITIALIZED;

而且,您库的客户端程序在调用时会得到“系统尚未初始化”

Error::GetErrorString( 1 );

或:

Error::GetErrorString( ERROR_SYSTEM_NOT_INITIALIZED );

我看到的唯一限制是,如果声明它们的 .h 文件包含在许多 .cpp 中,则会多次创建静态错误对象(这就是为什么我在构造函数中进行 _DEBUG 测试以检查地图的一致性)。如果您没有成千上万的错误代码,那应该不是问题(并且可能有解决方法...)

【讨论】:

    【解决方案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】:

    您建议的替代方案并没有更有效,但您可以通过两种方式进行改进:

    1. errorCode 枚举和此函数之间显然存在重复。
    2. 您的函数中也存在某种重复,因为枚举值与字符串给出的名称相同。

    你可以用一点预处理魔法来解决这两个问题:

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

    【讨论】:

    • 在枚举中我得到“未声明的标识符”(ERROR_CODES)。
    【解决方案5】:

    不,在预处理器传递您的代码后,两者将完全相同。唯一的问题是第二种方法不太容易出现拼写错误。

    我想说你已经实现的已经是一个很好的解决方案了。

    【讨论】:

      【解决方案6】:

      我知道这是一个旧线程,但我确实喜欢 Frerich Raabe 的方法并让它在 VS 中正常工作:

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

      【讨论】:

        猜你喜欢
        • 2013-03-30
        • 1970-01-01
        • 1970-01-01
        • 2019-05-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-11-25
        • 1970-01-01
        相关资源
        最近更新 更多