Jump to content
  • Advertisement
Sign in to follow this  
3DModelerMan

Registered subsystems

This topic is 2770 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

I came up with an idea the other day that would make subsystems really easy to get access to. If you had a pointer to either a global list of subsystems, or the main application class. You could have a std::map that associates a subsystem name, with a subsystem entry. The entry would contain some information to validate whether the subsystem was created properly, and a void* pointer to the subsystem itself. Subsystems would register here, and could be accessed by name at any place in the code. Does this sound like a bad idea? Or are there any things that I'm not thinking of that would make it not work?

Share this post


Link to post
Share on other sites
Advertisement
This sounds like you're about to re-implement COM.

Why do you want to do this?

Is it because you don't want tons of pointers in the global namespace? If you know all the subsystems, just have a "Game" container which contains a bunch of pointers to the subsystems and you make that global. {It's OK to have SOME global variables.}

Is it because you don't know (at compile time) which subsystems may exist in the system? {Perhaps because you're going to load them from DLLs?} In which case, you probably want to give each DLL an entrypoint to start it up, and that entrypoint could return you a pointer to something describing the subsystem. Try and use an interface class rather than a void* pointer for things like this -- it adds flexibility in the future so you can return extra data if you need to.

How will you access the methods in the subsystems if you don't know their interface at compile time?

Share this post


Link to post
Share on other sites

This sounds like you're about to re-implement COM.

Why do you want to do this?

Is it because you don't want tons of pointers in the global namespace? If you know all the subsystems, just have a "Game" container which contains a bunch of pointers to the subsystems and you make that global. {It's OK to have SOME global variables.}

Is it because you don't know (at compile time) which subsystems may exist in the system? {Perhaps because you're going to load them from DLLs?} In which case, you probably want to give each DLL an entrypoint to start it up, and that entrypoint could return you a pointer to something describing the subsystem. Try and use an interface class rather than a void* pointer for things like this -- it adds flexibility in the future so you can return extra data if you need to.

How will you access the methods in the subsystems if you don't know their interface at compile time?




I'm going to know the interface at compile time. I'm trying to implement the system in a library, I want to be able to create the subsystems and be able to access them from anywhere. The pointer to the "Game" container sounds like it would solve my problem though with an interface. The one problem is that I want the subsystems to be able to access each other without passing data all over in init and constructors. If I left initialization of the GameApp implementation till the main function though, I guess I could have something like this:

//Somewhere in Engine.h
IGameApp* g_gameApp;

//Somewhere in main() function
g_gameApp = new CExampleGameApp();



This way systems included in the library would be able to access the game app implementation through the global g_gameApp class. Is that what you were saying?

Share this post


Link to post
Share on other sites
These two are conceptually the same:class MainApp
{
public:
SystemA* m_aSystem;
SystemB* m_bSystem;
};
class MainApp
{
public:
map<string,System*> m_Systems;
};
Both designs have the flaw that anyone who knows about the MainApp automatically knows about every other system in the app, which hugely violates the Principle of Least Knowledge.

I prefer to work out which systems actually need to know about which other systems, and then pass references into system constructors. e.g.
class MainApp
{
SystemA* m_aSystem;
SystemB* m_bSystem;
public:
MainApp() : m_aSystem(new SystemA()), m_bSystem(new SystemB(*m_aSystem)) {} //B depends on A
};
class MainApp
{
map<string,System*> m_Systems;
public:
MainApp() { m_Systems["A"] = new SystemA(); m_Systems["B"] = new SystemB(*m_Systems["A"]); } //B depends on A
};
I want to be able to access them from anywhere. The one problem is that I want the subsystems to be able to access each other without passing data all over in init and constructors.[/quote]To be blunt, you want a bad design... Why do you want unrestricted global access instead of controlled lines of access?
In the long run, I find that code remains more maintainable if the dependencies between systems are clearly defined.

Share this post


Link to post
Share on other sites
"Is that what you were saying?"

