3

While there might be an XY issue that led me here, I am curious about the limits of the new bit_cast in C++20, and this seems a good illustration of it.

Note that there is nothing inherently wrong with std::memcpy and it does not cause undefined behavior. It is also enough of an idiom to generally be optimized out to be the equivalent of a cast. The problem I have is that the intent of its usage is not as clear as it could be and it seems more C than C++.

How could you use bit_cast to do the same thing (without any undefined behavior) as the following:

#include <cstring>

void ptr2array(short (&x)[], int offset, const void * ptr)
{
    std::memcpy(&(x[offset]), ptr, sizeof(void *));
}

void array2ptr(void * & ptr, int offset, short (&x)[])
{
    std::memcpy(ptr, &(x[offset]), sizeof(void *));
}

Note that the following is fundamentally a no-op (tested here: https://godbolt.org/z/ce9Ecx8TG)

void * tst(void * orig)
{
    short x[101];
    int offset = 67;
    void * ret=orig;

    ptr2array(x, offset, ret);
    array2ptr(ret, offset, x);

    return ret;
}

Keep in mind that memcpy works fine with the misalignment (using offset).

Also requiring an array of shorts, as opposed to char, avoids some of the leniencies with char, but keeps the alignment issues.

Glenn Teitelbaum
  • 10,108
  • 3
  • 36
  • 80

1 Answers1

2

bit_cast is for doing conversions between objects. It creates a new object whose binary data comes from some other object. It doesn't work on language arrays because, while they are objects, they don't work like objects. You can't pass them around the same way you can other objects. They decay to pointers, you can't return arrays, etc.

You can bit_cast with std::array and similar types, but even those are potentially a problem. std::array<char, sizeof(void*)> for example is not required to have the size of a void*. And bit_cast only works if the source and destination objects have the same size.

bit_cast also returns the object it creates. Which means you cannot (easily) bit_cast into existing storage. Nor is it easy to do a bit_cast from a range of bytes.

If you're trying to copy bits of data into storage... just do that. bit_cast isn't here to replace every use of memcpy.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 1
    When in doubt there's static_assert(sizeof(std::array) == sizeof(void*)); it ensures that the "potentially a problem" does not need to be paid attention at before it becomes actual problem. – Öö Tiib Dec 17 '21 at 18:01
  • 2
    @ÖöTiib: Well, you don't really need the `static_assert`; `bit_cast` is specifically constrained to require that the types have the same size. So you'll get a compile error either way. – Nicol Bolas Dec 17 '21 at 18:05
  • Perhaps I misunderstood your explanation. With memcpy we theoretically need that static_assert (that I've not heard failing on any implementations). – Öö Tiib Dec 17 '21 at 18:18
  • @ÖöTiib: With `memcpy`, no such assert is needed. You can copy into the `std::array::data()` pointer which is *guaranteed* to be an array as big as you asked for. My point was about any hypothetical padding inserted after the array inside of the `std::array`. – Nicol Bolas Dec 17 '21 at 19:14