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
?
source to share
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;
source to share
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
source to share
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 usestd::is_standard_layout
insteadstd::is_pod
in test. -
In C ++ 17, you can use
std::is_standard_layout_v<SSVec>
insteadstd::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:
-
Why is the definition of C ++ 11 POD standard layout the way it is? : contains an excellent discussion of the type of standard layout
-
What are aggregates and PODs and how / why are they special? : another great article for more details, scroll down a bit for the POD discussion, scroll up for the pre-c ++ 11 detais.
-
First class member : for other quirky and interesting prizes that also define behavior for the standard layout classes.
source to share