c++ - 将多维数组作为 void * 传递给 extern "C"函数

标签 c++ c arrays pointers strict-aliasing

我的库中有一个 C 函数可以很好地处理多维数组:

void    alx_local_maxima_u8 (ptrdiff_t rows, ptrdiff_t cols,
                    const uint8_t arr_in[static restrict rows][static cols],
                    bool arr_out[static restrict rows][static cols])
        __attribute__((nonnull));

我有一个 unsigned char *,它是从 openCV 中定义的 class 接收到的。该指针代表一个二维数据,但它不是,我必须将它与指针算法一起使用 (unsigned char *img_pix = img->data + i*img->step + j;) ,我不是特别喜欢。

我创建了一个与图像大小相同的 bool 数组(这是一个真正的数组,所以我可以使用数组表示法)来存储函数的结果。

我可以编写一个几乎完全相同的 alx_local_maxima_u8() 拷贝,它只使用一个指针和指针算法,但如果可以的话,我希望能够重新使用它。

以这种方式编写使用 void * 的原型(prototype)来愚弄 C++ 是否安全?:

extern "C"
{
[[gnu::nonnull]]
void    alx_local_maxima_u8 (ptrdiff_t rows, ptrdiff_t cols,
                             const void *arr_in,
                             void *arr_out);
}

理论上 void * 可以保存 C 将接收到的任何指针,并且 C 不会访问任何不属于这些指针的数据,所以我看到的唯一问题是别名unsigned char * 作为 uint8_t *[],并传递一个 void *,其中 uint8_t *[] 是预期,这可能会导致各种链接器错误。另外,我不知道 C bool 和 C++ bool 是否会在内存中转换成相同的东西(我希望如此)。

也许我应该用 C 编写一个包装器,它接收 void * 并将它们传递给实际函数,这样我就不需要欺骗 C++。

性能是一个问题,但我使用 -flto,因此任何包装器都可能会在链接器中消失。

我在启用了 POSIX 的 Linux 中使用 GCC (-std=gnu++17)。

最佳答案

保证 T[N][M] 将包含 NxM 个连续的 T 类型对象会阻碍一些其他有用的优化;在 C 的预标准版本中,该保证的主要用途是它允许代码在某些情况下将存储视为一维数组,但在其他情况下将其视为多维数组。不幸的是,标准无法识别由内部数组的衰减形成的指针与通过直接或通过 void* 将外部数组转换为内部元素类型而形成的指针之间的任何区别,即使尽管它们对前者施加了限制,这会阻碍后者的实用性。

在任何典型的平台上,在没有整体程序优化的情况下,ABI 会将指向多维数组元素的指针视为等同于指向具有相同总数的一维数组元素的指针元素的数量,使得将后者视为前者是安全的。然而,我不相信 C 或 C++ 标准中有任何内容会禁止实现“优化”类似的内容:

// In first compilation unit
void inc_element(void*p, int r, int c, int stride)
{
  int *ip = (int*)p;
  ip[r*stride+c]++;
}
// In second compilation unit
int array[5][5];
void inc_element(void*p, int r, int c, int stride);
int test(int i)
{
  if (array[1][0])
    inc_element(array, i, 0, 5);
  return array[1][0];
}

通过将对 inc_element 的调用替换为 array[0][i*5]++,这又可以优化为 array[0] [0]++。我不认为该标准的作者打算邀请编译器进行此类“优化”,但我不认为他们认为激进的优化器会将未能禁止此类事情解释为邀请。

关于c++ - 将多维数组作为 void * 传递给 extern "C"函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56994155/

相关文章:

c++ - 当我尝试创建对象数组时出错

c - 想知道如何添加 vector 数组?

c++ - 如何在 SDL 中获取屏幕尺寸

c++ - boost asio 和 coroutine2 示例

c - 为什么关闭管道需要很长时间才能终止子进程?

c - 如何使用 glob 函数?

c++ - 无法制作包含 ptr_vector<an_abstract_class> 的类的 vector

c++ - 以 n 秒为间隔调用函数 n 次

c - 为什么即使将数组名称与其索引交换也可以使用数组?

php - 在 javascript/jquery 中使用 PHP print_r 数组结果