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.