Skip to main content
gamedev.net
🔒 Locked

loosing vtable?

Started by RonHiler 2 replies 1.3k views
Original Post
RonHiler
RonHiler
Hey guys,

So I have a base class which defines my plugin interface


class PluginInterface
{
public:
virtual void OnLoad(String *PluginName, bool *AddToEngineMenu, bool *PerFrame) = 0;
virtual void OnExecute() = 0;
virtual void OnFrameDraw() = 0;
virtual void OnDestroy() = 0;
};

This is inherited by all plugins. So, for example, one of my plugins (which are DLLs):


class FontBuilder : public PluginInterface
{
public:
virtual void OnLoad(String *PluginName, bool *AddToEngineMenu, bool *PerFrame);
virtual void OnExecute();
virtual void OnFrameDraw();
virtual void OnDestroy();

private:
void BuildFontBitmap();
void CreateMaterial();

void *FontTexture;
};

extern "C" { DLLEXPORT PluginInterface* CreatePlugin() { return new FontBuilder(); }}

Note that last line, which is an additional requirement for all plugins.

Okay, so back in the executable again, I have a plugin manager class. One routine in that class is to load the plugins. I'm going to abbreviate this somewhat for space

void PluginManager::LoadPlugin(String PluginFile, String *PluginName, bool *AddToEngineMenu, bool *PerFrame)
{
FARPROC Create;
DWORD Error;
LPVOID lpMsgBuf;
HMODULE NewModule;
PluginInterface *Plug;

NewModule = LoadLibraryW(PluginFile.c_strW());
if (NewModule == nullptr)
{
//error stuff
}

Create = GetProcAddress(NewModule, "CreatePlugin");
if (Create == nullptr)
{
//error stuff
}

Plug = reinterpret_cast<PluginInterface*>(Create());
Plugin.Push_Back(Plug);
Plug->OnLoad(PluginName, AddToEngineMenu, PerFrame);
Plugin[Plugin.Size()-1]->OnLoad(PluginName, AddToEngineMenu, PerFrame);
}

Note those last four lines, especially the last two. Obviously this is just test code, I'm not really going to be calling OnLoad() twice. I'm just trying to see what is going on here.

This line:
Plug->OnLoad(PluginName, AddToEngineMenu, PerFrame);
works just fine. The variable Plug has four members in a __vfptr table, corresponding to the four virtual members of that base class, and I can call on any of those members, works great.

However, the plugin manager isn't supporting just one plugin, I need an arbitrary number of plugins (one for each DLL that exists in the plugin folder for the project). So, I pushed plug onto an array (same as a vector for you STL types) which is part of the manager class (again, which I am shortening for space):


class PluginManager
{
private:
Array <PluginInterface*> Plugin;
};


Okay, seems like it ought to work fine. But when I try to use that array to call functions, like I've done in the test code:
Plugin[Plugin.Size()-1]->OnLoad(PluginName, AddToEngineMenu, PerFrame);

I get an access violation error. And it turns out that Plugin[0] (which is the first plugin, obviously) has only one member in it's __vvfptr table, and it is not the same address as any of the vtable members of Plug.

So what arcane concept of C++ am I missing here? I thought if I stored a pointer to the plugin, that should give me access to its vtable as well, but that doesn't seem to be the case. Any advice on how to solve this?
Creation is an act of sheer will
Hodgman
Hodgman
First thing that comes to mind is that FARPROC is a pointer to a function that returns an int. You're getting a pointer to a function that returns a pointer. Casting a pointer to an int and back to a pointer isn't guaranteed to work.
...but that doesn't explain why one of your lines works and the other doesn't.

Not knowing what this Array class is, I'd say it's bugged ;P

[edit]Does this test fail?PluginInterface *Plug;
...
Plug = reinterpret_cast<PluginInterface*>(Create());
Plugin.Push_Back(Plug);
assert(Plug == Plugin[Plugin.Size()-1]);


[edit]<<Insert snarky remark of the standard library being preferable to the reinvented wheel>>
RonHiler
RonHiler
Assertion failed!
Expression: Plug == Plugin[Plugin.Size()-1]

Interesting. You may be right, Array may be bugged, perhaps the Push_Back() routine, or even the [] operator. But that's odd, I've used Array roughly seventy batrillion times in the engine, and it's never failed me before. I'm setting up a minimal test case now with a good ole STL vector to see if that works under the same conditions. If it does, that's pretty conclusive evidence against Array.

I will work on it.

[EDIT: Yeah, in a minimal test case, vector works under this situation fine. So it is a problem in Array somewhere. Still working on it.]
Creation is an act of sheer will
RonHiler
RonHiler
I've traced the problem to the memory manager, and how it interacts with the Array class. I'm quite frankly, absolutely astonished this bug hasn't surfaced before. I kid you not, I've used the Array class hundreds of times, including this particular routine that handles pushing data to the memory blocks. It's really weird it's just coming to light now. I'm going to take a closer look at it to see if I can determine when and why it succeeds versus fails.


[edit]<<Insert snarky remark of the standard library being preferable to the reinvented wheel>>


Interesting discussion on that topic

http://www.codinghorror.com/blog/2009/02/dont-reinvent-the-wheel-unless-you-plan-on-learning-more-about-wheels.html

I personally am in particular agreement with the comment made by J. Stoever on February 8, 2009 4:20 AM a bit down the page.

For the purposes of what I'm doing with my engine, it makes sense to rebuild certain STL containers. I'm not saying I am smarter or a better coder than all those STL programmers, but in the environment I'm in, I can write code that runs faster, in a smaller memory space, and is more capable in terms of functionality because I know the conditions under which that code will run. Ya know? Besides, if no one ever reinvented the wheel, we'd still have stone wheels on our cars

But yeah, that means I have to deal with the occasional hiccup, such as this one, which has cost me most of a day of work. Oh well, it happens, right?
Creation is an act of sheer will

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.