Understanding Code example with reinterpret_cast of POD-struct

299 views Asked by At

I found some code and want to make sure that I understand this correctly. The usecase is an packed image that is represented by an array of values. In this example three values represent one pixel.

The code I found goes something like this:

struct Pixel{ 
  int[3] data
  int x(){return data[0];}
  int y(){return data[1];}
  int z(){return data[2];}
};

void main(){

  std::vector<int> img(300);
  Pixel* access = reinterpret_cast<Pixel*>(img.data()+3*5);
  foo(access->x());
}

As I understand from reading POD and standard layout, I think the code example is valid, because we only use the first member of Pixel? Then replacing Pixel with

struct Pixel2{
  int red;
  int green;
  int blue;
};

will result in undefined behaviour?

Edit: I work with cuda and found another example: casting and unsigned char pointer (an array) to an uchar3 pointer. The uchar3 type definition is equal to the second pixel definiton. Does this mean the second is also valid? Or does this only works for code compiled by nvcc? If the second Pixel definition is valid, then why?

Edit: To further emphasis what the code is trying to do I renamed some fields above: I have an array of raw data. In my case that is an packed image. I want to have an nice way to access the pixel and their values. So I can do something like this:

void bar(int* data,size_t size)
{
   Pixel2* img = reinterpret_cast<Pixel*>(data);
   std::cout << "Pixel 13 has blue value: " << img[13].blue;
}

I have seen code using this in cuda and it worked, but I want to know if it is allways okay, since it seems not covered by what I read about POD. Do I just missed something about POD or is this something that can fail?

Edit: is There a difference between:

  foo(access->x());
  foo(access->data[0]);

I thought second should be legal since for POD-types the first member variable has the same address as the object?

Edit: What I take from the answers is: that is UB in all cases I mentioned. The way to go would then be an random-access-iterator that gives me the access I would like.

2

There are 2 answers

2
T.C. On BEST ANSWER

Calling a non-static member function on a nonexistent object and performing a class member access for a non-static data member on a nonexistent object are both undefined behavior.

Nothing in your code creates a Pixel or Pixel2 object.

4
user1810087 On

I could be totally wrong here, but to me it seems both of your struct actually are UB. However, it is unlikely to ever happen.

The (unlikely) use case it could do some harm is when the alignment of your vector (which could be provided from a library for example) and the structs differ. This should not happen if the code is compiled with the same compiler, and the same settings, except you align it yourself. Consider the alignment of the vector is different, both structs will result in UB. Or your struct alignment differs, same here, UB. The undefined behavior, however, does not come from the alignment, but from the fact reinterpret_cast does know nothing about it.

As a quick and dirty example:

struct Pixel2 {
    alignas(8) int red;
    alignas(8) int green;
    alignas(8) int blue;
};

Will give you wrong values for your pixels. Same could be done with the struct where you use an int array.

See this example where you can play around. Here both structs fail to get the correct values. For a variant where the vector is differently aligned, replace comments on line 69/70 (replace std::vector<int> data; with static_vector<int, 128> data;).

Some mentionable SO answers: