我正在使用一个带有3个官方文档成员的结构库,但它的实现实际上包含4.最后一个是用于填充的字节数组。这是C中保持ABI兼容的常用技术:最后添加一堆字节,如果以后的版本在结构中添加成员,则此填充区域会相应缩小。总的来说,结构的大小保持不变。
但是现在我必须在C ++中使用这个结构,并且我将它用于静态const值,所以我需要使用初始化列表初始化它。所以,像这样:
static const foo_struct foo = { 1, 2, 3 };
由于这不会初始化第四个值,GCC会输出:
warning: missing initializer for member ‘foo_struct::padding’ [-Wmissing-field-initializers]
像foo(1,2,3)这样的构造函数语法不起作用,因为这是一个C结构。并且将其设置为{0}也不是选项,因为我必须初始化前三个成员。
是否有符合C ++ 11 / C ++ 14标准的处理此警告的方式?
编辑:简单地使用{1,2,3,0}可能有效,但是不稳定,因为填充区域没有记录。此外,如果将来的版本添加一个成员,那么它总计最多5个成员,并且警告将返回。
答案 0 :(得分:2)
你可以写一个这样的函数:
template <class ... T>
constexpr foo_struct make_foo_struct(T ... t) {
return foo_struct{t..., 0};
}
static const auto foo = make_foo_struct(1, 2, 3);
您无需禁用任何警告。作为奖励,如果在结构中添加了另一个字段,警告将返回(因为您将有5个成员,而您只是初始化4)。这也很方便,因为如果你创建了很多foos,并且添加了一个你不关心的新字段(比如说它是一个布尔值,你总是想成为true
),你可以改变{{ 1}}以你想要的方式初始化它,而无需修改所有的呼叫站点。
您当然可以在make_foo_struct
中写出类型和参数名称,而不是使用make_foo_struct
;它使事情更加明确,但也需要更多的维护和灵活性。
如果删除了填充,这应该无法编译,并且您只需要修复这一个函数。但是如果你不喜欢这样,另一个选择就是在函数中用编译器编译指示本地静音警告。
T...
对于地狱,我会给出第三种选择。如果您有3个命名成员,并且他们的名称是稳定的,并且您只想初始化它们并将其余部分归零,则可以执行以下操作:
template <class ... T>
constexpr foo_struct make_foo_struct(T ... t) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
return foo_struct{t...};
#pragma GCC diagnostic pop
}
编译器应该很乐意优化它。
答案 1 :(得分:0)
C ++是在兼容C的情况下制作的......
由于库结构是一个C结构,填充字节的方式与在C中完全相同。虽然填充字节未记录,但它们是结构定义的一部分,使用第四个初始化程序(零)将是好的,是必要的。你实际上别无选择,只能将填充字节填充为零。
示例:
// in the library .h
struct foo_struct
{
int first, second, third;
unsigned char padding[256 - 3 * sizeof(int)];
};
// in your cpp
// your could do this:
static const foo_struct foo1 = { 1, ,2, 3, 0 }; // add 0, since it's needed.
// or if you really want to create this boiler plate...
static const foo_struct foo2; // note that it is initialized to zero at startup
// as all static variables are, unless a value is specified
static bool InitFoo2();
static bool fooInitialized = InitFoo2(); // it does waste some data space...
static bool InitFoo2()
{
p = const_cast<foo_struct*>(&foo2);
memset(p, 0, sizeof(foo2)); // not needed in this particular case
// but doesn't hurt to do be explicit.
p->first = 6;
p->second = 7;
p->third = 42;
return true;
}
// Top keep your code compatible with future version, you have no choice,
// but to clear the padding bytes of any foo_struct before using the library.
// Let's look at dynamic allocation, the required data space is the
// exact same size as for static memory allocation.
//
foo_struct* bar()
{
// sizeof(foo_struct) = 256 bytes, padding MUST be reset,
// so your app is compatible with future versions of the library.
//
foo_struct* p = (foo_struct*)malloc(sizeof(foo_struct));
if (p)
{
// By clearing the foo_struct this way, you do not ever need to
// the undocumented members.
memset(p, 0, sizeof(*p));
p->first = 6;
p->second = 7;
p->third = 42;
}
return p;
}
我个人会选择这个解决方案:
static const foo_struct foo = { 6, 7, 42, 0 };