i ran in quite a serious design problem with my object-oriented 2d game engine. i already know 2 solutions to it, but neither one is perfect (still hoping someone can suggest one that is
), and i can't figure out which one to use.
this is the basic structure for my oo game engine. it's pretty self-explanatory (i hope
), just read the legend in the top right corner.
edit: replaced with a gif.
anyway, i will now try to explain this problem.
as you can see, CRoot is the main class (a singleton), and it has access to everything.
you must delete each singleton class manually if you don't want a memory leak, but CRoot does that for you. it registers a static function (CRoot::DestroySingleton(), which deletes the singleton instance) inside CRoot ctor to be called on exit, using 'atexit(DestroySingleton);'. in the dtor of CRoot i have calls to each of the manager's DestroySingleton() function. of course, each manager's dtor cleans up after itself (e.g. CLogManager closes all opened CLog's). that way all singletons are automatically deleted on exit.
CRoot is constructed when you first call CRoot::GetSingleton() (which is also used to return a pointer to the singleton).
i want the user to be able to control everything, so if he doesn't want a default log (all the major wwEngine events get logged there), then there shouln't be one. if he does want it, he'll call 'CRoot::GetSingleton()->GetLogManager()->CreateDefaultLog(/* ...* /);' note that CRoot::GetLogManager() returns 'CLogManager::GetSingleton()'. the problem, as you might already have noticed, that CRoot will be constructed first, and only then it will create the default log (if one doesn't exist, all attempts to log a message to it will be ignored). thus any attempt to log something in CRoot ctor (which is supposed to initialize some stuff, for example some libraries i'm using) will fail.
now, on to the 2 solutions i came up with.
1. instead of doing all the init in CRoot ctor, move all the init code inside a function (called Init()), which the user should call after he creates (or not) the default log. this is bad because i'd need to somehow check if the user did not call CRoot::Init(), and by 'standards', all the init code should be in a class'es ctor.
2. since my program includes CRoot's header (#include "CRoot.h"), which includes CLogManager's header, my program can do this:
int main(int argc, char **argv)
{
using namespace wwE;
// i use exceptions so some try {} catch () {} stuff is also in here
// i omitted it to make things simpler
CLogManager::GetSingleton()->CreateDefaultLog(/* ... */);
CRoot* pRoot = CRoot::GetSingleton(); // this line creates an instance of CRoot,
// and its ctor will log anything it wants to successfully,
// because the default log is already created
// the rest of the code goes here
return 0;
}
the problem with this is that it makes me wonder the whole purpose of CRoot::GetSingleton()->GetSomeManager(), if you can just call CSomeManager::GetSingleton(). so i don't know what's better. =/
if anyone can suggest another solution, which is better than the two above, or help me decide on what to do, i would really appreciate it! thank you very much.
ps. sorry for a long post.
ps2. singleton is a class that only has one instance of it at all times (no more). to get a pointer to it, you call CClassName::GetSingleton() (well, in my implentation anyway).
ps3. ctor == constructor; dtor == destructor.
---
shurcool
wwdev
[edited by - shurcool on August 13, 2002 6:17:47 PM]