通过宏组合两个指定的初始值设定项

时间:2016-02-20 18:27:16

标签: c c99 designated-initializer

在嵌入式项目中,我使用一个提供宏来初始化结构的库。这提供了合理的默认值,但默认值取决于其他参数。我想覆盖此指定初始值设定项的一个或多个值,因为之后初始化值会产生开销。

理想情况下,我不想复制粘贴所有宏,因为我必须管理第三方代码。如果图书馆更改了默认设置,我也不想这样做。

有没有办法合并或覆盖designated initializers,所以没有开销?代码必须符合C99并且可移植。

用于演示此问题的一些示例代码:

#if SITUATION
#define LIBRARY_DEFAULTS \
{ \
  .field_a = 1, \
  .field_b = 2, \
  .field_c = 3 \
}
#else
#define LIBRARY_DEFAULTS \
{ \
  .field_a = 100, \
  .field_b = 200, \
  .field_c = 300, \
  .field_d = 400, \
  .field_e = 500 \
}
#endif

/* The following is what I want (or similar), but (of course) doesn't 
   work. */
// #define MY_DEFAULTS = LIBRARY_DEFAULTS + { .field_a = 100 }

int main(void) {
    /* The exact definition of something also depends on situation. */
    struct something library_thing = LIBRARY_DEFAULTS;

    /* This generates overhead, and I want to avoid this. It is certain
       that the field exists. */
    library_thing.field_a = 100;
}

2 个答案:

答案 0 :(得分:1)

您可以将library_thing包装在外部结构中,并从外部结构的初始化程序执行覆盖:

#include <stdio.h>

struct foo {
    int a,b,c;
};

#define FOO_DEFAULTS { .a = 1, .b = 2, .c = 3 }

int main() {
    struct {
        struct foo x;
    } baz = {
        .x = FOO_DEFAULTS,
        .x.a = 4,
    };

    printf("%d\n", baz.x.a); // prints 4
}

事实上,你甚至可以做到

.x = FOO_DEFAULTS,
.x = {.a = 4},

如果你需要真正“合并”两个初始化器。

这对Clang(7.0.2)编译很好,但在-Winitializer-overrides下生成警告。检查生成的代码确认结构已使用4, 2, 3初始化,因此此技巧不会产生额外开销。

答案 1 :(得分:1)

这是一个可能的解决方案。首先从宏

中删除大括号
#define LIBRARY_DEFAULTS .a=1, .b=2, .c=3

然后,对于默认值正常的变量,将宏括在大括号中

struct something standard = { LIBRARY_DEFAULTS };

对于需要调整默认值的变量,添加一些额外的初始化程序

struct something tweaked = { LIBRARY_DEFAULTS, .a=100 };

为什么这样做? C规范的第6.7.9节讨论了在初始化列表中使用指示符,并且有关于多次指定同一个指示符的说法:

  

19初始化应在初始化程序列表顺序中进行,每个顺序   初始化程序为特定的子对象提供覆盖任何子对象   先前列出的同一子对象的初始化程序; 151)   所有未明确初始化的子对象应为   隐式初始化与具有静态存储的对象相同   持续时间。

并注释151说明

  

151)子对象的任何初始值设定项被覆盖,因此不被覆盖   用于初始化该子对象可能根本不会被评估。

也就是说编译器需要使用最后指定的初始化程序,而可能实现此解决方案而不会产生任何开销。