【问题标题】:Enum-based static array: C header基于枚举的静态数组:C 头文件
【发布时间】:2013-09-05 19:21:32
【问题描述】:

我有一个 C 头文件中的值枚举。我想定义一个额外的数组或一组值,其中每个值对应于一个枚举值。本质上,该数组将是与每个枚举值相关的一些信息的查找表。但是,我想在编译时在头文件中执行此操作。

有什么好的方法可以做到这一点?

这是一个如何使用两个单独的枚举或定义结构来完成它的示例。有没有更优雅的方法来仅使用操作枚举来完成此任务?

enum op {
op1,
op2,
op3
};

enum op_information {
op1_info = 0xff,
op2_info = 0xf3,
op3_info = 0xc1
}

更新:如果有任何方法可以使用引用静态定义数组,那么对我来说同样有价值的帮助是回答。也就是说,而不是这样做:

ushort op_information = { 0xff, 0xf3, 0xc1 },

我们能做到吗

ushort op_information[3]
op_information[op.op1] = 0xff;
op_information[op.op2] = 0xf3;
op_information[op.op3] = 0xc1

在编译时?

【问题讨论】:

    标签: c


    【解决方案1】:

    使用 C99 指定初始化器

    ushort op_information[] = {
        [op1] = 0xff,
        [op2] = 0xf3,
        [op3] = 0xc1,
    };
    

    【讨论】:

    • 这正是我想要的。不幸的是,C++ 似乎不再支持此功能?不幸的是,这个头文件将在 C、C++ 和 C# 应用程序中使用。
    • @TSM:您将与 C# 应用程序共享一个 C 头文件
    • @TSM:指定初始化器根本不在 C++ 中(它们只是添加到 C 中,而 C 和 C++ 是具有不同标准的不同语言)。您可以通过在链接到所有项目(例如,作为库)的 C 文件中定义这些常量来解决这个问题。然后你的标题只需要说extern ushort op_information[]
    【解决方案2】:

    您可以使用旧的 GNU cpp 技巧。创建一个完全由宏调用组成的包含文件。

    /* opdefs.h */
    OP(op1, 0xff)
    OP(op2, 0xff)
    

    现在通过重复定义和取消定义 OP 来构建必要的枚举,其中包含:

    #define OP(Id, Val) Id,
    enum op {
    #include "opdefs.h"
    };
    #undef OP
    
    // Handy for debugging, e.g. printf("%s", op_to_string[the_op]);
    #define OP(Id, VaL) #I,
    char op_to_string[] = {
    #include "opdefs.h"
    };
    #undef OP
    
    #define OP(Id, Val) Id ## _info = Val,
    enum op_information {
    #include "opdefs.h"
    };
    #undef OP
    

    我相信您会看到同样的包含也将允许您生成所需的数组。

    加法

    正如@Sebastian 建议的那样,您可以将包含文件替换为另一个宏:

    #define OP_DEFS \
    OP(op1, 0xff) \
    OP(op2, 0xff)
    
    #define OP(Id, Val) Id,
    enum op { OP_DEFS };
    #undef OP
    
    #define OP(Id, VaL) #I,
    char op_to_string[] = { OP_DEFS };
    #undef OP
    
    #define OP(Id, Val) Id ## _info = Val,
    enum op_information { OP_DEFS };
    #undef OP
    

    如果有很多 OP defs 或者如果您需要从多个来源使用它们,则包含文件方法通常会更干净。宏方法更简洁,仅用于一个文件中的短列表。

    【讨论】:

    • 您不需要包含文件。 #define OPDEFS(OP) OP(op1, 0xff) OP(op2, 0xff) 也可以(您可以使用反斜杠将其拆分为单独的行)。
    • @Sebastian 果然。我一直有很长的定义列表,所以发现包含更干净,但你的方法更适合短列表。我会编辑答案。
    【解决方案3】:

    无论您做什么,您都可能想要进行自检。使用断言进行自测,因此您只能在调试版本中进行测试。

    enum op {
        op1,
        op2,
        op3,
    };
    
    enum op_information {
        op1_info = 0xff,
        op2_info = 0xf3,
        op3_info = 0xc1,
    };
    
    ushort op_information = { op1_info, op2_info, op3_info };
    
    // then somewhere, e.g. in a function called _OnBootup():
    assert(op1_info == op_information[op1]);
    assert(op2_info == op_information[op2]);
    assert(op3_info == op_information[op3]);
    

    另外,对于像上面这样非常无聊和真正数据驱动的东西,我通常会编写一个 Python 程序来编写适当的 C 代码。您真的不需要为真正固定的事情这样做,例如当您为冻结的标准编解码器编写解码器时;但是如果枚举和匹配值不断变化(如在研发过程中),并且保持一切都是最新的可能容易出错,只需将操作名称和值放在 Python 字典中并让 Python 代码生成所有样板 C代码。 (很明显,当我使用 Python 时,你应该使用你喜欢的任何东西。)

    【讨论】:

      【解决方案4】:

      如果您需要将结果作为编译时间常数,您可以只做一个嵌套的三元表达式

      #define INFO(X) ( (X) == op1 ? val1 : ((X) == op2 ? val2 : .... ) )
      

      注意这会多次评估X,所以如果X 有副作用,你就完蛋了。

      但是如果X 是一个编译时整数表达式,那么任何 C 编译器必须也评估这个编译时间。

      有了新的 C 标准,C11,_Generic 特性,甚至有办法判断这是否是编译时表达式,如果不是,则用函数调用替换三元表达式,从而避免评估问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-06-01
        • 2021-12-06
        • 2015-04-26
        • 1970-01-01
        • 2013-10-05
        • 1970-01-01
        • 1970-01-01
        • 2011-06-01
        相关资源
        最近更新 更多