Yes, that's exactly the sort of thing. It means you don't have to build systems which you don't need as well.

{ Remember that in order to cut down your compilation times, you can just forward declare the classes, so your SystemA and SystemB definitions are only visible to the other systems which actually use them. This is a really good reason for using a "service directory" pattern like this. }



"To be blunt, you want a bad design..."

There are no absolute "bad designs".

There are just designs being used in inappropriate places sometimes. Some software works perfectly well using only global variables. Some software needs extravagant information hiding techniques.

The Zen of being a good engineer is being able to tell which goes where after quiet contemplation.

Share this post


Link to post
Share on other sites
Yeah, I guess I'll just pass through constructors. I'm also thinking I could tie it into my event system. If I've got a "physics object moved" event then I could just send a pointer to the physics system along with the data so I could access it from anywhere that registers to receive it.

Share this post


Link to post
Share on other sites
Note: not solving your problem, only an advice.

How about separate the problem to different layers?

Layer 1, the public interface. A subsystem manager object is made to hold all subsystem information (such like name, how to create a subsystem instance, etc).
And it exposes the APIs that needs by the game: give a pointer of sub system A, hide the sub system for now, etc, what ever you want.

Layer 2, inside the subsystem manager. Manage the subsystems in any way you like. std::list or std::map don't matter, as long as the public APIs work correct. If you get any performance issue, change the internal implementation with better algorithm.

Share this post


Link to post
Share on other sites
At the point when you add scripting, you're probably going to want easy global access to subsystems anyway, like graphics.setResolution(), input.isKeyDown(). So I don't see it as bad design if the C++ side has similar easy access. However I'd also recommend to rather use a base or interface class instead of void*. If each subsystem class had a static type identifier, you could use a templatized form of subsystem access, like:

std::map<std::string, Subsystem*> subsystems;

Subsystem* getSubsystem(const std::string& name); // Non-template version. Does the map lookup
template <class T> T* getSubsystem() { return static_cast<T*>(getSubsystem(T::getStaticTypeName())); }

In my design I call the subsystem container "context" and it also manages event passing and object factories. The nice thing about that kind of design is also that writing object factories becomes trivially easy, as most objects will just need a pointer to the context when constructed, instead of n different pointers to different systems.

Of course you'll need discipline so that for example physics engine code doesn't start making direct requests to audio or input etc. I'd like to think that you can grant the programmer some freedom and he won't immediately abuse it.

Share this post


Link to post
Share on other sites

At the point when you add scripting, you're probably going to want easy global access to subsystems anyway, like graphics.setResolution(), input.isKeyDown(). So I don't see it as bad design if the C++ side has similar easy access. However I'd also recommend to rather use a base or interface class instead of void*. If each subsystem class had a static type identifier, you could use a templatized form of subsystem access, like:

std::map<std::string, Subsystem*> subsystems;

Subsystem* getSubsystem(const std::string& name); // Non-template version. Does the map lookup
template <class T> T* getSubsystem() { return static_cast<T*>(getSubsystem(T::getStaticTypeName())); }

In my design I call the subsystem container "context" and it also manages event passing and object factories. The nice thing about that kind of design is also that writing object factories becomes trivially easy, as most objects will just need a pointer to the context when constructed, instead of n different pointers to different systems.

Of course you'll need discipline so that for example physics engine code doesn't start making direct requests to audio or input etc. I'd like to think that you can grant the programmer some freedom and he won't immediately abuse it.





The map of subsystems sounds really flexible. The reason I was originally asking about the subsystems was because I wanted to have an easy way to access them from script.


Share this post


Link to post
Share on other sites

template <class T> T* getSubsystem() { return static_cast<T*>(getSubsystem(T::getStaticTypeName())); }


How does this work? Or, wait, I think I understand now. I was trying to figure out how a static function in the base class could return a derived class's static identifier. But I guess the base class doesn't have getStaticTypeName() in its declaration. You just need to know that each derived class needs one. Correct?

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!