samol
samol

Reputation: 20590

How to reinterpret cast an array and is it safe to do so?

// this boxes8a contains a flat array of floats
// There are n boxes and the array is n * 8 long
float* boxes8a = ...;

I want to reinterpret this into an array of Box, where each box contains an array of 4 points. And each point contains 2 floats, (x, y)

struct Box
{
  point points[4]
}

struct point
{
   float x, y
}

Q1: How do I cast the float* boxes8a into an array of Box?

Q2: Is it a good practice to do this?

Upvotes: 0

Views: 2093

Answers (3)

Paul Evans
Paul Evans

Reputation: 27577

Q1: how do I cast the float* boxes8a into an array of Box?

Box* boxes = std::reinterpret_cast<Box*>(boxes8a);

Q2: is it good practice to do this?

No, this is an abomination. struct point can have padding inside or between elements in the array. It's undefined behaviour. Please redesign your solution to the problem,

Upvotes: 0

Xirema
Xirema

Reputation: 20396

How do I cast the float* boxes8a into an array of Box?

Dangerously.

//Undefined Behavior, might not do what you want it to do!
Box* boxes = reinterpret_cast<Box*>(boxes8a); 

Is it good practice to do this?

No.

This is considered Undefined Behavior in C++, and while many compilers will give you results that seem like they work if you attempt this, I cannot advise it. Your best solution is to simply copy the values into Box objects and let the objects own their own data.

std::vector<Box> boxes(n);
for(size_t i = 0; i < n; i++) {
    for(int j = 0; j < 4; j++)
        boxes[i][j] = point{boxes8a[i*8 + j*2 + 0], boxes8a[i*8 + j*2 + 1]};
}

Alternately, and at the risk of sounding like one of the Data-Oriented-Design Cultists Proponents, you might consider having the Box object simply contain a pointer to a location in the array where its data begins:

class point {
    float* data;
public:
    point(float* data) : data(data) {}

    float& x() const {return *data;}
    float& y() const {return *(data+1);}
};

class Box {
    float* data;
public:
    Box(float* data) : data(data) {}

    point operator[](size_t index) const {
        return point(data + index * 2);
    }
};

int main() {
    std::vector<Box> boxes;
    for(size_t i = 0; i < n; i++)
        boxes.emplace_back(boxes8a + i*8);

    boxes[3][3].x() = 34.7; //Will alter the original value in boxes8a!
}

This method will allow the Box objects to alias the original float* array without breaking aliasing rules.

Upvotes: 1

NathanOliver
NathanOliver

Reputation: 180650

Q1: how do I cast the float* boxes8a into an array of Box?

This is not something you should solve by casting. A float* and a Box are different things. They both might be built with floats but they are aren't related so can't be cast to each other, as that would be a strict aliasing violation.

Q2: is it good practice to do this?

No. As stated above you aren't allowed to do so.

What you can do though is use std::memcpy to copy the value of the float array into the Box array. That would look like

float* boxes8a = some_data;
Box array[correct_size];
static_asset(sizeof(Box) == sizeof(float) * 8, "data sizes are incompatible");
std::memcpy(array, boxes8a, correct_size);

Upvotes: 3

Related Questions