Bunun oldukça yaygın bir konu olduğunu biliyorum, ancak tipik UB'nin bulunması kolay olduğu kadarıyla, bu varyantı bulamadım.
Yani, verilerin gerçek bir kopyasını kaçınarak resmi olarak Pixel nesnelerini tanıtmaya çalışıyorum.
Bu geçerli mi?
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.
}
Beklenen kullanım şekli, büyük ölçüde basitleştirilmiş:
std::byte * buffer = getSomeImageData();
auto pixels = promote(buffer, 800*600);
// manipulate pixel data
Daha spesifik olarak:
- Bu kod iyi tanımlanmış bir davranışa sahip mi?
- Evetse, döndürülen işaretçiyi kullanmayı güvenli hale getirir mi?
- Evet ise, başka hangi
Pixel
türlere genişletilebilir? (is_trivial sınırlama? pikselini sadece 3 bileşenle gevşetmek?).
Hem clang hem de gcc tüm döngüyü hiçliğe göre optimize eder, bu da istediğim şeydir. Şimdi, bunun bazı C ++ kurallarını ihlal edip etmediğini bilmek istiyorum.
Onunla oynamak istiyorsanız Godbolt bağlantısı .
(Not: std::byte
Soru kullanılmaya devam ettiğinden c ++ 17 etiketlemedim char
)
pixels[some_index]
ya da *(pixels + something)
? Bu UB olurdu.
pixels
(P) dizi nesnesine bir gösterici değil, tek bir göstericidir Pixel
. Bu, yalnızca pixels[0]
yasal olarak erişebileceğiniz anlamına gelir .
Pixel
yeni yerleştirilen bitişik s hala birPixel
s dizisi değildir .