나는 이것이 매우 일반적인 주제라는 것을 알고 있지만 일반적인 UB를 쉽게 찾을 수있는 한 지금 까지이 변형을 찾지 못했습니다.
따라서 실제 데이터 사본을 피하면서 Pixel 객체를 공식적으로 소개하려고합니다.
이것이 유효합니까?
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.
}
예상되는 사용 패턴, 크게 단순화 :
std::byte * buffer = getSomeImageData();
auto pixels = promote(buffer, 800*600);
// manipulate pixel data
더 구체적으로:
- 이 코드에는 잘 정의 된 동작이 있습니까?
- 그렇다면 반환 된 포인터를 사용하는 것이 안전합니까?
- 그렇다면 다른
Pixel
유형으로 확장 할 수 있습니까? (3 개의 구성 요소 만있는 is_trivial 제한? 픽셀을 완화합니까?).
clang과 gcc는 전체 루프를 무용지물로 최적화합니다. 이제 이것이 이것이 일부 C ++ 규칙을 위반하는지 여부를 알고 싶습니다.
(참고 : std::byte
질문 에도 불구하고 c ++ 17 태그를 지정하지 않았습니다. char
)
pixels[some_index]
또는 *(pixels + something)
? 그것은 UB입니다.
pixels
(P)는 배열 객체에 대한 포인터가 아니라 단일에 대한 포인터 Pixel
입니다. 즉, pixels[0]
법적으로 만 액세스 할 수 있습니다 .
Pixel
된 새 항목은 여전히의 배열이 아닙니다Pixel
.