Saya tahu ini adalah subjek yang cukup umum, tetapi sebanyak UB tipikal mudah ditemukan, saya tidak menemukan varian ini sejauh ini.
Jadi, saya mencoba memperkenalkan objek Pixel secara resmi sambil menghindari salinan data yang sebenarnya.
Apakah ini valid?
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.
}
Pola penggunaan yang diharapkan, sangat disederhanakan:
std::byte * buffer = getSomeImageData();
auto pixels = promote(buffer, 800*600);
// manipulate pixel data
Lebih spesifik:
- Apakah kode ini memiliki perilaku yang jelas?
- Jika ya, apakah aman menggunakan pointer yang dikembalikan?
- Jika ya, untuk
Pixel
tipe apa lagi dapat diperpanjang? (mengendurkan batasan is_trivial? pixel dengan hanya 3 komponen?).
Baik dentang dan gcc mengoptimalkan seluruh loop ke ketiadaan, itulah yang saya inginkan. Sekarang, saya ingin tahu apakah ini melanggar beberapa aturan C ++ atau tidak.
Link Godbolt jika Anda ingin bermain-main dengannya.
(catatan: Saya tidak memberi tag c ++ 17 std::byte
, karena pertanyaannya tetap menggunakan char
)
pixels[some_index]
atau *(pixels + something)
? Itu akan menjadi UB.
pixels
(P) bukan pointer ke objek array tetapi pointer ke satu Pixel
. Itu berarti Anda hanya dapat mengakses pixels[0]
secara legal.
Pixel
s ditempatkan baru masih bukan arrayPixel
s.