简体   繁体   中英

How to get C to find the macro identifier given the numeric macro value?

Say I have some C code like:

#define FOO 2
#define BAR 3
#define BAZ 500

void get_value(int val) {
  printf("The value is %s\n", some_function_or_macro_or_something(val));
}

int main(int argc, char** argv) {
  get_value(BAZ);
  get_value(FOO);
  return 0;
}

and I want it to print out "The value is BAZ" and "The value is FOO". But there are thousands of #defines, so I want to do it programmatically.

C is definitely capable of doing this using # stringizing operator, but you need to call macro with identifier directly:

#include <stdio.h>

#define FOO 2
#define BAR 3
#define BAZ 500

#define get_value(val)     \
  printf("The value is %s\n", #val);

int main(void)
{
  get_value(BAZ);
  get_value(FOO);

  return 0;
}

In your example after passing value of eg BAZ to get_value function, the token BAZ is not recognized anymore, as arguments are passed by value. In other words macro identifier is gone (of course you can still stringize its replacement, see comment below).

What can I do more?

Here is some small hack to obey this "top-level" rule. Assuming that you have small amount of object-like macros (constants) with unique values, then you can do something like:

#include <stdio.h>

#define FOO 2
#define BAR 3
#define BAZ 500

#define STRINGIFY(val) #val

void get_value(int val) {
  switch (val) {
    case FOO :
      printf("The value is %s\n", STRINGIFY(FOO)); break;
    case BAR :
      printf("The value is %s\n", STRINGIFY(BAR)); break;
    case BAZ :
      printf("The value is %s\n", STRINGIFY(BAZ)); break;
  }
}

int main(int argc, char* argv) {
  get_value(BAZ);
  get_value(FOO);
  return 0;
}

Result:

The value is BAZ
The value is FOO

Use the right tool for the job. Which tool knows all about macros? A compiler. GNU gcc and clang both allow to dump the list of defined macros:

$ cc -E -dM -x c /usr/include/stdio.h
#define BUFSIZ 1024
#define EOF (-1)
#define FILENAME_MAX 1024
#define FOPEN_MAX 20
#define L_ctermid 1024
#define L_cuserid 17
#define L_tmpnam 1024
#define NULL ((void *)0)
#define P_tmpdir "/tmp/"
#define SEEK_CUR 1
#define SEEK_END 2
#define SEEK_SET 0
/* etc */

will list the whole shebang defined by <stdio.h> and everything it includes. If you need to process this data programmatically in a C program,

#include <stdio.h>

#define BIG_ENOUGH   1024

int main (void)
{
    char buffer[BIG_ENOUGH];
    FILE *fp = popen ("cc -E -dM -x c /usr/include/stdio.h", "r");
    while (fgets(buffer, sizeof buffer, fp) != NULL) {
       char identifier[BIG_ENOUGH];
       int value;
       if (sscanf (buffer, "#define %s %d", identifier, &value) == 2) {
           printf ("value %d is from macro %s\n", value, identifier);
       }
    }
    fclose(fp);
    return 0;
}

This prints here for stdio.h

value 1024 is from macro BUFSIZ
value 1024 is from macro FILENAME_MAX
value 20 is from macro FOPEN_MAX
value 1024 is from macro L_ctermid
value 17 is from macro L_cuserid
value 1024 is from macro L_tmpnam
value 1 is from macro SEEK_CUR
value 2 is from macro SEEK_END
value 0 is from macro SEEK_SET
value 308915776 is from macro TMP_MAX
[...]

Instead of stdio.h you simply use your file.c as input.

The classical, if somewhat heavyweight, approach is a table file:

enum.tab:

#ifndef DECL_CONST
#    define DECL_CONST(name, value)
#endif

DECL_CONST(FOO, 2)
DECL_CONST(BAR, 3)
DECL_CONST(BAZ, 500)
#undef DECL_CONST

enum.h:

enum My_Enum
{
#define DECL_CONST(name, value) name = value,
#include "enum.tab"
};

char const* get_name(enum My_Enum value);

enum.c:

#include "enum.h"

char const* get_name(enum My_Enum value)
{
    switch (value)
    {
    #define STR(name) #name
    #define DECL_CONST(name, value) case value: return STR(name);
    #include "enum.tab"
    default:
        return 0;
    }
}

main.c:

#include "enum.h"

void process_value(int v)
{
    char const* s = get_name((enum My_Enum) v);
    if (s)
        printf("The name of value %d is %s\n", v, s);
    else
        printf("There is no name for value %d\n", v);
}

int main()
{
    process_value(1);
    process_value(2);
    process_value(3);
    process_value(500);
    process_value(501);
    return 0;
}

Of course, you can expand this basic scheme to include as many attributes as you like and support any number of dependencies between them. Note that if you have more than one name defined to the same value, the compilation of switch statement will fail.

Not sure it is possible. GCC states in their manual :

There is no way to convert a macro argument into a character constant.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM