Consider following code:
#include <iostream>
struct bar {
double a = 1.0;
int b = 2;
float c = 3.0;
};
void callbackFunction(int* i) {
auto myStruct = reinterpret_cast<bar*>(i) - offsetof(bar, b);
std::cout << myStruct->a << std::endl;
std::cout << myStruct->b << std::endl;
std::cout << myStruct->c << std::endl;
//do stuff
}
int main() {
bar foo;
callbackFunction(&foo.b);
return 0;
}
I have to define a callback function and I want to use some additional information in that function. I defined my own struct and pass the address of a member to the function. In the function I want to "retrieve" the whole struct by casting, but the pointers don't seem to match and I get wrong results. I guess I'm doing something wrong while casting but I'm not sure what?
You're missing a cast to make this work. You need to cast to a byte type before subtracting the offset, and then recast back to bar*. The reason is that the macro offsetof returns the offset as a number of bytes. When you do pointer arithmetic, subtraction and addition work in terms of the size of the pointed type. Let's make an example:
Let's assume that you have a bar instance, named b that is at address 0x100h. Assuming that sizeof(double) == 8, sizeof(int) == 4 and sizeof(float) == 4, then sizeof(bar) == 16 and your struct and its members would look like that in memory:
b @ 0x100h
b.a @ 0x100h
b.b @ 0x108h
b.c @ 0x10Ch
offsetof(bar,b) would be equal to 8. Your original code says 'treat 0x108h as if it's pointing to a struct of type bar. then give me the bar struct at address 0x108h - 8 * sizeof(bar), or specifically: 0x108h - 0x80h = 88h.' Hopefully the example demonstrates why the original code was performing the wrong calculation.
This is why you need to tell the compiler that you want to subtract the addresses as bytes instead, to get to the correct address of the first member in your struct.
The solution would look something like this:
bar* owner = reinterpret_cast<bar*>(reinterpret_cast<char *>(i) - offsetof(bar, b));
One thing that you should be very careful about: This is legit only if bar is standard layout. You can use the template std::is_standard_layout<bar>::value to make a static assert to verify you're not accidentally invoking UB.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With