Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Casting back from member pointer to holding class pointer

I have the following classs:

class SSVec
{
public:
    float values[8];
}

I have an object SSVec obj, I pass the float* pointer obj.values to another function. Elsewhere in the code, I get this float* pointer, and wand to cast it back to a SSVec* pointer.

Is this possible in a C++ standard defined behavior way? Most of the time this will work with a static cast, but my guess is it's in fact undefined behavior.

The reason for this, is the float* pointer is passed to and from a DLL, which knows nothing about SSVec. I have the guarantee that the passed pointer always point to a SSVec::value[8] object member.

The class could be more complex, but it does not derive from anything, has no virtual function, and contains only POD types. values is the first member

The question could be reformulated : are the class address and the first member address guaranteed to be the same through static_cast?

like image 206
galinette Avatar asked Dec 25 '22 01:12

galinette


1 Answers

It is defined behavior if SSVec is a POD type. You can statically assert this when implementing a special cast function for your type:

SSVec* SSVec_cast(float* ptr) {
    // Break if someone changes SSVec to be no POD anymore:
    static_assert(std::is_pod<SSVec>::value, "SSVec is no longer a POD!");

    // Break if someone changes SSVec to contain more than the array:
    // [ NOTE: This is optional. Behavior is still defined if the structure
    //   changes(*), but then only if the pointer really points into an SSVec.
    //   With these assertions included, you can even cast from a different
    //   float array of size 8, even if it hasn't been declared as a SSVec. ]
    static_assert(sizeof(SSVec) == 8 * sizeof(float), "SSVec has wrong size!");
    static_assert(sizeof(SSVec::values) == sizeof(SSVec), "SSVec has wrong structure!");
    static_assert(offsetof(SSVec, values) == 0, "SSVec has wrong structure!");

    // Now it is safe to reinterpret cast the pointer:
    // [ (*) NOTE: If above assertions are removed, please change (ptr)
    //   to (reinterpret_cast<char*>(ptr) - offsetof(SSVec, values)). ]
    return reinterpret_cast<SSVec*>(ptr);
}

The same can be done with const pointers by overloading; of course you can then move those assertions to some common function or in global scope (preferably).


PS: Please look into std::array. It does exactly what you want:

typedef std::array<float,8> SSVec;
like image 55
leemes Avatar answered Dec 28 '22 05:12

leemes