So che questo è un argomento piuttosto comune, ma per quanto il tipico UB sia facile da trovare, finora non ho trovato questa variante.
Quindi, sto cercando di introdurre formalmente oggetti Pixel evitando una copia effettiva dei dati.
È valido?
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.
}
Modello di utilizzo previsto, fortemente semplificato:
std::byte * buffer = getSomeImageData();
auto pixels = promote(buffer, 800*600);
// manipulate pixel data
Più specificamente:
- Questo codice ha un comportamento ben definito?
- In caso affermativo, è sicuro utilizzare il puntatore restituito?
- Se sì, a quali altri
Pixel
tipi può essere esteso? (rilassando la restrizione is_trivial? pixel con solo 3 componenti?).
Sia clang che gcc ottimizzano l'intero ciclo fino al nulla, che è quello che voglio. Ora, vorrei sapere se questo viola alcune regole C ++ o no.
Godbolt link se vuoi giocare con esso.
(nota: nonostante non abbia taggato c ++ 17 std::byte
, perché la domanda è ancora valida char
)
pixels[some_index]
o *(pixels + something)
? Sarebbe UB.
pixels
(P) non è un puntatore all'oggetto array ma un puntatore a un singolo Pixel
. Ciò significa che puoi accedere solo pixels[0]
legalmente.
Pixel
i nuovi contigui inseriti non sono ancora un array diPixel
s.