C++中的另一个静态数组初始化

Yet another static array initialization in C++

本文关键字:数组 初始化 静态 另一个 C++      更新时间:2023-10-16

我在C++中有以下代码(C++11可用(

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");

我希望能够在不更改LOOKUP_TABLE初始化列表的情况下更改枚举中的顺序。

我已经阅读了这个 初始化一个具有一个默认值的普通数组,模板几乎是我需要的,但不完全是。我在元编程上挣扎了半天,仍然不知道该怎么做。

代码中的实际枚举从零开始,包含 20-30 个条目。我考虑过哈希表 - 哈希函数是微不足道的,但初始化问题仍然存在。我在代码中有很多 - 5+ - 这样的查找表。值得奋斗吗?

附言在围绕开关构造的几个答案之后,让我展示一大块真正的代码。这只是一个例子。

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

我在之前的 P.S. 中想说的是,"switch"给了编译器太多的自由。我想确保查找是一个简单的表,而不是一些双索引数组或 if/else 分支

P.S.3 这是初始化数组的另一个示例。数组包含钩子,调用正确的函数非常重要。

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
              // ..... 
        }
}

我认为处理这种情况的最有效方法是使用将枚举值映射到其相应 C 字符串的函数:

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;
}

现场演示

此时,如果你忘记在show中定义fruits::last的字符串,大多数编译器会用类似的话警告你:

main.cpp: 在函数 'const char* show(fruits(' 中:

主.cpp:7:12: 警告:未在开关 [-Wswitch] 中处理枚举值"last">

 switch (x) {

现场演示

如果使用-Werror进行编译,则会阻止编译。

鉴于您问题的症结是:

我希望能够在没有 更改LOOKUP_TABLE中的初始化列表。

这是一个非常简单的问题:

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
}

是的,你不能对它做static_assert()。 但你不需要。 许多现代编译器甚至在 switch(( 中未处理enum fruits_t之一时会警告您。

对于 30 个左右的值,函数会变得有点冗长,但列表必须很长,哈希表才会开始超过 switch((。

这比涉及宏的模板化函数更具可读性。 如果你需要它来进行大学作业或其他什么,那就好了。 但是上述方法在 C 和 C++ 中都有效,不需要 C++11。

只是试图保持简单。