tags:

views:

182

answers:

5

I have C++ structure as

struct myStruct {
    int a;
    int b;
    int c;
}; 

myStruct b;
int *ptr = &b.c;

How can I get myStruct object back from ptr?

(I know I can do this using pointer arithmatic like container_Of() in C. Basically something like

reinterpret_cast<myStruct*>(reinterpret_cast<char *>(ptr) - offsetof(myStruct, c));

I am asking if there is any recommended/elegant way?)

+5  A: 

There's certainly no recommended way, as doing this is definitely not recommended at all in C++. This is one of those questions where the correct answer has to be "Don't do that!"

The whole reason for using C++ instead of C is that you want to encapsulate the structure of data inside classes with sensible operations defined on them, instead of allowing the whole program to have knowledge of the internal layout of data structures.

That said, the offsetof technique you describe will work on plain old data objects, because they are no different to C structs.

Daniel Earwicker
Agreed. But its a wierd logic and have to use something in C library and register a callback. If I define a strcture with just a few functions (no derived strcuts etc), would it still count as POD?
Methos
As long as there are no virtual functions, yes but it is not even close to portable.
jmucchiello
Yes. The C++ compiler just generates the member functions as ordinary functions (just with weird names for the linker and some way of passing the 'this' pointer value), and the layout of the struct in memory is just like it would be in C. But in any case, if the C library you're calling allows you to pass an opaque pointer for a callback, you can pass it the address of anything you like. If it wants a function pointer, you can pass it the address of a static member function. But you can certainly pass it a pointer to an entire class object (how would it know?)
Daniel Earwicker
+3  A: 

Because ptr has no knowledge of its overlaying struct, I don't think there's an elegant way of getting back to myStruct. I just recommend to not do this!

nulldevice
+1  A: 

Your reinterpret_cast solution is the standard way to achieve what you want. The reason that it must involve at least one casts is that the operation that you want to perform is inherently unsafe. Obviously, not every pointer to an int is a pointer to the third member of a myStruct so there can't be a simple type-safe way to perform the conversion.

Charles Bailey
+1  A: 

Your text & example use a POD struct; your title talks about classes as well, however. The answer below won't work for non-POD types.

For POD types, check out the "offsetof" macro....

Find the offset, use that & pointer arithmetic to back up say a char * the base.

Dan
Is there a difference between classes and structures in C++ besides the fact that default declaration in one are private and in another are public?
Methos
A: 

You can maybe use the fact, that when you make a cast from a pointer to a base class, that is not the only base class, the compiler will adjust the pointer so that it will point to the beginning of the super class. Therefore, if your "c" member would exist at the beginning of some base class, you could do a trick like:

struct myStructBase1 { int a; };
struct myStructBase2 { int b; };
struct myStructBase3 { int c; };

struct myStruct : public myStructBase1,myStructBase2,myStructBase3 {
    int d;
};

int main() {
    myStruct my;

    int * bptr = &my.b;
    int * cptr = &my.c;
    // The only dirty part in the code...
    myStructBase2 * base2ptr = reinterpret_cast<myStructBase2*> (bptr);
    myStructBase3 * base3ptr = reinterpret_cast<myStructBase3*> (cptr);

    // In each (myStruct*) cast the pointers are adjusted to point to the super class.
    cout << &my << endl <<
            (myStruct*) base2ptr << endl <<
            (myStruct*) base3ptr << endl;
    return 0;
}
// Output:
// 0xbfbc779c
// 0xbfbc779c
// 0xbfbc779c

The requirement for this to work is: if a member is the first member in its class, then its address in a object is equal to address of the object (of that class). I'm not sure if this is true.

EDIT: it should hold when the wrapper-base classes will be PODs. After following modifications:

struct myStructBase1 { int a; virtual void g() {} };
struct myStructBase2 { int b; virtual void f() {} };
struct myStructBase3 { int c;  };
struct myStruct : public myStructBase1,myStructBase2,myStructBase3 {
    int d;
    virtual void h() {}
};

The output is:

0xbfa305f4
0xbfa305f8
0xbfa305f4

For the c member, the constraint still holds. So generally the answer is: yes, there is an alternative way. However, defining a base class for each "reversible"-member is probably not wise way..