Can a C program determine the name of a macro ID / enum based on its value?

Let's say we define some error codes as macros -

#define ERR_SUCCESS 0
#define ERR_BAD_INPUT 1

      

...

or as an enumerated data type -

enum err_t = { ERR_SUCCESS, ERR_BAD_INPUT, ...};

      

and one of these ids is returned by a function such as -

int foo(); /* if foo() returns 0, it means success, etc */

      

Can the caller foo()

determine which identifier / name (ERR_SUCCESS, ERR_BAD_INPUT, ...) is associated with the returned int value?

+3


source to share


5 answers


Not directly, as pointed out by others, these ids are not available at runtime, but you can use a parallel list of names ( X Macro might help):

#include <stdio.h>

#define ERRS \
    X(ERR_SUCCESS) \
    X(ERR_BAD_INPUT) \
    X(ERR_MORE)

#define X(x) x,
enum err_t {ERRS};
#undef X

#define X(x) #x,
static char *err_name[] = {ERRS};
#undef X

static int foo(void)
{
    /* ... */
    return ERR_BAD_INPUT;
}

int main(void)
{
    printf("%s\n", err_name[foo()]);
    return 0;
}

      



Output:

ERR_BAD_INPUT

      

+2


source


Not.

Processor macros, as the name suggests, are resolved in preprocessing prior to compilation. When called, that is, at runtime, there are no references to type names ERR_SUCCESS

, ERR_BAD_INPUT

etc., and therefore you cannot determine the name of an identifier at runtime.



Simliar reasoning for enum

, since it enum

creates compile-time constants and you cannot get the name of an identifier at runtime.

+6


source


Since C99, Keine Lust macros can even be extended to set values ​​explicitly:

#define X(x) x,
#define X_WITH_VALUE(x, v) x = v,

      

and

#define X(x) [x] = #x,
#define X_WITH_VALUE(x, v) X(x)

      

finally the function:

char const* getName(enum E e)
{
    char const* n = e < sizeof(arr)/sizeof(*arr) ? arr[e] : NULL;
    return n ? n : "<unknown error>";
}

      

Edit (in response to comment): Assigning values ​​explicitly allows for (desired!) Spaces and synonyms:

enum Flags
{
    None = 0,
    F0   = 1,
    F1   = 2,
    F2   = 4,
    F3   = 8,
    G3   = F3,
}

      

This will lead to a break in the array, although it will lead to the need to check for a null pointer in the function. If your enum values ​​turn out to be large, the array can get huge, so the array solution may not be suitable anymore ...

Synonyms impose another problem, see Jens. The problem is partially solved, the code does work, but you don't necessarily return the originally used synonym, but always the last one is defined! In the example above, it will be G3 even if you used F3 in your code. So you have to define your desired synonym for last (which is a bit unnatural for me) or you use the SYNONYM macro below.

Some special cases of array size can be solved with additional tricks, for example. d. if you have subsequent values ​​with a higher starting value:

#define X(x) [x - Initial] = #x,
char const* n = e < sizeof(arr)/sizeof(*arr) ? arr[e - Initial] : NULL;

      

Interestingly, it receives powers from the two listed above:

#define X(x, p) x = (1 << p),
enum err_t {None = 0, ERRS};

char const* getName(enum E e)
{
    if(e == None)
        return S(None); // stringification macro, yet to be defined
    // check, if power of two:
    if((unsigned int) e & ((unsigned int) e - 1))
    {
        int index = log2(e);
        if(index < sizeof(arr)/sizeof(*arr)
            return arr[index];
    }
    return "<unknown>";
}

      

The power of two checks comes from Sean Aaron Anderson ( here for sure ), you will find good solutions for calculating log2 there too, e. Mr. this .

Completely different approach, suitable for any different values ​​(but synonyms must be handled explicitly!):

#define X(x) x,
#define X_WITH_VALUE(x, v) x = v,
#define SYNONYM(y, x)      y = x,

#define X(x) case x: return #x;
#define X_WITH_VALUE(x, v) X(x)
#define SYNONYM(y, x)
char const* getName(enum E e)
{
    switch(e)
    {
        ERRS
        default:
            return "<unknown>";
    }
}

      

+2


source


Not. This is easy to see when you understand that matching values ​​to names is not bijective . What a fancy way of saying that macros like

#define ZERO   0
#define ZILCH  0
#define NADA   0
#define NIENTE 0

      

allowed to coexist. Given 0, which is the associated name?

The same argument applies to identifiers enum

.

+1


source


No, it is not supported by the compiler. The compiler gets the output of the C # defines preprocessor replaced with these values. You need to define values ​​in your code. such as in this question .

0


source







All Articles