I have an issue where class GUI_System accesses a member of class ThreadManger through class GUI_Top. What happens is that accessing the member from its original container shows its correct contents, but when accessed from GUI_System it doesn't.
I have found out that in class ThreadManger, prior to the member in question, there are some other objects that if i comment them out, the behavior is changed.
- if i leave everything in, then StructLoadSystem has an address of 0x0
- if i comment out one of the two method pointers then the address of StructLoadSystem is not 0x0 anymore, but is still different from that seen from ThreadManger.
- if i further comment out some more objects, then the address returns to be 0x0.
My issue happens on a big project of mine with a lot of interdependent files so i have stripped to the bare minimum the code in order to create a test-case. Please also note that i have to keep the files separate, because for some reason if i put everything in the same file, the issue disappears so including order might be a thing to look into.
For convenience i have created a Visual Studio solution that can be launched directly. You can find it here.
Can someone give any ideas of what could be happening and/or what i could do to further debug this?
CodePudding user response:
I downloaded your project and had a look at it. The primary issue seems to be that you are storing a pointer to a method on a forward-declared type. The size and/or padding for such values is unknown without the definition of what it points to.
Here is your ThreadManger
[sic] stripped back:
class GUI_System;
class ThreadManger
{
class JobsClass
{
public:
void (GUI_System::* EndEvent)();
};
public:
JobsClass MyJobs; //<-- Unknown size, if GUI_System is not defined
ServiceStruct* StructQuitApplication; //<-- Offset might change depending on above
};
And so, in ThreadManger.cpp
you have this situation:
//<-- At this point, GUI_System is NOT defined
#include "ThreadManger.h"
Whereas in GUI_System.cpp
you have this:
#include "GUI_System.h"
//<-- At this point, GUI_System IS defined
#include "GUI_Top.h" //<-- This includes ThreadManger.h
When I compile the project as 32-bit, the following occurs:
ThreadManger
thinks thatsizeof(ThreadManger::MyJobs)
is 16GUI_System
thinks thatsizeof(ThreadManger::MyJobs)
is 4
When I compile the project as 64-bit, the following occurs:
ThreadManger
thinks thatsizeof(ThreadManger::MyJobs)
is 24GUI_System
thinks thatsizeof(ThreadManger::MyJobs)
is 8
That means the offset of ThreadManger::StructQuitApplication
is different in these two translation units and in fact the issue is caused by your ThreadManger
implementation, not GUI_System
. It's a nasty one for sure, being sensitive to the order of header inclusion.
The quick fix is to #include "GUI_System.h"
in ThreadManger.h
, instead of forward-declaring it.
That's not to say this is a "good" fix, because more fundamentally your design is much too interconnected. The tight coupling of these classes all knowing about each-other's members is something that should be avoided. And nothing quite illustrates that better than the issue you've just encountered. I highly recommend you rethink your design.
At the very least, you ought to be making a lot of this stuff private and expose functionality via methods. As for the JobsClass
stuff, you can use the PIMPL idiom by forward-declaring it and storing privately as a pointer. Then, in the implementation you would ensure you have included GUI_System.h
so that the class can be properly defined.
You may also consider using std::function
and lambdas instead of pointer-to-method for your jobs. Alternatively, define an abstract Job
class with a Execute
method, which is called when dispatching an event. Whichever way you do it, ideally a "thread manager" should be general purpose and not need to know anything about who or what is using it.