Mike
Mike

Reputation: 2464

extract string messages from set of #define

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

Answers (3)

Martin Beckett
Martin Beckett

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

JayG
JayG

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

Stu
Stu

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

Related Questions