Je sais que c'est un sujet assez courant, mais autant que l'UB typique est facile à trouver, je n'ai pas trouvé cette variante jusqu'à présent.
Donc, j'essaie d'introduire formellement des objets Pixel tout en évitant une copie réelle des données.
Est-ce valable?
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.
}
Modèle d'utilisation prévu, fortement simplifié:
std::byte * buffer = getSomeImageData();
auto pixels = promote(buffer, 800*600);
// manipulate pixel data
Plus précisement:
- Ce code a-t-il un comportement bien défini?
- Si oui, est-il sûr d'utiliser le pointeur renvoyé?
- Si oui, à quels autres
Pixel
types peut-il être étendu? (assouplissant la restriction is_trivial? pixel avec seulement 3 composants?).
Clang et gcc optimisent toute la boucle vers le néant, ce que je veux. Maintenant, je voudrais savoir si cela viole certaines règles C ++ ou non.
Lien Godbolt si vous voulez jouer avec.
(note: je n'ai pas tagué c ++ 17 malgré std::byte
, parce que la question tient en utilisant char
)
pixels[some_index]
ou *(pixels + something)
? Ce serait UB.
pixels
(P) n'est pas un pointeur vers un objet tableau mais un pointeur vers un seul Pixel
. Cela signifie que vous ne pouvez y accéder que pixels[0]
légalement.
Pixel
s contigus placés nouveaux ne sont toujours pas un tableau dePixel
s.