Ich weiß, dass dies ein ziemlich häufiges Thema ist, aber so leicht die typische UB zu finden ist, habe ich diese Variante bisher nicht gefunden.
Daher versuche ich, Pixelobjekte formell einzuführen und dabei eine tatsächliche Kopie der Daten zu vermeiden.
Ist das gültig?
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.
}
Erwartetes Verwendungsmuster, stark vereinfacht:
std::byte * buffer = getSomeImageData();
auto pixels = promote(buffer, 800*600);
// manipulate pixel data
Genauer:
- Hat dieser Code ein genau definiertes Verhalten?
- Wenn ja, ist es sicher, den zurückgegebenen Zeiger zu verwenden?
- Wenn ja, auf welche anderen
Pixel
Typen kann es erweitert werden? (Lockerung der is_trivial-Einschränkung? Pixel mit nur 3 Komponenten?).
Sowohl clang als auch gcc optimieren die gesamte Schleife ins Nichts, was ich will. Jetzt möchte ich wissen, ob dies gegen einige C ++ - Regeln verstößt oder nicht.
Godbolt-Link, wenn Sie damit herumspielen möchten.
(Hinweis: Ich habe c ++ 17 trotz nicht markiert std::byte
, da die Frage mit verwendet wird char
)
pixels[some_index]
oder *(pixels + something)
? Das wäre UB.
pixels
(P) kein Zeiger auf ein Array-Objekt, sondern ein Zeiger auf ein einzelnes Pixel
. Das heißt, Sie können nur pixels[0]
legal zugreifen .
Pixel
s, das neu platziert wird, ist immer noch kein Array vonPixel
s.