Sign in to follow this  

Class Creation Problem

This topic is 4201 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, I've got a problem that I was hoping someone could help me with. I've done tons of searching and haven't come up with anything satisfactory, just a few dead ends. My issue is with the game engine I'm writing. All my game entity objects for my scene manager are derived from the Entity class. There are a few predefined derived classes from Entity, but I'd like to extend it such that the client application of the engine's DLL can create it's own derived classes. This is all fine and everything, until it meets my memory manager class. To delete these entities in the memory manager part of the engine's DLL, they must have been created (with new) within the DLL also. So herein lies my problem. I need to be able to have a method for the DLL itself (preferably within the scene manager) to instantiate arbitrary classes derived from the base Entity. At first, I thought it would be a simple matter of adding a templated member function to the scene manager such as template< class T > Entity *SceneManager::CreateSpecialEntity( ) { return (Entity *)(new T( )); } This, of course, failed miserably, since the DLL is unable to create a copy of the code for the application's classes. My second attempt was to pass a pointer to a static function within the derived class that would create an instance of itself and return a pointer, but that again failed. So, being out of ideas, I'm asking for help here. Anyone? Please?

Share this post


Link to post
Share on other sites
Quote:
Original post by Ray1234
This is all fine and everything, until it meets my memory manager class. To delete these entities in the memory manager part of the engine's DLL, they must have been created (with new) within the DLL also.


What's so special about your memory management that enforces this condition?
Maybe you just need to link to DLL core-run-time? Or is it somewhat more complicated?

And as for your original problem: try overloading new operator for the classes that need to take memory from "someplace", then, let this overloaded operator kindly ask the DLL for the memory (that would be independent from any template instantiation issues).

Share this post


Link to post
Share on other sites
Have you consider not making subclasses to specialize your objects and using compostion instead ? You could then create Entity specialization at runtime and leave your memory manager instanciate all Entity, which you would specialize afterwards.

There is a nice article about that in GP Gems 6.

Share this post


Link to post
Share on other sites
I did a quick search on Google and found this article and it's even on GameDev:

http://www.gamedev.net/reference/articles/article2097.asp

Hope that gives you what you are looking for.

Share this post


Link to post
Share on other sites
Even simpler.

header:
class Entity
{
//...
void* operator new(size_t size); // do not implement here!!!!11
};


DLL:
void* Entity::operator new(size_t size)
{
return ::operator new(size); // or from some mem-manager.
};


APP:
class MyEntity : public Entity
{
// nothing speceial here
// ...
};


And when new MyEntity() is invoked the code flow will jump to DLL part, since only there will be implementation of the overloaded operator new.

Or am I on the wrong track?
~def

Share this post


Link to post
Share on other sites
Quote:
Original post by deffer
What's so special about your memory management that enforces this condition?
Maybe you just need to link to DLL core-run-time? Or is it somewhat more complicated?

And as for your original problem: try overloading new operator for the classes that need to take memory from "someplace", then, let this overloaded operator kindly ask the DLL for the memory (that would be independent from any template instantiation issues).


Nothing special, really. Objects within the engine are reference counted and the memory manager is responsible for deleting them after all their references are released. You simply have to delete objects within the same scope as they were created (dll-created objects have to be delete within the dll, application-created objects must be deleted within the application.)

I'll look into the new operator to see if it's possible with what I'm attempting to do.

Quote:
Original post by janta
Have you consider not making subclasses to specialize your objects and using compostion instead ? You could then create Entity specialization at runtime and leave your memory manager instanciate all Entity, which you would specialize afterwards.

There is a nice article about that in GP Gems 6.


Thanks, I'll check this out when I have time tonight.

Quote:
Original post by CyberCowboy
http://www.gamedev.net/reference/articles/article2097.asp


Neat article, but doesn't really apply to my situation. That's the gist of what I'm trying to do, but it relies on all the objects being defined within the same DLL/exe as the factory.

Just to give a better example of what I'm trying to do:
I have my Entity class, as mentioned above. As a child of this, defined within the engine DLL, I have a PagedTerrain class which handles dynamically streaming in/out terrain data for the viewer.

What I'd like to do is be able to, in my terrain editor, create a derived class of PagedTerrain called EditablePagedTerrain that would work identically as far as the engine and scene manager are concerned, but would provide the application with functionality to modify the terrain and those results would be apparent in its output to the renderer.

The issue is that since EditablePagedTerrain is derived from PagedTerrain, Entity and finally MMObject (the reference counted object base class), the memory manager will eventually try to delete it. If I just create an instance of it within the application and toss it to the scene manager to work with, once the application is closed, it will cause a nasty error and probably leak any memory created after it, along with any DirectX resources it was using and any that were unable to be released because it had yet to release its own.

Share this post


Link to post
Share on other sites
Quote:
Original post by Ray1234
You simply have to delete objects within the same scope as they were created (dll-created objects have to be delete within the dll, application-created objects must be deleted within the application.)


