Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variation on the type punning theme: in-place trivial construction

I know this is a pretty common subject, but as much as the typical UB is easy to find, I did not find this variant so far.

So, I am trying to formally introduce Pixel objects while avoiding an actual copy of the data.

Is this 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.
}

Expected use pattern, heavily simplified:

std::byte * buffer = getSomeImageData();
auto pixels = promote(buffer, 800*600);
// manipulate pixel data

More specifically:

  • Does this code have well-defined behavior?
  • If yes, does it make it safe to use the returned pointer?
  • If yes, to what other Pixel types can it be extended? (relaxing the is_trivial restriction? pixel with only 3 components?).

Both clang and gcc optimize out the whole loop to nothingness, which is what I want. Now, I'd like to know whether this violates some C++ rules or not.

Godbolt link if you want to play around with it.

(note: I did not tag c++17 despite std::byte, because the question holds using char)

like image 261
spectras Avatar asked Jan 14 '20 16:01

spectras


1 Answers

It is undefined behavior to use the result of promote as an array. If we look at [expr.add]/4.2 we have

Otherwise, if P points to an array element i of an array object x with n elements ([dcl.array]), the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) array element i+j of x if 0≤i+j≤n and the expression P - J points to the (possibly-hypothetical) array element i−j of x if 0≤i−j≤n.

we see that it requires the pointer to actually point to an array object. You don't actually have an array object though. You have a pointer to a single Pixel that just happens to have other Pixels following it in contiguous memory. That means the only element you can actually access is the first element. Trying to access anything else would be undefined behavior because you are past the end of the valid domain for the pointer.

like image 189
NathanOliver Avatar answered Oct 21 '22 05:10

NathanOliver