Passing pointers to arrays of unrelated, but compatible, types without copying?

Viewed 108

(Disclaimer: At this point, this is mostly academic interest.)

Imagine I have such an external interface, that is, I do not control it's code:

// Provided externally: Cannot (easily) change this:

// fill buffer with n floats:
void data_source_external(float* pDataOut, size_t n);

// send n data words from pDataIn:
void data_sink_external(const uint32_t* pDataIn, size_t n);

Is it possible within standard C++ to "move" / "stream" data between these two interfaces without copying?

That is, is there any way to make the following be non-UB, without copying of the data between two correctly typed buffers?

int main()
{
  constexpr size_t n = 64;
  float fbuffer[n];
  data_source_external(fbuffer, n);

  // These hold and can be checked statically:
  static_assert(sizeof(float) == sizeof(uint32_t), "same size");
  static_assert(alignof(float) == alignof(uint32_t), "same alignment");
  static_assert(std::numeric_limits<float>::is_iec559 == true, "IEEE 754");
  
  // This is clearly UB. Any way to make this work without copying the data?
  const uint32_t* buffer_alias = static_cast<uint32_t*>(static_cast<void*>(fbuffer));

  // **Note**: 
  // + reinterpret_cast would also be UB.

  data_sink_external(buffer_alias, n);
  // ...

As far as I can tell the following would be defined behavior, at least with regard to strict aliasing:

...
uint32_t ibuffer[n];
std::memcpy(ibuffer, fbuffer, n * sizeof(uint32_t));
data_sink_external(ibuffer, n);

but given that the ibuffer will have exactly the same bits as the fbuffer this seems quite insane.

Or would we expect optimizing compilers to optimize even this copy away? (In a now deleted comment-like answer a user posted a godbolt link that seems to indicate, at least on first glance, that clang 11 indeed would be able to optimize out the memcpy.)

1 Answers

I didn't test and can't comment yet (cause not enough reputation). But reinterpret_cast may help in this situation.

Documentation

Basically it tells the compiler, hey treat this pointer as if it was the specified type in the cast.

Related