Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to type-pun Boost quantity arrays to the underlying type?

I'm building a dynamic animation & rendering system and I would like to use Boost.Units for representing physical quantities to get the nice dimensional safety. However, I will have to pass arrays of quantities around to functions which know nothing about Boost, such as:

  • OpenGL buffer-filling commands. These simply take a const void * and expect to find an array of either float or double values when dereferencing it. They read the data.

  • Linear algebra functions (such as gemm or gesv) from different implementations of BLAS and LAPACK. These generally take either a float * or double * to a given array. They both read and write to the data.

I know that boost::units::quantity<U, T> has a const T& value() member which gives direct reference access to the contained T value. I have also verified that a boost::units::quantity<U, T> is a standard-layout struct with exactly one non-static data member, of type T.

So, let's assume that for a boost::units::quantity<U, T> q, the following holds:

  • static_cast<const void*>(&q) == static_cast<const void*>(&q.value())
  • sizeof(q) == sizeof(T)

My question is: given an array boost::units::quantity<U, T> a[100];, is it safe to:

  1. Pass &a[0].value() to a function which expects to read an array of 100 objects of type T at the address?

  2. Pass reinterpret_cast<T*>(&a[0]) to a function which will write 100 sequential values of type T at the address?

I am well aware this is probably Undefined Behaviour, but right now I have to follow the "Practicality beats purity"(1) principle. Even if this is UB, is it one which will do the expected thing, or will it bite in unforeseen ways? Since this might be compiler-specific: I need this for modern MSVC (from VS 2015).

And if this is not safe, is there a way to actually do this safely? With "this" referring to "using Boost.Units with OpenGL and with number crunchers which only have a C interface," without unnecessarily copying data.


(1) Adapted from the Zen of Python.

like image 311
Angew is no longer proud of SO Avatar asked Sep 25 '15 12:09

Angew is no longer proud of SO


Video Answer


1 Answers

Yes, this looks like something you can do.

There's one thing you didn't mention and should be added to the list of conditions to check, though: the alignment of the wrapped amount type should match that of the underlying type. (see alignof).

So, in practice I'd write code like this only with a number of static_asserts¹ that guard the assumptions that make the re-interpretation valid.

If you add the assertion that T is the same as remove_cv_t<decltype(q.value())> this should be reliable.

With these pre-cautions in place there should not be UB, just IB (implementation defined behaviour) due the semantics of reinterpret_cast on your particular platform.

¹ and perhaps the debug assert that &q.value() == &q

like image 164
sehe Avatar answered Oct 07 '22 02:10

sehe