Engine: allow modules to access eachother (singleton?)

Started by
24 comments, last by Subotron 16 years, 2 months ago
I'm currently designing what I call an engine, but is basically a framework for my upcoming game project. I'm having some serious issues concerning access of modules by different modules. My engine base class is the EngineApp, which consists of a number of modules, like a texture manager, window manager, renderer, error manager, etc. Now, this EngineApp should, in my opinion, be globally accessible because each module might need to access different modules. For example, if something goes wrong in the texture manager because the renderer tries to access a non-existant texture, the error manager should handle the error, which in turn may mean it needs to tell the window manager to exit. Since these modules are all defined in EngineApp, the simplest thing to do (to me) is to make the engine app a singleton class. However, in a game, it would be useful to derive a class from the EngineApp (say GameApp). However, if I make the EngineApp singleton, the GameApp class will call the functions from EngineApp (so GameApp::GetSingletonPtr()->Run(); would actually execute EngineApp::GetSingletonPtr->Run();). This is not intended obviously. Is there a way, with or without singleton usage, to fix these global access problems? I could obviously pass pointers to all modules that are needed when working in some module, but that doesn't seem like a very good idea to me. Now I know how most people think about singletons, and I try not to use them if I can do without, but for now I don't see another solution that works equally well. Who can help me? Edit: not that I think it matters, but for the record, the engine code should ultimately be put in a lib or dll, while the game code (and thus also the GameApp class) is in another project that uses the library.
Advertisement
Quote:Original post by Subotron
I could obviously pass pointers to all modules that are needed when working in some module, but that doesn't seem like a very good idea to me.

Why doesn't this seem like a good idea?
Quote:For example, if something goes wrong in the texture manager because the renderer tries to access a non-existant texture, the error manager should handle the error, which in turn may mean it needs to tell the window manager to exit.


This way the error manager will have to know about all the modules, and know how they want to handle errors. It would be much better if the error manager would fire an event saying there was an error (with the relevant information), and the other modules would respond accordingly. That is, the error notification should be completely separate from the error handling (in fact, that's exactly what exceptions and error codes are for).
Because for the described scenario, the renderer would need a pointer to the texture manager, error manager, and as the engine expands probably a lot more. That would give messy constructors with 10 parameters that will always have the same value. Engines I used in the past (not my own) don't seem to take this approach either, and I was always very happy about that :p
Quote:Original post by Gage64
Quote:For example, if something goes wrong in the texture manager because the renderer tries to access a non-existant texture, the error manager should handle the error, which in turn may mean it needs to tell the window manager to exit.


This way the error manager will have to know about all the modules, and know how they want to handle errors. It would be much better if the error manager would fire an event saying there was an error (with the relevant information), and the other modules would respond accordingly. That is, the error notification should be completely separate from the error handling (in fact, that's exactly what exceptions and error codes are for).


I guess you are right. You kind of caught me, I don't do error _handling_ yet, mostly just logging of the error and quitting. But either way, the problem still exists that the module where the error occurs should call the error handler. I could just pass a pointer to the error handler for every module, but that really doesn't seem like an elegant solution to me. Is this just me?
Quote:That would give messy constructors with 10 parameters that will always have the same value.


This suggests that there isn't enough abstraction/indirection in your design. You might want to look at the Facade Pattern for a simple example of this. Also, some of the articles in the first link in my sig might also be helpful.
Quote:Original post by Subotron
Because for the described scenario, the renderer would need a pointer to the texture manager, error manager, and as the engine expands probably a lot more. That would give messy constructors with 10 parameters that will always have the same value. Engines I used in the past (not my own) don't seem to take this approach either, and I was always very happy about that :p

And lo, we find the real problem - too many cross-dependancies between systems. Or, more likely, you think theres too many dependancies between systems. Often when you actually start passing systems as parameters then you'll find you have much less cross talk than you initially thought. Give it a go and see. If you still have too many dependancies then take a look and see which ones are reasonable and which ones are inappropriate and see how you can remove or reduce them.
Quote:the problem still exists that the module where the error occurs should call the error handler.


Not really. The module should only report the error.

I don't know the best way to implement this, but for example, your error manager could allow other modules to register handlers for specific events. When a module generates an error that might interest other modules, it will tell the error manager about it, and the error manager will in turn iterate through the list of registered handlers for that error and call each one with the relevant error information. Note that the error manager doesn't know anything about the modules that are registering handlers (the error "listeners"). It simply calls the handlers, not knowing to who they belong.

Again, I don't know if this is a good implementation (in fact, I think it's not...), but it demonstrates the indirection I was talking about earlier.
Quote:Original post by Gage64
Quote:That would give messy constructors with 10 parameters that will always have the same value.


This suggests that there isn't enough abstraction/indirection in your design. You might want to look at the Facade Pattern for a simple example of this. Also, some of the articles in the first link in my sig might also be helpful.

I'm not quite sure if I understood, but would the EngineApp class in this case be the facade? Basically this simply means that this Facade hides the nasty code with pointer passing and such so that the end user doesn't notice it happening?
Quote:I'm not quite sure if I understood, but would the EngineApp class in this case be the facade? Basically this simply means that this Facade hides the nasty code with pointer passing and such so that the end user doesn't notice it happening?


I'm not sure that's a good example. I think the subsystems in the engine are too varied to allow you to give them one interface (at least not one that isn't composed of 300 functions...).

One example that comes to mind is the rendering system. It has many internal components that are conceptually separate (the transformation pipeline, the clipper, the lighting processor, the texture mapper, the rasterizer, ...), but you can have one interface that simplifies the interaction with all these systems and even hides some of them from you (sorry if this is a bad example, maybe someone can give a better one).

This topic is closed to new replies.

Advertisement