在不复制数据的情况下,将double数组转换为只有double成员的structs数组

Convert an array of doubles to an array of structs with only double members without copying data

本文关键字:double 数组 转换 structs 成员 复制 数据 情况下      更新时间:2023-10-16

我正在使用第三方C++库在Julia中做一些繁重的工作。在Julia方面,数据存储在类型为Array{Float64, 2}的对象中(这大致类似于替身的2D阵列(。我可以使用指向double的指针将其传递给C++。然而,在C++方面,数据存储在一个名为vector3:的结构中

typedef struct _vector3
{
double x, y, z;
} vector3;

我的快速而肮脏的方法是一个五步过程:

  1. 在C++端动态分配结构数组
  2. 将输入数据从double*复制到vector3*
  3. 进行重型吊装
  4. 将输出数据从vector3*复制到double*
  5. 删除动态分配的阵列

复制大量数据的效率非常低。是否有一些神秘的技巧可以用来避免将数据从double复制到struct再复制回来?我想以某种方式将double的1D数组(大小为3的倍数(解释为具有3个double成员的结构的1D阵列。

很遗憾,你不能。这是因为C++具有混叠规则。简而言之,如果您有一个对象T,您就不能从不兼容类型U的指针合法访问它。从这个意义上说,您不能通过struct _vector3类型的指针访问doubledouble*类型的对象,反之亦然。

如果你深入挖掘,你会发现reinterpret_cast,也许会想"哦,这正是我需要的",但事实并非如此。无论你用什么手段(reinterpret_cast或其他方式(绕过语言限制(也称为,只是让它编译(,事实仍然是,你只能通过double类型的指针合法访问double类型的对象。

键入双关语常用的一个技巧是使用union。在C中是合法的,但在C++中是非法的,但一些编译器允许这样做。然而,在你的情况下,我认为没有办法使用并集。

理想的情况是直接对double*数据进行重载。如果这在您的工作流程中可行。

严格来说,你不能。我以前问过一个类似的问题(用C++的方式别名结构和数组(,答案解释了为什么直接别名会调用Undefined Behavior,并给出了一些可能的解决方案。

话虽如此,你已经陷入了困境,因为原始数据来自不同的语言。这意味着该数据的处理不在C++标准的范围内,仅由您正在使用的实现(gcc/version或clang/version或…(定义

对于您的实现,将外部数组别名为C++结构或将外部结构别名为C++数组可能是合法的。您仔细阅读了混合语言编程的文档,以便进行精确的实现。

其他答案提到了真正的问题(转换可能无法正常工作(。我将添加一个小的运行时检查,以验证别名是否有效,并提供一个占位符,用于调用/使用大量复制的代码。

int aliasing_supported_internal() {
double testvec[6];
_vector3* testptr = (_vector3*)(void*)testvec;
// check that the pointer wasn't changed
if (testvec != (void*)testptr) return 0;
// check for structure padding
if (testvec+3 != (void*)(testptr+1)) return 0;
// TODO other checks?
return 1;
}
int aliasing_supported() {
static int cached_result = aliasing_supported_internal();
return cached_result;
}

此代码将一个小的doubles数组转换为一个结构别名数组(不是复制(,然后检查它是否有效。如果转换有效(函数返回1(,您可能可以自己使用相同类型的别名(通过空指针进行转换(。

请注意,代码仍可能以意外方式被破坏。严格的混叠规则规定,即使是上述检查也是未定义的行为。这可能奏效,也可能失败。只有允许正常工作的转换才能作废*并返回到原始指针类型。此外,在多个继承层次结构或虚拟基类中,上述检查可能是完全错误的(从某种意义上说,两者都不安全地转换为void*,因为实际指针值可能会发生偏移,也就是说,可能会由于对齐以外的约束而发生变化,并且偏移了相当多的字节(