如何将 Boost 数量数组类型化为基础类型?
How to type-pun Boost quantity arrays to the underlying type?
我正在构建一个动态动画和渲染系统,我想使用 Boost.Units 来表示物理量以获得良好的尺寸安全性。但是,我将不得不将数量数组传递给对 Boost 一无所知的函数,例如:
OpenGL 缓冲区填充命令。这些仅采用 const void *
并期望在取消引用时找到 float
或 double
值的数组。他们读取了数据。
来自 BLAS 和 LAPACK 不同实现的线性代数函数(例如 gemm
或 gesv
)。这些通常对给定数组采用 float *
或 double *
。他们都读取和写入数据。
我知道 boost::units::quantity<U, T>
有一个 const T& value()
成员,它提供对包含的 T
值的直接引用访问。我还验证了 boost::units::quantity<U, T>
是一个标准布局结构,只有一个非静态数据成员,类型为 T
.
因此,我们假设对于 boost::units::quantity<U, T> q
,以下成立:
static_cast<const void*>(&q) == static_cast<const void*>(&q.value())
sizeof(q) == sizeof(T)
我的问题是:给定一个数组boost::units::quantity<U, T> a[100];
,是否安全:
将 &a[0].value()
传递给一个函数,该函数期望在地址处读取 100 个类型为 T
的对象的数组?
将 reinterpret_cast<T*>(&a[0])
传递给函数,该函数将在地址处写入 100 个 T
类型的连续值?
我很清楚这可能是未定义的行为,但现在我必须遵循 "Practicality beats purity"(1) 原则。即使这是 UB,它会做预期的事情,还是会以无法预料的方式咬人?因为这可能是特定于编译器的:我需要这个用于现代 MSVC(来自 VS 2015)。
如果这不安全,有没有办法真正安全地做到这一点?使用 "this" 引用 "using Boost.Units with OpenGL and with number crunchers which only have a C interface," 而不会 不必要地复制数据。
(1) 改编自 Zen of Python.
是的,这看起来像你可以做的事情。
有一件事你没有提到,但是应该添加到要检查的条件列表中:包装金额类型的对齐方式应该与基础类型的对齐方式相匹配。 (参见 alignof
)。
因此,在实践中,我只会使用一些 static_asserts¹ 来编写这样的代码,以保护使重新解释有效的假设。
如果加上 T 与 remove_cv_t<decltype(q.value())>
相同的断言,这应该是可靠的。
有了这些预防措施,由于您特定平台上 reinterpret_cast 的语义,不应该有 UB,只有 IB(实现定义的行为)。
¹ 并且也许调试断言 &q.value() == &q
我正在构建一个动态动画和渲染系统,我想使用 Boost.Units 来表示物理量以获得良好的尺寸安全性。但是,我将不得不将数量数组传递给对 Boost 一无所知的函数,例如:
OpenGL 缓冲区填充命令。这些仅采用
const void *
并期望在取消引用时找到float
或double
值的数组。他们读取了数据。来自 BLAS 和 LAPACK 不同实现的线性代数函数(例如
gemm
或gesv
)。这些通常对给定数组采用float *
或double *
。他们都读取和写入数据。
我知道 boost::units::quantity<U, T>
有一个 const T& value()
成员,它提供对包含的 T
值的直接引用访问。我还验证了 boost::units::quantity<U, T>
是一个标准布局结构,只有一个非静态数据成员,类型为 T
.
因此,我们假设对于 boost::units::quantity<U, T> q
,以下成立:
static_cast<const void*>(&q) == static_cast<const void*>(&q.value())
sizeof(q) == sizeof(T)
我的问题是:给定一个数组boost::units::quantity<U, T> a[100];
,是否安全:
将
&a[0].value()
传递给一个函数,该函数期望在地址处读取 100 个类型为T
的对象的数组?将
reinterpret_cast<T*>(&a[0])
传递给函数,该函数将在地址处写入 100 个T
类型的连续值?
我很清楚这可能是未定义的行为,但现在我必须遵循 "Practicality beats purity"(1) 原则。即使这是 UB,它会做预期的事情,还是会以无法预料的方式咬人?因为这可能是特定于编译器的:我需要这个用于现代 MSVC(来自 VS 2015)。
如果这不安全,有没有办法真正安全地做到这一点?使用 "this" 引用 "using Boost.Units with OpenGL and with number crunchers which only have a C interface," 而不会 不必要地复制数据。
(1) 改编自 Zen of Python.
是的,这看起来像你可以做的事情。
有一件事你没有提到,但是应该添加到要检查的条件列表中:包装金额类型的对齐方式应该与基础类型的对齐方式相匹配。 (参见 alignof
)。
因此,在实践中,我只会使用一些 static_asserts¹ 来编写这样的代码,以保护使重新解释有效的假设。
如果加上 T 与 remove_cv_t<decltype(q.value())>
相同的断言,这应该是可靠的。
有了这些预防措施,由于您特定平台上 reinterpret_cast 的语义,不应该有 UB,只有 IB(实现定义的行为)。
¹ 并且也许调试断言 &q.value() == &q