This is more of an aesthetic question than a purely functional one, but I want to know if there's any answer. See, I'm programming an error reporter for a project I've recently begun working on, and it's mostly code-based for ease of function use. Think of errno
, with various values defined to note specific problems. It's a prevalent system among error handlers. However, I don't want to just give the user a code, because I know from the time before I started programming that a random array of numbers can often be overwhelming when trying to self-diagnose the problem.
I want to give them a string representation of the code. However, I cannot think of a pragmatic way to do this. A stringify (#
) macro wouldn't work because the error code sent to the function is unknown at compile time. Is there something beyond a large switch that could rectify this issue?
For context, this is my little error logger; this is the last step of the error pipeline, if the program gets here, a fatal error has occurred.
// Notes: We don't do a lot of error-checking in this function, beyond
// the stuff built into PushNotification. The reason for that is
// fairly simple; at this point, we don't give a darn. If the program
// gets here, something's irreversibly wrong regardless.
// Last edit: July 3rd, 2024
_Noreturn void InternalLogError(const char* caller, const char* file, u32 line,
u32 code)
{
char error_message[128];
// This is the place in which I want to add the code-to-string bit.
snprintf(error_message, 128,
"\nMemphim failed catastrophically.\n"
"Code: %d.\nCaller: %s @ %s\nLine: %d\n",
code, caller, file, line);
if (CheckShellAvailability()) puts(error_message);
else PushNotification("Memphim Error Reporter", error_message);
exit(EXIT_FAILURE);
}
A string look-up table would be one obvious way to do it.
typedef enum
{
ERR_NONE,
ERR_SERVERS_ON_FIRE,
ERR_LIVE_BEAVERS,
ERR_N // total number of supported errors
} err_t;
static const char* err_str[] =
{
[ERR_NONE] = "No error",
[ERR_SERVERS_ON_FIRE] = "The servers are on fire",
[ERR_LIVE_BEAVERS] = "Live beavers in the server room",
};
static_assert(sizeof(err_str)/sizeof(*err_str) == ERR_N,
"err_t and err_str are not consistent with each other");
...
void InternalLogError(..., err_t code)
{
puts(err_str[code]);
}
Or in case avoiding storing the values at separate places must be avoided, the X-macro version:
#define ERR_LIST(X) \
/* code str */ \
X(ERR_NONE, "No error") \
X(ERR_SERVERS_ON_FIRE, "The servers are on fire") \
X(ERR_LIVE_BEAVERS, "Live beavers in the server room")
typedef enum
{
#define ERR_T_ENUM(code, str) code,
ERR_LIST(ERR_T_ENUM)
ERR_N // total number of supported errors
} err_t;
static const char* err_str[] =
{
#define ERR_STR_LUT(code, str) [code] = str,
ERR_LIST(ERR_STR_LUT)
};