Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mapping error codes to string in C++

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.

like image 764
Gediminas Avatar asked Nov 29 '22 01:11

Gediminas


2 Answers

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";
}
like image 63
Mankarse Avatar answered Dec 10 '22 10:12

Mankarse


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...)

like image 28
jpo38 Avatar answered Dec 10 '22 10:12

jpo38