Sorry, I still can't see the reasons for that. Could you elaborate on this some more?

Quote:
Original post by Ray1234
The issue is that since EditablePagedTerrain is derived from PagedTerrain, Entity and finally MMObject (the reference counted object base class), the memory manager will eventually try to delete it. If I just create an instance of it within the application and toss it to the scene manager to work with, once the application is closed, it will cause a nasty error and probably leak any memory created after it[...]


Hmm...
This doesn't sound like a job for overloaded new.
This sounds like a job for virtual destructor.

Share this post


Link to post
Share on other sites
Quote:
Original post by deffer
Hmm...
This doesn't sound like a job for overloaded new.
This sounds like a job for virtual destructor.


Well, the overloaded operator new didn't work, same issue as without it.
The destructor is virtual already.

Quote:
Original post by deffer
Sorry, I still can't see the reasons for that. Could you elaborate on this some more?


It's an issue of the DLL having a different heap than the application EXE. Things created in one heap cannot be deleted from another. Since the DLL deletes all of these sorts of objects in my engine, they must all, therefore, be created within the DLL also.

Share this post


Link to post
Share on other sites
Quote:
Original post by Ray1234
Well, the overloaded operator new didn't work, same issue as without it.
The destructor is virtual already.


(I'm assuming you overloaded operator delete as well.)
And it didn't work, you're saying...


APP <-> DLL
new MyEntity();
operator new -> implemented here
get memory!!
constructor(s)
[...]
delete p;
virtual destructor
destructors
operator delete -> implemeted here
free memory!!



Is this the scenario? If not, please tell me where I'm wrong.


Quote:
Original post by Ray1234
It's an issue of the DLL having a different heap than the application EXE. Things created in one heap cannot be deleted from another. Since the DLL deletes all of these sorts of objects in my engine, they must all, therefore, be created within the DLL also.


That's understandable. However - you can always switch to single heap, linking to Run-Time Library from MSVCR71.DLL (/MD switch in MSVC). If you have reasons not to, those are the actual reasons I don't see.

Share this post


Link to post
Share on other sites
Quote:
Original post by Ray1234
So herein lies my problem. I need to be able to have a method for the DLL itself (preferably within the scene manager) to instantiate arbitrary classes derived from the base Entity.

I can't see how you would be able to do this. To be able to create an object using operator new, you have to know the class at compile time. If you don't, you won't be able to execute the constructor of the object. In a plugin-driven system, it means that you can't create instances of classes that are only known by the plugin without using some kind of proxy (ie you need to create the instance in the plugin). There is no other way to do it.

Of course, it also means that you have to delete the instance from within the plugin too - this is not what you want, but hey, this is what you have to do [smile].

However, there is a solution to your problem : your plugin can register a factory whose goal will be to call placement new on already allocated memory in order to call the entity class constructor.

class MyEntityFactory : public EntityFactory
{
public:
virtual size_t getEntitySize(int type) const
{
switch (type) {
case MY_ENTITY_1: return sizeof(MyEntity1);
case MY_ENTITY_2: return sizeof(MyEntity2);
case MY_ENTITY_3: return sizeof(MyEntity3);
}
return 0;
}

virtual Entity *create(void *p, int type)
{
switch (type) {
case MY_ENTITY_1: return new(p) MyEntity1;
case MY_ENTITY_2: return new(p) MyEntity2;
case MY_ENTITY_3: return new(p) MyEntity3;
}
return NULL;
}
virtual Entity *destroy(Entity *e, int type)
{
switch (type) {
case MY_ENTITY_1: e->MyEntity1::~MyEntity1; break;
case MY_ENTITY_2: e->MyEntity2::~MyEntity2; break;
case MY_ENTITY_3: e->MyEntity3::~MyEntity3; break;
}
return NULL;
}
};





class MemoryManager
{
// the plugin factory should be some kind of global in the
// plugin; at least, it should exist as long as the plugin
// is loaded, and while it exists it should be registered
// in the main factory (the memory manager, in my case)
std::vector<EntityFactory*> mFactories;

public:
Entity *createEntity(int type)
{
// find the factory that will be used to create the entity
for (size_t i=0; i<mFactories.size(); i++) {
size_t s = mFactories[i]->getEntitySize(type);
if (s) {
// allocate the memory
char *p = new char[s];
// call the constructor
Entity *e = mFactories[i]->create(reinterpret_cast<void*>(p), type);
if (e) {
return e;
}
delete [] p;
}
}
return NULL;
}

void deleteEntity(Entity *e)
{
// find the factory that has been used to allocate e
for (size_t i=0; i<mFactories.size(); i++) {
size_t s = mFactories[i]->getEntitySize(e->type());
if (s) {
// call the destructor
Entity *e = mFactories[i]->destroy(e, e->type());
}
}
// delete the allocated memory
delete [] reinterpret_cast<char *>(e);
}
};




Voilà. HTH,

Share this post


Link to post
Share on other sites

This topic is 4201 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this