Reputation: 2464
I'm using a third party library that has a lot of different error codes. An include files contains a whole bunch of lines like:
#define ABC_INVALID_BUFFER_SIZE 101
#define ABC_INVALID_COMMAND 102
etc.
At runtime, I'm getting various error codes as I'm developing my application. I want to, at runtime, have the application print out messages like "error: ABC_INVALID_COMMAND", instead of it printing "error: 102".
I want to have a translation map of sorts that allows me to convert the numbers to text using map[code].
So what I'm looking for is a mechanism that lets me, at compile time, parse the include file, and convert it into map[102] = string("ABC_INVALID_COMMAND"). I can do this using an awk script, but I'm wondering if there is a mechanism that already exists. Surely I can't be the first that wants/needs to do this? Something obvious I haven't discovered yet?
Upvotes: 2
Views: 1364
Reputation: 96147
It can't be done by the language, the pre-processor remaps the strings to their values before the compiler sees it.
Common solutions are:
1, A script to generate the list of constants together with a, matching set of #defined strings.
Or generate a function with a big switch statement to return string values.
2, replace the values with an array of structs
struct {
int value;
char *name;
} values;
and initialize it with a pairs of constant,"description". Depending on how you are using the #defines this might not be possible.
I would write a script (awk/python/etc) to read a text file of:
AVALUE 12
BVALUE 23
CVALUE 45
And spit out a .h file
#define AVALUE_S "AVALUE"
#define AVALUE 12
#define BVALUE_S "BVALUE"
#define BVALUE 23
Then in you code you can just use AVALUE_S wherever you need the human readable string.
Upvotes: 0
Reputation: 4459
Absolutely! This can be done with X-Macros using the preprocessor for the compiler. I use this a lot to associate error codes with strings.
Full description: What is a good reference documenting patterns of use of X-Macros in C (or possibly C++)?
For your specific question:
/*
* X Macro() data list
* - Add all new values here (and only here!)
* Format: Enum, Value
*/
#define X_ABC_ERROR \
X(ABC_INVALID_BUFFER_SIZE, 101) \
X(ABC_INVALID_COMMAND, 102) \
X(ABC_SOME_OTHER_ERROR, 200)
/*
* Build an array of error return values
* e.g. {101,102}
*/
static int ErrorVal[] =
{
#define X(Enum,Val) Val,
X_ABC_ERROR
#undef X
};
/*
* Build an array of error enum names
* e.g. {"ABC_INVALID_BUFFER_SIZE","ABC_INVALID_COMMAND"}
*/
static char * ErrorName[] = {
#define X(Enum,Val) #Enum,
X_ABC_ERROR
#undef X
};
/*
* Create an enumerated list of error indexes
* e.g. IDX_ABC_INVALID_BUFFER_SIZE = 0, IDX_ABC_INVALID_COMMAND = 1, ...
*/
enum {
#define X(Enum,Val) IDX_##Enum,
X_ABC_ERROR
#undef X
IDX_MAX /* Array size */
};
/*
* Sample function to show error codes/names
*/
static void showErrorInfo(void)
{
int i;
/*
* Access the values
*/
for (i=0; i<IDX_MAX; i++)
printf(" %s == %d\n", ErrorName[i], ErrorVal[i]);
}
Upvotes: 1
Reputation: 1567
You're certainly not the first to want this function, I would have loved it too.
Unfortunately, you'll need to keep using your script. I would have a script that ran make and then your script each time.
Even though the c pre-processor scraps the #define's label , you can still access them:
cpp -dM foo.h
Returns:
(some others)
#define A 1
#define B 2
You will find that you will still need to play around with the return values.
More info at http://gcc.gnu.org/onlinedocs/gcc-4.5.0/gcc/Preprocessor-Options.html#Preprocessor-Options
Upvotes: 1