SFML community forums
Help => General => Topic started by: Groogy on April 06, 2009, 10:50:26 pm
-
Oi! I'm making my own system for SFML. I've run into some problems and would like some help on it.
Anyway here's the basic how it works. We have a 3 Base objects. SignalSender, SignalReciver, SignalBase. SignalBase represents a signal which is sent by SignalSender to a SignalReciver. And the SignalReciver can subscribe to different levels of signals. Every signal is associated with a string name. For instance QuitSignal's name is "Signal.Window.Quit" and represents a Closed event. A Receiver can subscribe to "Signal.Window" to receive all Window associated events. Or he can be precise and say that he only wants "Signal.Window.Quit" or he can subscribe to "Signal" to get all signals. The biggest point with the system is that the developer can create his/her own signal classes.
See it as a extension to the already in place system.
Anyway here's problem number one. Data... Data data data. Since I store all events in a std::queue<SignalBase> some data seems to be lost when I pump the extended SignalBase class in there. (Like MouseMovedSignal). I guess it's because the queue only copies the SignalBase part of the entire object?
Here's some code:
class SignalSender {
private:
std::multimap<std::string, SignalReciver *> subscribers;
std::queue<SignalBase> signalQueue;
protected:
void SendToObjects(const SignalBase& signal, const std::string& currentNamespace);
SignalBase ConvertSFMLToSignal(const sf::Event& event, sf::Window * window);
public:
SignalSender();
void SubscribeReciver(SignalReciver * reciver, const std::string& type);
void SubscribeReciver(SignalReciver * reciver, const SignalBase& type);
bool IsReciverSubscribed(SignalReciver * reciver, const std::string& type);
void AddSignal(const SignalBase& signal);
void SendSignals();
void FetchEventsFromWindow(sf::Window * window);
};
void SignalSender::AddSignal(const SignalBase& signal) {
this->signalQueue.push(signal);
}
I was thinking of like, adding like some resource object in the SignalBase where we stuff in all signal specific data though I'm wondering if there's a more gracious method to do it. Also, is there some class I can use in the stl already?
Thx for the help on before hand
PS: Oh yeah Laurent, I fixed so it can calculate relative movement for ya now in multiple windows :D
-
I guess it's because the queue only copies the SignalBase part of the entire object?
True.
I see two solutions to your problem:
1/ Allocate your signals dynamically, and store pointers in your container. Then cast them back to their derived type when you pass them to receivers, using their type.
2/ Use a union (like SFML does), and like in 1/ extract the right part when you pass them back to receivers, according to the type of the event.
I think solution 1/ is cleaner and more C++ friendly than 2/.
-
Thx for the tips. I was thinking of doing it with dynamic allocation. Though I am doing it but not with the signal, I instead have an resource object. I'll show you:
class SignalBase {
protected:
//... more code
class Resource {
private:
int reference;
protected:
Resource() { reference = 1; };
public:
void IncRef() { reference++; };
void DecRef() { reference--; if(reference<=0) delete this; };
virtual ~Resource() {};
};
Resource * resourcePtr;
public:
//... more code
};
class KeyPressedSignal : public SignalBase {
public:
typedef sf::Key::Code KeyCode;
private:
class KeyResource : public Resource {
public:
KeyCode keycode;
bool alt;
bool control;
bool shift;
};
public:
//... more code
};
I've never developed my own class that uses Reference counting. So I feel a bit uneasy and worry about memory leaks. I had it print out text on creation and destruction and I didn't see any address that wasn't deleted.
Anyway, the copy constructor and the destructor in the signalBase takes care of the reference counting for you so the developer only needs to create the new class and then in the constructor create the object with new.
I know it's a bit more tricky... But.. I had a reason why yesterday before I went to sleep.. I think it was that if I hold a pointer to the object, I still has to copy the object with the new-operator (new SignalBase(signal); ). Which wouldn't copy the actual signal but only the SignalBase.
Anyway this way it just "passes" over the resource on the copy and increases the reference count in SignalBase.
-
I don't get the point of your Resource class. Can't the signal data be put directly in the signal class? Why do you need this extra level of indirection?
-
Because the SignalBase copy constructor don't know of the children of SignalBase when I try this:
void SignalSender::AddSignal(const SignalBase& signal) {
this->signalQueue.push(new SignalBase(signal));
}
That will only copy the SignalBase part and not the children. I don't want to force the developer to do the memory allocation so I'd like to keep it inside the Signal classes.
This way with resources, they don't need to think about memory allocation and only define the resource class. More or less.
I'm open for better ideas cause this way makes it a pain in the ass to access variables in the resource class ^^
-
Because the SignalBase copy constructor don't know of the children of SignalBase when I try this
Then use the clone pattern, i.e. do the copy in a virtual function named Clone().
But who's calling AddSignal, is it done internally? If so, you can call it directly with the dynamically allocated instance, so you don't need to copy it anymore.
Or you can make AddSignal a template function, so that you can pass it the true type of signal and call its copy constructor.
-
Interested in the Clone function. It would have to look something like this:
virtual SignalBase Clone();
// Can I write like this?
SignalBase::SignalBase(const SignalBase& copy) {
this = copy.Clone();
}
But doesn't that mean that each signal has to reimplement it to get the correct clone of the object?
But who's calling AddSignal, is it done internally? If so, you can call it directly with the dynamically allocated instance, so you don't need to copy it anymore.
It is available so that the developer (outside the object) can call it. You know like:
SignalSender sender;
// Add subscribers
sender.AddSignal(QuitSignal()); // Add a signal to the queue
sender.SendSignals();
-
Interested in the Clone function. It would have to look something like this:
No, you don't have to implement the copy constructor anymore. The Clone function is the copy constructor.
Or you can keep the copy constructor if you want, and simply call it in your Clone function:
SignalBase* SignalXxx::Clone() const
{
return new SignalXxx(*this);
}
But doesn't that mean that each signal has to reimplement it to get the correct clone of the object?
Yes, just like it would have to reimplement the copy constructor otherwise ;)
It is available so that the developer (outside the object) can call it
Ok I see. But my last comments still apply, it's up to you to decide what fits the most in your design.
-
Hmm.. Donno.. The resource way is more efficient at being copied. But being a pain in the ass when you need to access a member.
Resource -> Efficient and easy copy done in SignalBase.
Clone -> Easier to develop custom signals.
That's kind of the pros of each of the methods.
-
I don't think you need efficiency for such a system. Events are not generated 60 times per second, it's really ok to have one extra copy each time an event is triggered.
-
Lol. I went into a non-logical problem now.. I'm working on MouseEntered and can't come up with the name for it. It can be two things.
Signal.Mouse.EnteredWindow
Signal.Window.MouseEntered
Which one is most suitable? :P
-
As your system is hierarchical, I'd put Window first. And to follow your philosophy, I'd even decompose it into Signal.Window.Mouse.Entered so that one can just observe System.Window.Mouse events.
-
Nice idea ^^
And heeey! I'm the one developing it .... :oops:
**EDIT**
Well I'm almost done now. Adding a sf::Window pointer to all base signals for convenience so the developer doesn't have to keep track of "Which window am I working in right now". Though It's fairly simple to ignore it and work without it.
I don't think there's anything left. Except maybe doing it Thread-safe? Though if someone wants that they can probably modify it themselves.
So it will get posted on the wiki pretty soon ^^
-
just a very small minor thing: Receiver is spelled wrong. I don't know if that was intentional or not.
Grammar police out.
-
just a very small minor thing: Receiver is spelled wrong. I don't know if that was intentional or not.
Grammar police out.
Thx!
Anyway, should I add comments to the code? Or that doesn't matter? It's very simple code with little to comment on. Most is self-explaining.
There's a lot of files but little code inside them.
-
I've added it :D
http://www.sfml-dev.org/wiki/signalsender
Though this Wiki is confusing me so if someone could help me to get it into the right place ^^
-
thanks a lot, swedes are good people :P
-
thanks a lot, swedes are good people :P
Nah I just misses what's called a life ^^
-
You have to add it under wiki/en/sources, and add it to the index page (still wiki/en/sources). So now you have to remove the page you just created, and recreate it at the right place. I leave it to you as an exercise ;)
-
Aight, now it's there :D
http://www.sfml-dev.org/wiki/en/sources
-
Awsome! I found a great way using signals to keep track of what items is going to be painted :D And just send a custom signal to them telling them to repaint ^^
This was more useful for non-sfml events than I thought myself. :P