I have the following code in C++ (C++11 is available)
typedef enum
{
APPLE,
ORANGE,
LAST,
} fruits_t;
template <typename T, size_t size> char (&ArraySizeHelper( T (&)[size]))[size];
#define arraysize(pArray) sizeof(ArraySizeHelper(pArray))
static const char *LOOKUP_TABLE[]
{
"apple", // APPLE
"orange", // ORANGE
};
// Fail the compilation if the lookup table is missing anything
static_assert(arraysize(LOOKUP_TABLE) == LAST, "Update the lookup table");
I want to be able to change the order in the enumeration without changing the initialisation list in the LOOKUP_TABLE.
I have read this Initialization of a normal array with one default value and the template is almost what I need, but not quite. I struggled with metaprogramming for half a day and still can not figure out how to do it.
The actual enumeration in the code starts from zero and contains 20-30 entries. I have considered a hash table - the hash function is trivial, but the problem of initialization remains. I have many - 5+ - lookup tables like this in the code. Is it worth the struggle?
P.S. After a couple of answers around switch construct let me show a chunk of the real code. This is just one example.
typedef enum
{
EVENTS_STATISTICS_COLLECTOR_POOL_TIMEOUT ,
EVENTS_STATISTICS_COLLECTOR_POOL_FAILED ,
EVENTS_STATISTICS_COLLECTOR_SENT_TO_PIPELINE ,
// 50 entries like this
EVENTS_STATISITCS_LAST ,
} events_statistics_t;
uint64_t events_statistics[EVENTS_STATISITCS_LAST];
const char *event_statistics_names[] = {
"collector_pool_timeout ",
"collector_pool_failed ",
"collector_sent_to_pipeline ",
// and so on ...
};
static inline void events_statistics_bump_counter(events_statistics_t counter)
{
events_statistics[counter]++;
}
// The actual function is a generic one which prints arbitrary pairs
// But this one gives an idea
void print_statistics()
{
int col = 0;
static const int COLUMNS = 3;
printf("\n");
for (int i = 0;i < EVENTS_STATISITCS_LAST;i++)
{
printf("%-30s %9lu", event_statistics_names[i], events_statistics[i]);
col++;
if ((col % COLUMNS) == 0)
printf("\n");
else
printf("%4s", "");
}
if ((col % COLUMNS) != 0)
printf("\n");
}
P.S.2 What I wanted to say in the previous P.S. is that "switch" gives too much freedom to the compiler. I want to ensure that the lookup is a simple table and not some double indexed array or if/else branches
P.S.3 Here is another example of an initialised array. The array contains hooks and it is of an extreme importance to call the correct function.
typedef struct
{
const int id;
const events_process_t processor;
uint64_t counter;
} events_process_table_t;
static events_process_table_t events_processors[EVENT_ID_LAST] =
{
{EVENT_ID_OPEN , (events_process_t)events_process_open },
{EVENT_ID_OPENAT , (events_process_t)events_process_open },
// and so on for 30 lines
};
static_assert(arraysize(events_processors) == EVENT_ID_LAST, "Update the table of events processors");
void some_code(int event_id)
{
events_process_table_t *table = &events_processors[event_id];
if (event_id == table->id)
{
// Looks alright and I can use the table->processor
// .....
}
}
答案 0 :(得分:2)
I think that the most efficient way to handle this situation is with a function that maps the enum values to their corresponding C-string:
enum class fruits { apple, orange, last };
char const* show(fruits x) {
switch (x) {
case fruits::apple: return "apple";
case fruits::orange: return "orange";
case fruits::last: return "last";
}
assert(false);
return nullptr;
}
At this point, if you forget to define fruits::last
's string in show
, most compilers will warn you with something like:
main.cpp: In function 'const char* show(fruits)':
main.cpp:7:12: warning: enumeration value 'last' not handled in switch [-Wswitch]
switch (x) {
which if you compile with -Werror
will prevent the compilation.
答案 1 :(得分:0)
Given the crux of your question is:
I want to be able to change the order in the enumeration without changing the initialisation list in the LOOKUP_TABLE.
This is a very simple problem:
const char *fruityLookup(fruits_t f)
{
switch (f)
{
case APPLE: return "apple";
case ORANGE: return "orange";
case DURIAN: return "Woah!";
// do not add a default
}
assert(false); // check definition of fruits_t
}
Yes, you can't do a static_assert()
on it. But you don't need to. A lot of modern compilers will even warn you when one of enum fruits_t
is not handled in the switch().
For 30 or so values the function will get a little long-winded, but the list would have to be quite long before a hash table would start to out-pace a switch().
This is more readable than a templated function involving macros. If you need it for a uni' assignment or whatever, then fine. But the above works in both C and C++, and doesn't need C++11.
Just trying to keep it simple.