C ++ Returning from an element pointer to a class pointer

I have the following classes:

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

      

I have an object SSVec obj

, I am passing a pointer float*

obj.values

to another function. Elsewhere in the code, I get this pointer float*

and wand to get it back to the pointer SSVec*

.

Is this possible in the standard C ++ behavior mode? Most of the time this will work with a static cast, but I think this is indeed undefined behavior.

The reason for this is a pointer float*

passed to and from the DLL, which knows nothing about SSVec. I have a guarantee that the passed pointer always points to a member of the SSVec :: value [8] object.

The class can be more complex, but it fails, has no virtual function and only contains POD types. values

is the first member

The question can be reformulated: are the class address and the first member address guaranteed to be the same with static_cast

?

+3


source to share


3 answers


The behavior is defined if SSVec

is the POD type. You can statically assert this when implementing a custom throw 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 move these statements to some general function or global scope (preferred).




PS: Take a look std::array

. It does exactly what you want:

typedef std::array<float,8> SSVec;

      

+6


source


You can use offsetof

to get a pointer to a class. Example:

#include <cstddef>
#include <iostream>

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

SSVec* getSSVeciFromValuesPointer(float* floatPointer)
{
    char* rawPointer = reinterpret_cast<char*>(floatPointer);
    char* movedPointer = rawPointer - offsetof(class SSVec, values);

    return reinterpret_cast<SSVec*>(movedPointer);
}

void callBack(float* p)
{
    std::cout << "Callback: float*: " << p << std::endl; 

    SSVec* vec = getSSVeciFromValuesPointer(p);

    std::cout << "Callback: vec*: " << vec << std::endl;
}

typedef void DummyCallback(float*);

void functionThatCanNotBeChanged(float* parameter, DummyCallback callback)
{
    std::cout << "FunctionThatCanNotBeChanged: " << parameter << std::endl;
    callback(parameter);
}

int main()
{
    SSVec vec;

    std::cout << "Vec pointer: " << &vec << std::endl;
    functionThatCanNotBeChanged(vec.values, &callBack);

    return 0;
}

      



As a result, I got:

Vec pointer: 0x7fff85655f90
FunctionThatCanNotBeChanged: 0x7fff85655f94
Callback: float*: 0x7fff85655f94
Callback: vec*: 0x7fff85655f90

      

+2


source


I just came across this question and the two previous answers. While the selected answer is nicely concise, correct and specific to the details of the question (the given class is a POD), I wanted to clarify something for people arriving here looking for information on "C ++ casting pointer from pointer to pointer to class" as the name suggests.

In short, the C ++ 11 standard splits the technical definition of POD into two distinct concepts: the trivial class and the standard layout class. Since C ++ 11 has become the standard, calling a "POD" type (or more formally "POD struct") implies that it is trivial and standard layout.

However, in order to cast a pointer to an object to / from a pointer to the first element, it is important that the type of the object is the standard layout class. In particular, it doesn't matter if the type of the object is also a trivial class.

This means, for example, that the SSVec class in the original question can have custom (non-trivial): default constructors, constructor / assignment copy / move operations, and / or a destructor.

I think it's important to note this as it means that this kind of cast, as well as some other interesting casts, dictate behavior for many other class types than if it were restricted to POD. The C ++ 11 Standard Layout concept was initially sometimes unofficially referred to as a "relaxed" POD for this very reason.

So the first part of the example code from the selected answer could safely be changed to:

SSVec* SSVec_cast(float* ptr) {
    // Break if someone changes SSVec to be a non-standard-layout class:
    static_assert(std::is_standard_layout<SSVec>::value, "SSVec is no longer a standard-layout class!");
    ...

      

Notes:

  • The C ++ 14 standard did not change the definition of a POD structure, standard layout class, or trivial class.

  • I have not checked the C ++ 17 standard, but I have not read anywhere that it substantially changed these definitions.

  • It cannot be stressed that when writing code that depends on the standard layout of a class, static_assert

    always use as the selected answer shows; just use std::is_standard_layout

    instead std::is_pod

    in test.

  • In C ++ 17, you can use std::is_standard_layout_v<SSVec>

    instead std::is_standard_layout<SSVec>::value

    .

see also

C ++ standard types in cppreference:

  • std :: is_pod : both trivial and standard layout.

  • std :: is_standard_layout : scalar type, standard layout class or array of this type / class, possibly qualified

  • std :: is_trivial : a scalar type, a trivially copyable class with a trivial default constructor, or an array of this type / class, possibly cv-qualified.

Pseudo-concepts in cppreference:

For more information, these stackoverflow related articles:

+1


source







All Articles