2

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?

Mike
  • 2,393
  • 3
  • 25
  • 37
  • 1
    I would think you would do better to write (by hand) a function that translates the codes into a human-readable error message. That can be as simple as a switch() statement that returns a string constant. Many libraries already provide such a thing (e.g., ldap_err2string() for LDAP); but if your 3rd party library doesn't provide it for you, it might still be worth spending a little time coding one up by hand. – David Gelhar Jul 29 '10 at 16:07
  • 1000's of messages, most of them I don't know what they mean? – Mike Jul 31 '10 at 03:34

3 Answers3

1

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

Stu
  • 1,497
  • 1
  • 13
  • 24
1

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]);
}
Community
  • 1
  • 1
JayG
  • 4,339
  • 3
  • 23
  • 19
  • 1
    Very interesting elegant solution if you'd have control over the constants you're defining, but it's a third party library (for special dedicated hardware), which defines 1000's of messages and error codes... – Mike Jul 31 '10 at 03:40
0

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.

Martin Beckett
  • 94,801
  • 28
  • 188
  • 263
  • Partially helpful. Cpp would show what defines ended up getting executed (depending on other ifdefs). Still need my awk script. – Mike Jul 31 '10 at 03:44