I know this is a pretty common subject, but as much as the typical UB is easy to find, I did not find this variant so far.
So, I am trying to formally introduce Pixel objects while avoiding an actual copy of the data.
Is this valid?
struct Pixel {
uint8_t red;
uint8_t green;
uint8_t blue;
uint8_t alpha;
};
static_assert(std::is_trivial_v<Pixel>);
Pixel* promote(std::byte* data, std::size_t count)
{
Pixel * const result = reinterpret_cast<Pixel*>(data);
while (count-- > 0) {
new (data) Pixel{
std::to_integer<uint8_t>(data[0]),
std::to_integer<uint8_t>(data[1]),
std::to_integer<uint8_t>(data[2]),
std::to_integer<uint8_t>(data[3])
};
data += sizeof(Pixel);
}
return result; // throw in a std::launder? I believe it is not mandatory here.
}
Expected use pattern, heavily simplified:
std::byte * buffer = getSomeImageData();
auto pixels = promote(buffer, 800*600);
// manipulate pixel data
More specifically:
- Does this code have well-defined behavior?
- If yes, does it make it safe to use the returned pointer?
- If yes, to what other
Pixel
types can it be extended? (relaxing the is_trivial restriction? pixel with only 3 components?).
Both clang and gcc optimize out the whole loop to nothingness, which is what I want. Now, I'd like to know whether this violates some C++ rules or not.
Godbolt link if you want to play around with it.
(note: I did not tag c++17 despite std::byte
, because the question holds using char
)