Wiem, że jest to dość powszechny temat, ale ponieważ typowy UB jest łatwy do znalezienia, do tej pory nie znalazłem tego wariantu.
Tak więc próbuję formalnie wprowadzić obiekty Pixel, unikając rzeczywistej kopii danych.
Czy to jest ważne?
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.
}
Oczekiwany wzór użycia, znacznie uproszczony:
std::byte * buffer = getSomeImageData();
auto pixels = promote(buffer, 800*600);
// manipulate pixel data
Dokładniej:
- Czy ten kod ma dobrze zdefiniowane zachowanie?
- Jeśli tak, czy bezpiecznie jest używać zwróconego wskaźnika?
- Jeśli tak, na jakie inne
Pixel
typy można go rozszerzyć? (rozluźnianie ograniczenia is_trivial? piksel z tylko 3 składnikami?).
Zarówno clang, jak i gcc optymalizują całą pętlę do nicości, czego chcę. Teraz chciałbym wiedzieć, czy to narusza niektóre reguły C ++, czy nie.
Link Godbolt, jeśli chcesz się nim bawić.
(uwaga: mimo to nie oznaczyłem tagu c ++ 17 std::byte
, ponieważ pytanie to wymaga użycia char
)
pixels[some_index]
albo *(pixels + something)
? To byłby UB.
pixels
(P) nie jest wskaźnikiem do obiektu tablicy, ale wskaźnikiem do pojedynczego Pixel
. Oznacza to, że masz dostęp tylko pixels[0]
legalnie.
Pixel
s umieszczone nowe wciąż nie jest tablicąPixel
s.