3

我知道灵活的数组成员不是 C++11 标准的一部分。

那么,与来自 C++11 的具有灵活数组成员的结构返回或接受作为参数的 C 代码进行互操作的正确方法是什么?

我应该编写一个垫片,将灵活的数组成员从 C 结构映射到 C++ 中的指针吗?

4

3 回答 3

4

据我所知,标准 C++ 甚至不接受具有灵活数组成员的结构声明。在这种情况下,我认为除了编写包装函数(在 C 中)之外别无选择,除非包含 FAM 的结构类型对您的 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;
}

当然,如果你有几个函数要包装,那么你可能想要编写可重用的转换函数来简化它。

于 2017-05-08T04:10:44.250 回答
2

Windows 使用了一个技巧,将灵活数组成员的大小设置为 1(因为 Win32 API 早在该功能进入 C99 之前就已经开发,更不用说 C++了)

struct foo {
    int count;
    my_type fam[1];
};

如果允许您更改 C 版本,则在 C 和 C++ 中使用相同的结构。如果您无法更改 C 版本,则需要在 C++ 中重新定义结构。当 C 结构被修改时,你仍然需要更改 C++ 代码,但至少它会编译得很好

也可以看看

于 2019-06-02T01:18:11.557 回答
1

由于灵活的数组成员不能暴露给 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
};

这依赖foo于 C 中结构的二进制布局作为前缀成员,然后是灵活数组成员的元素,并且不需要进行额外的打包或对齐。它还要求灵活的数组成员永远不能为空。

我会使用,但如果和this+1之间有填充,则会遇到对齐问题。countfam

不建议使用memcpymemmov类似的。foo一般来说,foo在 C++ 端创建 a 不是一个好主意。如果必须,您可以执行以下操作:

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。上面的 s 将在运行时优化为 noops ,但 C++ 要求声明所讨论的内存应该是在严格阅读标准的情况下被视为该类型的对象。

现在,手动按元素构造数组元素以及数组和元素之间的布局兼容性存在一些标准问题(缺陷),因此您必须稍微相信 C++ 编译器的 ABI 和 C 代码兼容(或检查)。

通常,C 和 C++ 之间的所有互操作都未由 C++ 标准定义(除了 C++ 包含的标准 C 库的某些部分;即使在这里,也没有强制要求 C++ 使用相同的 C 库)。您必须了解 C 和 C++ 的特定实现如何互操作。

于 2017-05-08T10:28:49.067 回答