0

We're working on an engine with a team and a specific feature would require us to access the memory addresses of each member by adding values to the class's address. This is demanded for a component save-load system, where each member of a component would be saved and then loaded back if the user loads the scene. Obviously, since users can also create components, we can't just go ahead and stick to manually setting each member's value -- because the ones created by users aren't known for us. While writing the post, I figured that virtual functions interrupted - originally I didn't have a clue why we couldn't reach the members.
In the following scenario, I want to access the componentID variable of the Component class.

Component.h:

class Component
{
private:
    static int componentID_Count;

protected:
    virtual void Start() {};
    virtual void Tick() {};
    friend class Entity;

public:
    int componentID; // This is originally private, we set it to public in order to test the mentioned system
    Component();
    inline int GetID() { return componentID; }
};

And inside main():

    Component c = Component();
    std::cout << (&c + 1) << std::endl;
    std::cout << "component address: " << &c << "   offset size: " << offsetof(Component, componentID)
        << "    component address + offset: " << (&c + offsetof(Component, componentID)) 
        << "    component id address: " << &c.componentID << std::endl;

I saw offsetof() being recommended on many forums, my experience is the following:

  • (&c + 1) returns an address 2 bytes after &c
  • (&c + offsetof(Component, componentID)) returns an address 10 bytes after &c (and the offset size is said to be 8)
  • &c.componentID is a byte after &c, as expected

Does anyone have a clue how to ignore/jump over virtuals and be able to manipulate the member values based on their addresses?

ErikG
  • 1
  • 1
    Don’t you need to cast &c to char * to get the expected value when adding the result of offset of? Virtual functions shouldn’t take up any space in the structure. – James McLeod May 29 '21 at 08:52
  • @JamesMcLeod if you meant **(((char*)&c) + offsetof(Component, componentID))**, it doesn't work and outputs nothing. – ErikG May 29 '21 at 08:55
  • Also, virtuals do take up space, that's why the offset size is 8 for me. – ErikG May 29 '21 at 09:23
  • 1
    @ErikG That only doesn't work because when you pass a `char *` to std::cout, it thinks it is a string. Cast it again to `(void *)` so `std::cout` will print the value of the pointer. – G. Sliepen May 29 '21 at 09:34
  • @G.Sliepen Yeah I was aware why the sheer char* cast didn't work, but I didn't think casting it to a void* would happen to be a good result. This way I can move forward with this, thanks a lot! – ErikG May 29 '21 at 09:46
  • @G.Sliepen Also, do you mind giving me some explanation on why it works this way, and what void* has to do with casting the char*? – ErikG May 29 '21 at 10:16
  • 1
    @ErikG Again, the problem is that if `std::cout` sees a `char *`, it will think it is a pointer to a string, and tries to print the contents of the string instead of printing the address of what is pointed to. Casting to `void *` is just there to make `std::cout` not print a string but print out the address. It seems to print nothing because most likely, the first byte of `c` is a NUL-byte, so `std::cout` thinks it's an empty string. – G. Sliepen May 29 '21 at 10:22
  • @G.Sliepen Yeah the char* cast part is clear, and the reason why I was confused is because we were already casting `c`'s address, not `c` itself, and I'm aware that void* is used for getting the address of something. So presumably char* cast simply allows void* to work as we're not operating with an address anymore at that point. – ErikG May 29 '21 at 12:19
  • @ErikG No the `char *` cast is necessary for your pointer arithmetic to work correctly. It is not necessary in any way for the cast to `void *` to work. – G. Sliepen May 29 '21 at 12:22
  • @ErikG - Unfortunately, it is all too easy to underestimate the effort required to debug a brand new save/load system; especially one where “users” may provide their own object types. As an alternative, you might want to consider re-using an existing system. More details under another SO question: [SO-q61244928](https://stackoverflow.com/questions/61244928/writing-objects-to-hard-disk-files-in-c) – jpmarinier May 29 '21 at 17:47

0 Answers0