将双精度数组转换为仅具有双精度成员的结构体数组,而无需复制数据

时间:2018-07-16 07:25:11

标签: c++ struct casting julia

我正在使用第三方C ++库在Julia中进行一些繁重的工作。在朱莉娅方面,数据存储在类型为Array{Float64, 2}的对象中(这与二维双精度数组相似)。我可以使用指向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数组。

3 个答案:

答案 0 :(得分:3)

严格来说,您不能。不久前,我曾问过类似的问题(Aliasing struct and array the C++ way),答案解释了为什么直接别名会调用未定义的行为,并给出了一些可能的解决方法的提示。

话虽这么说,您已经陷入困境,因为原始数据来自另一种语言。这意味着该数据的处理未包含在C ++标准中,仅由您使用的实现(gcc / version或clang / version或...)定义。

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

答案 1 :(得分:2)

不幸的是,您不能。这是因为C ++具有aliasing rule。简而言之,如果您有对象T,则不能从不兼容类型U的指针合法访问它。从这种意义上说,您无法通过类型为double的指针访问类型为double*struct _vector3的对象,反之亦然。

如果深入研究,您会发现reinterpret_cast并可能认为“哦,这正是我需要的”,但不是。无论您采取什么欺骗手段(reinterpret_cast或其他方式)来绕过语言限制(也称为只需使其编译),事实仍然是您可以合法访问类型{{ 1}}仅通过类型double的指针。

一种经常用于类型调整的技巧是使用double。在union中合法,但是在C ++中是非法的,但是某些编译器允许这样做。但就您而言,我认为没有办法使用联合。

理想的情况是直接对C数据进行繁重。如果在您的工作流程中可行的话。

答案 2 :(得分:0)

其他答案提到了实际问题(转换可能无法正常进行)。我将添加一个小的运行时检查,以验证别名是否有效,并提供一个占位符,您可以在其中调用/使用繁重的代码。

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;
}

此代码将一小部分的double转换为别名为结构的数组(不复制),然后检查其是否有效。如果转换有效(该函数返回1),则您自己可能会使用相同的别名(通过void指针转换)。

请注意,该代码仍会被意外破坏。严格的别名规则规定,即使上述检查也是未定义的行为。这可能会起作用,或者可能会严重失败。只有允许正常工作的转换才是void *并返回到原始指针类型。同样,上述检查在多个继承层次结构或虚拟基类中可能是完全错误的(从某种意义上来说,两者都不安全地转换为void *,因为实际的指针值可能会发生偏移,也就是说,由于对齐以外的约束可能会发生更改)并且有很多字节)