2

This is a follow up to Get pointer to object from pointer to some member with the caveat that my structs aren't standard layout.

Consider the following scenario:

struct Thing;  // Some struct

struct Holder {  // Bigger struct
    Thing thing;

    static void cb(Thing* thing) {
        // How do I get a pointer to Holder here? Say, to access other fields in Holder?
        // Can consider storing a void* inside thing, but that's avoiding the problem and not zero overhead either
    }
};

// C function which takes a Thing and eventually calls the callback with the same Thing
void cfunc(Thing* thing, void(*cb)(Thing*) cb_ptr);

void run() {
    Holder h;
    cfunc(&h.thing, &Holder::cb);
}

Now, how do I get a pointer to Holder inside cb? Of course, I am prepared to do unsafe stuff (like reinterpret casting, etc) to tell the compiler my assumptions and accept undefined behaviour if my assumptions are violated.

The main issue seems to be that the info that the callback would always be called on the thing passed in seems to be missing to the compiler. Notwithstanding that, the fact that cb would only ever be called with the holder's own thing member (and by extension, only things inside holders) also seems to be missing. This is important if I have multiple Things and multiple (unique) callbacks associated with them.

Note that inheritance seems to make this pretty simple:

struct Thing;  // Some struct

struct Holder : public Thing {  // Bigger struct
    static void cb(Thing* thing) {
        Holder* holder = (Holder*)thing;
    }
};

// C function which takes a Thing and eventually calls the callback with the same Thing
void cfunc(Thing* thing, void(*cb)(Thing*) cb_ptr);

void run() {
    Holder h;
    cfunc((Thing*)&h, &Holder::cb);
}

If I want multiple Things, I just inherit multiple times (probably with intermediate types since I don't know how to cast to the base class if I have multiple of the same type) and that's it.

Coming back to the linked answer, offsetof seems to be a decent solution till you run into the requirement of standard layout which is a no-go since I have both public and private data members.

Is there another way to do this without inheritance?

Bonus points if you can tell me why offsetof requires standard layout and why mixing public and private isn't standard layout. At least theoretically, it seems like the compiler should be able to figure this out anyway, especially if structs ALWAYS have a consistent layout (maybe this isn't true?) in the program.

Roshan
  • 1,937
  • 1
  • 13
  • 25
  • 1
    Regarding the `offsetof`, what is wrong with the explanation in [`Why can't you use offsetof on non-POD structures in C++?`](https://stackoverflow.com/questions/1129894/why-cant-you-use-offsetof-on-non-pod-structures-in-c)? Furthermore, don't ask two different questions at once. – t.niese Aug 22 '20 at 13:26
  • And about your actual question. It is IMHO a duplicate to [C/C++ Pointer to a POD struct also points to the 1st struct member](https://stackoverflow.com/questions/19786013/c-c-pointer-to-a-pod-struct-also-points-to-the-1st-struct-member). For a POD the pointer to `h` and the one to its first member `thing` is equivalent. – t.niese Aug 22 '20 at 13:34
  • Does `cfunc` only call `Holder::cb` once, and is `Holder::cb` called immediately so when the `cfunc` returns `Holder::cb` is called? – t.niese Aug 22 '20 at 13:55

1 Answers1

1

What about having the Holder pointer inside Thing:

 struct Holder;
 struct Thing
 {
     Holder * parent; 
 };
    
 struct Holder {  // Bigger struct
     Thing thing;
     Holder() 
     {
        thing.parent = this;
     }
     Holder(const Holder & h)
     {
        thing.parent = this;
     }
     Holder& operator= (const Holder & h)
     {
        //leave thing.parent as is
     }
     static void cb(Thing* thing) {
        Holder* holder = thing->parent;
     }
 };

And here is a good place to learn about standard layout. Short answer is that that the standard does not guarantee the order outside the same access control level.

Nellie Danielyan
  • 1,001
  • 7
  • 19