Sign in to follow this  
GeekPlusPlus

Question on OOP design preferences.

Recommended Posts

GeekPlusPlus    124
Hey all, I'm strictly talking C++ here when I talk about OOP. What i'm wondering is how some of you handle certain functions which are repeated throughout a program. Obviously you try to design away form this, but it's bound to happen. Something like an output function maybe which could be needed all over for bug reporting to talking. Anyway, how do some of you handle functions like this generaly? I really only see three options myself... 1)Independant global functions in a header somewhere. yuck. That's all i've really got to say about that. 2)You can wrap the function, or the group if they are related functions in a singleton and then grab the instance wherever you need it. This one seems fairly popular. But there's something about a singleton that just seems like cheating. 3)Wrap your functions in a class definition but make them static, so you can do CClass::Function(); But isn't this almost if not exactly the same as a global function in some header? You'd never Instantiate the class, so they are just functions in memory you're calling, does the scope help any? Also seems like cheating. I have no problem using a singleton to stop extra Instantiating, but using it just to cart around a function bugs me. Static functions hiding in a class definition bug me more. And global functions? ew =) So... I'm wondering what are some methods other people have used?

Share this post


Link to post
Share on other sites
Fruny    1658
Non-member functions are perfectly fine, and even sometimes required (e.g. mixed-mode arithmetic operators).

C++ namespaces are much better suited to hold functions than a class that would only have static members. And no, it's not cheating.

Share this post


Link to post
Share on other sites
MadKeithV    992
Quote:
Original post by GeekPlusPlus
I really only see three options myself...
1)Independant global functions in a header somewhere. yuck. That's all i've really got to say about that.


I agree with Fruny. Why yuck? Call a duck a duck, what you want is exactly that: an independent globally accessible function.

OOP is not a silver bullet.... just because you have a good hammer, don't make all the problems nails.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
What's your problem with (1)?

Here's what I like to do:


// File log.h

#ifndef LOG_H
#define LOG_H

#define g_logStream (*g_pLogStream)
extern std::ostream* g_pLogStream;

#endif



// File log.cpp

#include <iostream>
#include "Log.h"

// The output logging stream. This is initalized to std::cerr,
// but can be set to another stream object anytime.
std::ostream* g_pLogStream = &std::cerr;




With this, you just #include "log.h" wherever you want to use the logging stream. Usage:



#include "log.h"

// ...

void foo() {
g_logStream << "Hello, world!" << std::endl;
}



If you want to log to a file instead of std::cerr, you can set the g_pLogStream pointer to point to a file stream that you open at the start of the program and close before the program exits. It's easy to write, easy to use, and doesn't clutter things up much. Wrap all this in a namespace if you're worried about collisions and stuff.

Share this post


Link to post
Share on other sites
petewood    819
If you have a system where every object is "owned" by another object then you would have a base object at the root of a tree. This object (e.g. the application, or engine) could provide services such as logging. Any other object wanting to log something would pass on the request to its owner. Sort of like the ChainOfResponsibility Pattern.

If every object points to its owner already then it's not adding any burden but if you decide to introduce it to a design then there is a memory overhead of everything knowing what owns it. The calling code will be longer than just accessing one global object as the request would get passed along until it reached the root of the tree.

You would also be able to customise the response so objects owned by a particular object might have their requests for logging directed to a different log rather than the parent one.

It's an alternative. Hope it helps.

Pete

Share this post


Link to post
Share on other sites
Pxtl    354
I've often run into one problem in the ownership tree: the inability to bring in the namespace of parent objects. Am I alone in wishing you could "import" an instance's namespace in C++, similar to the "with" operation in visual basic (or "using" with namespaces). I often find it would be very handy with parent objects. Yes, it might also be somewhat obfuscating, but having to use -> operators all over the place to access the core engine functions is a big pain.

Share this post


Link to post
Share on other sites
petewood    819
Quote:
Original post by Pxtl
I've often run into one problem in the ownership tree: the inability to bring in the namespace of parent objects. Am I alone in wishing you could "import" an instance's namespace in C++, similar to the "with" operation in visual basic (or "using" with namespaces). I often find it would be very handy with parent objects. Yes, it might also be somewhat obfuscating, but having to use -> operators all over the place to access the core engine functions is a big pain.


I think what you are talking about is having an object navigating all the way back up to the core, from the leaf object itself. What I meant was you may have a number of hierarchies and individual classes each of which will have a log(string) function and each of which can if it wishes forward that to its owner.


#include <string>
#include <iostream>

class Owner {
Owner* m_owner;
public:
Owner(Owner* owner) : m_owner(owner) {}
//default Log just calls owner's Log function
virtual void Log(const std::string& message) {
if(m_owner)
m_owner->Log(message);
}
};

//don't have to derive from Owner as not overriding virtual Log
class BaseGameEntity {
Owner* m_owner;
public:
BaseGameEntity(Owner* owner) : m_owner(owner) {}
void Log(const std::string& message) {
m_owner->Log(message);
}
};

class Player : public BaseGameEntity {
public:
Player(Owner* owner) : BaseGameEntity(owner) {}
void SomeFunc() {
Log("called SomeFunc");//calls BaseGameEntity::Log->Owner::Log
}
};

class Application : public Owner {
public:
Application() : Owner(0) {}
//override Owner function so that it actually logs to std::cout
virtual void Log(const std::string& message) {
std::cout << message << "\n";
}
void Main() {
Player player(this);//'this' Application owns the player
player.SomeFunc();
}
};

int main() {
Application app;
app.Main();
return 0;
}



[Edited by - petewood on July 12, 2004 7:11:46 AM]

Share this post


Link to post
Share on other sites

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