cecil
cecil

Reputation: 463

How to cast one struct to another type with identical members?

If I'm given a struct variable like:

struct Quaternion {
    float    x;
    float    y;
    float    z;
    float    w;
}

But I need to call a function expecting a

struct Vector {
    float    x;
    float    y;
    float    z;
    float    w;
}

Is there a way in C++ to cast a variable of type Quaternion to type Vector?

Upvotes: 6

Views: 4093

Answers (3)

cigien
cigien

Reputation: 60228

This is just an addition to largest_prime_is_463035818's answer to point out that the members don't have to have the same name in order to write a generic function to use them. So long as there are an equal number of members, all are publicly accessible, and they have the same types, you can decompose objects with a structured-binding and use that.

template <typename T>
void foo(const T& t) {
    auto const & [x, y, z, w] = t;  
    std::cout << x << y << z << w;
}

Upvotes: 3

Jim Oldfield
Jim Oldfield

Reputation: 674

If, as you said in a comment, you can't change to the two types, you could possibly "solve" the problem by using yet another type:

struct MyVector {
    float    x;
    float    y;
    float    z;
    float    w;
    operator Quaternion() { return Quaternion{x, y, z, w}; }
    operator Vector() { return Vector{x, y, z, w}; }
};

My advice: It's probably better to pick one or the other as the main type in your code and use a separate conversion function.

What I think you're hoping for, given that you specifically asked about "cast", is to use a cast to go from one to the other.

Vector v = {1, 2, 3, 4};
Quaternion* q = reinterpret_cast<Quaternion*>(&v);
do_something_with_quart(*q);  // Could modify v if pass by reference

Strictly speaking, this is undefined behaviour, although it is overwhelmingly likely to work in practice, but I still wouldn't recommend it. If you're really desparate to do something cast like, it's legal to use memcpy so long as the member variables are the same type in the same order (and there are no virtual methods):

Vector v = {1, 2, 3, 4};
Quaternion q;
memcpy(&q, &v, sizeof(v));
do_something_with_quart(q);
memcpy(&v, &q, sizeof(v));  // Copy back any change

Although the language and documentation describes memcpy as a copying function, it has certain magical properties that often in practice make it identical to the previous block of code. But I would also strongly advise against this. I mean, yuck! Just look at it!!

Upvotes: 3

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122516

You can write a converting constructor:

struct Quaternion {
    float    x;
    float    y;
    float    z;
    float    w;
    explicit Quaternion(const Vector& vec) : x(vec.x),y(vec.y),z(vec.z),w(vec.w) {}    
}

And similar for the other way.

In case you are looking for a way that does not require to copy the members, then I am not aware of a portable way to do that, and members having the same name does not help for that.

On the other hand, having same named members helps to write generic code like this:

 template <typename T>
 void foo(const T& t) {
     std::cout << t.x << t.y << t.z << t.w;
 }

You can call this with either a Quaternion or a Vector without needing to convert between them.

In case you cannot modify any existing code (not the structs nor the function you want to call), you can write a simple function to do the conversion (as suggested by ShadowRanger in a comment):

Vector Quat2Vect(const Quaternion& q) {
    return {q.x,q.y,q.z,q.w};
}

Upvotes: 4

Related Questions