我正在构建一个动态动画&渲染系统,我想使用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
类型的连续值吗?
我很清楚这可能是未定义的行为,但是现在我必须遵循“实用性节拍纯度”(1)原则。即使这是UB,它是否会做出预期的事情,还是会以不可预见的方式咬人?因为这可能是编译器特定的:我需要这个用于现代MSVC(来自VS 2015)。
如果这不安全,有没有办法真正安全地做到这一点? “this”指的是“使用带有OpenGL的Boost.Units和只有C接口的数字处理程序”,没有不必要地复制数据。
(1)改编自Zen of Python。
答案 0 :(得分:2)
是的,这看起来像你可以做的事情。
有一件事你没有提及,应该添加到要检查的条件列表中:包装金额类型的对齐应该与基础类型的对齐。 (见alignof
)。
所以,在实践中,我只会使用一些static_asserts来编写这样的代码,以保护使重新解释有效的假设。
如果你添加断言T与remove_cv_t<decltype(q.value())>
相同,这应该是可靠的。
有了这些预防措施,由于特定平台上的reinterpret_cast语义,不应该有UB,只有IB(实现定义的行为)。
¹,也许调试断言&q.value() == &q