键入来自不同第三方库的两种类型,无需联合

Type punning two types from different third party libraries without union

本文关键字:类型 两种 第三方      更新时间:2023-10-16

我读到使用联合进行类型双关语实际上是C++中未定义的行为,我想知道您将如何键入双关语?

例如,我使用联合来键入来自两个具有相同布局的第三方库的双关语两种类型(libAQuaternion 和 libBQuaternion 是我无法更改的第三方库的类型(:

struct libAQuaternion {
double x, y, z, w;
};
void libAFunc(libAQuaternion &p) {
p.x = p.y = p.z = p.w = 1.;
}
struct libBQuaternion {
double x, y, z, w;
};
void libBFunc(libBQuaternion &p) {
p.x = p.y = p.z = p.w = 2.;
}
union myQuat {
libAQuaternion a;
libBQuaternion b;
};
int main() {
myQuat q;
libAFunc(q.a);
libBFunc(q.b);
}

符合最佳解决方案的标准是什么?

符合此标准的最佳解决方案是什么?

编写一个函数以从一个四元数转换为另一个四元数。

libBQuaternion convert(const libAQuaternion &Quat) {
return{Quat.x, Quat.y, Quat.z, Quat.w};
}
libAQuaternion convert(const libBQuaternion &Quat) {
return{Quat.x, Quat.y, Quat.z, Quat.w};
}
// or template if you want to
template<typename T, typename U>
T convertTo(U &&Quat) {
return{Quat.x, Quat.y, Quat.z, Quat.w};
}

任何优化器都应该能够完全优化这一点,因此不应该有性能损失。

但是,如果有一个函数通过左值引用获取一个这样的类,这将是一个问题。您需要创建适当类的新对象,传递该对象,然后将正确的值重新分配给原始结构。我想你可以为此创建一个函数,但 IMO 最干净的方法是更改函数的签名以通过左值 ref 获取单个值,但这并不总是可能的。

如果不调用 UB,就无法在C++中进行类型双关语。

C++不允许类型双关语。大多数时候。

你写的东西是完全合法的,但有一个潜在的危险。

这两个四元数是标准布局类,它们共同的初始序列是它们的整体。 因此,通过工会阅读另一方的成员是合法

myQuat q = libAQuaternion{1, 0, 0, 0};
std::cout << q.b.x;    // legal

然后我们注意到,四元数只能通过内置/琐碎赋值或放置 new 来写入。

对非活动成员
  • (或其成员,递归方式(使用内置/简单赋值会导致非活动成员生存期的隐式开始。

  • 使用新展示位置也将开始成员的生命周期。

因此,在写入四元数时,它的生命周期要么已经开始,要么将开始。

读取和写入一起称为访问,严格别名规则禁止访问具有另一种类型的内容,这是禁止类型双关的原因。但是,由于我们刚刚证明了访问任一四元数是明确定义的,因此这是类型双关语确实合法的一个例外。

一个危险是当库函数部分写入四元数时

void someFunc(libBQuaternion& q)
{
q.x = 1;
}
myQuat q = libAQuaternion{1, 0, 0, 0};
someFunc(q.b);
std::cout << q.a.y;    // UB

不幸的是,q.a.y是未初始化的,因此读取它是未定义的行为。

但是,考虑到所有以前的规则以及未初始化变量背后的原因是效率,编译器不太可能利用 UB 和"错误编译"。

template<class D, class S>
D bitcpy( S const* s ){
static_assert( sizeof(S)>=sizeof(D) );
D r;
memcpy( &r, s, sizeof(D) );
return r;
}
union myQuat {
libAQuaternion a;
libBQuaternion b;
void AsA(){
a = bitcpy<libAQuaternion>(&b);
}
void AsB(){
b = bitcpy<libBQuaternion>(&a);
}
};

您必须跟踪工会中是否有AB。 切换呼叫AsAAsB;这是运行时的Noop。

我相信工会规则允许通过分配来激活成员;如果我误解了标准或情况(这些是豆荚类型吧?(,事情会变得更加棘手。 但我认为这不适用于这里。

键入双关语的最安全方法是使用从类型 A 到 B 类型的memcpy。在许多平台上,memcpy是一个内在的含义,它是由编译器实现的,因此很可能被优化掉。