[英]What is the correct way of interop'ing with C flexible array members from C++?
我知道灵活的数组成员不是C ++ 11标准的一部分。
那么,与C ++ 11中使用灵活数组成员返回或接受结构的C代码进行互操作的正确方法是什么?
我应该编写一个垫片,将灵活的数组成员从C结构映射到C ++中的指针吗?
据我所知,标准C ++甚至不接受具有灵活数组成员的结构的声明。 在这种情况下,除了包含FAM的结构类型对C ++代码不透明外,我认为除了编写包装函数(在C中)之外别无选择。 我不确定包装是否是你想到的那种垫片。
然而,在我们进一步讨论之前,我应该指出,如果你的C函数接受并返回指向具有灵活数组成员的结构的指针,而不是它们传递并返回实际结构,那么问题就大不相同了。 我假设他们确实使用指向这些结构的指针,否则似乎没有必要首先使用FAM。
我想这给了C声明如
struct foo {
int count;
my_type fam[];
};
我会用C ++表示相同的数据
struct cpp_foo {
int count;
my_type *fam;
};
,当然也可以由C处理。 请注意,您无法在这些之间成功转换,因为数组不是指针。
给定C函数
struct foo *do_something(struct foo *input);
然后所需的包装器可能如下所示:
struct cpp_foo *do_something_wrap(struct cpp_foo *input) {
struct cpp_foo *cpp_output = NULL;
// Prepare the input structure
size_t fam_size = input->count * sizeof(*input->fam);
struct foo *temp = malloc(sizeof(*temp) + fam_size);
if (!temp) {
// handle allocation error ...
} else {
struct foo *output;
temp->count = input->count;
memcpy(temp->fam, input->fam, fam_size);
// call the function
output = do_something(temp);
if (output) {
// Create a copy of the output in C++ flavor
cpp_output = malloc(sizeof(*cpp_output));
if (!cpp_output) {
// handle allocation error
} else {
fam_size = output->count * sizeof(output->fam[0])
cpp_output->fam = malloc(fam_size);
if (!cpp_output) // handle allocation error
memcpy(cpp_output->fam, output->fam, fam_size);
// Supposing that we are responsible for the output object ...
free(output);
}
} // else cpp_output is already NULL
free(temp);
}
return cpp_output;
}
当然,如果你有几个函数要包装,那么你可能想要编写可重用的转换函数来简化它。
由于灵活的数组成员不能暴露给C ++( my_type fam[];
不是有效的C ++成员),我们必须定义自己的类型。
幸运的是,C链接函数没有依赖于它们的参数的符号。 因此,我们可以在共享头中修改foo
的定义,也可以定义我们自己的定义,也不要包含它们的头。
这是一个可能在布局方面兼容的struct
。 请注意,您不应该在C ++中在堆栈上声明这些:
struct foo {
int count;
#ifndef __cplusplus
my_type fam[];
#else
my_type do_not_use_fam_placeholder;
my_type* fam() {
return &do_not_use_fam_placeholder;
}
my_type const* fam() const {
return &do_not_use_fam_placeholder;
}
#endif
};
这依赖于C中foo
结构的二进制布局作为前缀成员,后面是灵活的数组成员的元素,并且不需要进行额外的打包或对齐。 它还要求灵活的阵列成员永远不会是空的。
我会使用this+1
但如果在count
和fam
之间存在填充,则会遇到对齐问题。
不建议在foo
上使用memcpy
或memmov
等。 通常,在C ++端创建foo
并不是一个好主意。 如果必须,你可以这样做:
struct foo_header {
int count;
};
foo* create_foo_in_cpp(int count) {
std::size_t bytes = sizeof(foo)+sizeof(my_type)*(count-1);
foo* r = (foo*)malloc(bytes);
::new((void*)r) foo_header{count};
for (int i = 0; i < count; ++i)
::new( (void*)(r->fam()+i) my_type();
return r;
};
它构造了C ++中的每个对象。 C ++的对象存在规则比C更严格; 仅仅采用一些POD内存并将其解释为POD并不是C ++中的有效操作,而是在C中。上面的new
将在运行时针对noops进行优化,但是C ++要求声明有问题的内存应该在严格阅读标准的情况下被视为该类型的对象。
现在,有一些标准问题(缺陷),手动每元素构造数组的元素,以及数组和元素之间的布局兼容性,所以你必须要相信C ++编译器的ABI和C代码兼容(或检查)。
通常,C ++标准之间的所有互操作都不是由C ++标准定义的(除了C ++所包含的标准C库的某些部分;即使在这里,也没有要求C ++使用相同的C库)。 您必须了解C和C ++的特定实现如何互操作。
通过将灵活数组成员设置为大小为1,Windows使用了一个技巧(因为Win32 API早在功能进入C99之前就开发了,更不用说C ++了)
struct foo {
int count;
my_type fam[1];
};
如果允许更改C版本,则在C和C ++中使用相同的结构。 如果您无法更改C版本,则需要在C ++中重新定义结构。 在修改C结构时,您仍然需要更改C ++代码,但至少它可以正常编译
也可以看看
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.