Jump to content
  • Advertisement
Sign in to follow this  
Zimans

Singletons, Class Factories, and cyclic dependencies

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

Ok, I'm a much better C programmer than C++, but i'm trying. As is everyone else i'm working on my game engine. My quandry is getting pointers to objects that need em the 'right' way. I've started by passing pointers thru functions, but that is sarting to get rediculous. For instance I have a class CConsoleLog (C is for Core class, not class :P ). Just about every class needs to see this as that is my logging mechanism. Not only that it would have to basically be passed into every function. I contemplated letting a class hold on to a pointer to the class, but that seems like a bad idea. Not only do I have to pass Logging, but File System to resource classes, as well as the display for DirectX stuff, etc. An option would be to create a container class that holds on to the pointers for all these core classes, but i still end up passing a class pointer EVERYWHERE. This is just screaming to be a gloabl, but everyone in the C++ world seems to chant "Globals are evil". As I dig around the singleton is another approach to this problem. But even that seems to have two camps for and against. If I was doing this in good'ol C all of my classes would essentially be source files with their associated file local variables and functions. Only the needed functions would be visible outside file scope and anything could use them from anywheres provided the proper header is included. I've also had some cyclic problems. To solve that i've been breaking classes into seperate classes. Again the Logging/Console comes to mind. Just about every object needs to write to the log, but the console also needs to access certain core classes (for input, and rendering). I've split these up, but I still have the problem of getting the pointer. If the log class creates the cmd input class, it still has to get a poointer down to it somehow so it can get input. To help alleviate that I was thinking of using callbacks. But that brings into the mix holding onto a pointer, wich I'm trying to avoid. The answer to it all is probably to do whatever the hell works, but I'm trying to do it proper (And improve my C++). --Zims Tangled in a web of class dependencies

Share this post


Link to post
Share on other sites
Advertisement
Look up singleton and factory patterns!

Good for you for branching out and trying a new language!!

Cheers
Chris

Share this post


Link to post
Share on other sites
Another option I suppose is using reference counting. Whenever a class stores a pointer to another Coreclass it increases the reference count. Then when the main app goes to release the class it can check the reference count. If it's greater than 0 Something is amiss.

However, dunno how this would help as I would have to go tell all classes using said pointer to let go.

I guess holding the pointer isn't a bad thing, Just as long as I make sure my code initializes and releases in proper order. The Core classes should only be substantiated once anyways. Plus I can use protected members and friend class'es to make sure that only the parent class can substantiate the object. All other classes can only reference it.

I would still have to pass in the initial pointer.

Looking into singletons more I'm thinking of using a single singleton called CSys. CSys is just a container holding the pointers to all the core classes. Anyone can substantiate it and get pointers to the core classes.. We'll see how that goes.

I'm still open to suggestions, as most of you are probably much more expierienced than me.

--Zims

Share this post


Link to post
Share on other sites
If you're going to use the Singleton pattern, in many articles I've seen, like the OpenGL GUI one here have written a template Singleton class, which is really easy to use.


class A : public CSingleton<A>
{

};

// and from then on, reference it like so:
A::function();
A::data_member;



It's really easy.

Share this post


Link to post
Share on other sites
Making the jump to c++ is worth the effort once you get your head around it.


Firstly read this to make sure you have your project organised correctly.


Then remember you can always make function static in a class if it needs to be called thought the project. So forinstance with the log:


Class Log
{
public:
..
..
static void WriteLog(……..);

}


then you can just call


Log::WriteLog(“blaaa blaa”);


anywhere in your code , you just have to include the Log header file.

happy coding…

Share this post


Link to post
Share on other sites
Structure:
Hey, thanks for pointing out the class:static member thing. I didn't know about that. That does make some stuff easier (or atleast more elegant). I suppose I really need to find my C++ book.....

Something else that bugs me, and this is probably just me being anal retentive (or a left over of my C coding style) is defining vars in the private section.

Since the private section is declared along with the class, any time I have a member variable that is not a standard type I have to include the header file for it. For instance, to keep an open file handle FILE *fp; I have to include #include <stdio.h> in the header. Now any other class that includes this class has stdio.h included in it, with no need. Any other class is never going to access that file pointer. This creates alot of redundant header includes that the compiler now has to dredge through.

I understand that the class defenition needs to have the private variables present so that the compiler knows how much memory to allocate for the instance. But it still bugs me.

So i guess there is no way around this without some ugly kludge involving inheritance and forward declarations with a member variable instace.

--Zims

Share this post


Link to post
Share on other sites
Provided that you have

Frog * x;

the the header doesnt need to know how big Frog is, because the size of the pointer is known anyway.


class Frog; // predeclaration.

class Pond
{
private:
Frog* mFrog; // This is ok.
Frog mOtherFrog; // This is no ok.
};

Share this post


Link to post
Share on other sites
Quote:
Original post by Zimans
If I was doing this in good'ol C all of my classes would essentially be source files with their associated file local variables and functions. Only the needed functions would be visible outside file scope and anything could use them from anywheres provided the proper header is included.


Good for you; clearly your years of C experience have taught you how to use globals properly and responsibly (i.e. still providing appropriate data hiding and encapsulation).

This technique is still valid in C++, and one that I personally recommend where appropriate (although my view is apparently rather heretical). However, this is only useful where "instantiating an object of the class" is not, and you should be aware that C++ classes allow for getting use out of object instantation somewhat more often than plain C structs do. :) Also, consider making use of namespaces for additional protection.

Quote:
Since the private section is declared along with the class, any time I have a member variable that is not a standard type I have to include the header file for it. For instance, to keep an open file handle FILE *fp; I have to include #include <stdio.h> in the header. Now any other class that includes this class has stdio.h included in it, with no need. Any other class is never going to access that file pointer. This creates alot of redundant header includes that the compiler now has to dredge through.


It's not a problem; including a header multiple times is not going to add anything extra to your program (because of include guards; and if not for those, you would get linker errors) - you should know this. All you might pay is compilation time, and there are ways to deal with that ("#pragma once" on some compilers - others are smart enough to grok include guards fully and not bother reopening the files - and precompiled headers).

BTW, you will get lots and lots of mileage out of learning the new C++ I/O via <iostream> (no .h!!!) - trust me. :)

Quote:

So i guess there is no way around this without some ugly kludge involving inheritance and forward declarations with a member variable instace.


As noted, the size of a pointer is always known, so you can use a pointer member (and possibly a forward declaration just to say "this is a class, therefore I can make a pointer to one"). However, this adds extra complexity, so you should only be doing it when you need to (i.e. to resolve a cyclic aggregation of structs - but again, you should be familiar with such tricks).

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.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!