Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Signalsending system  (Read 7036 times)

0 Members and 1 Guest are viewing this topic.

Groogy

  • Hero Member
  • *****
  • Posts: 1469
    • MSN Messenger - groogy@groogy.se
    • View Profile
    • http://www.groogy.se
    • Email
Signalsending system
« 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:
Code: [Select]

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
Developer and Maker of rbSFML and Programmer at Paradox Development Studio

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Signalsending system
« Reply #1 on: April 07, 2009, 09:41:22 am »
Quote
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/.
Laurent Gomila - SFML developer

Groogy

  • Hero Member
  • *****
  • Posts: 1469
    • MSN Messenger - groogy@groogy.se
    • View Profile
    • http://www.groogy.se
    • Email
Signalsending system
« Reply #2 on: April 07, 2009, 12:36:55 pm »
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:

Code: [Select]

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.
Developer and Maker of rbSFML and Programmer at Paradox Development Studio

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Signalsending system
« Reply #3 on: April 07, 2009, 01:13:04 pm »
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?
Laurent Gomila - SFML developer

Groogy

  • Hero Member
  • *****
  • Posts: 1469
    • MSN Messenger - groogy@groogy.se
    • View Profile
    • http://www.groogy.se
    • Email
Signalsending system
« Reply #4 on: April 07, 2009, 01:41:45 pm »
Because the SignalBase copy constructor don't know of the children of SignalBase when I try this:

Code: [Select]

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 ^^
Developer and Maker of rbSFML and Programmer at Paradox Development Studio

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Signalsending system
« Reply #5 on: April 07, 2009, 01:49:16 pm »
Quote
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.
Laurent Gomila - SFML developer

Groogy

  • Hero Member
  • *****
  • Posts: 1469
    • MSN Messenger - groogy@groogy.se
    • View Profile
    • http://www.groogy.se
    • Email
Signalsending system
« Reply #6 on: April 07, 2009, 02:01:05 pm »
Interested in the Clone function. It would have to look something like this:

Code: [Select]

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?

Quote from: "Laurent"

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:
Code: [Select]

SignalSender sender;
// Add subscribers
sender.AddSignal(QuitSignal()); // Add a signal to the queue
sender.SendSignals();
Developer and Maker of rbSFML and Programmer at Paradox Development Studio

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Signalsending system
« Reply #7 on: April 07, 2009, 02:29:37 pm »
Quote
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:
Code: [Select]
SignalBase* SignalXxx::Clone() const
{
    return new SignalXxx(*this);
}


Quote
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 ;)

Quote
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.
Laurent Gomila - SFML developer

Groogy

  • Hero Member
  • *****
  • Posts: 1469
    • MSN Messenger - groogy@groogy.se
    • View Profile
    • http://www.groogy.se
    • Email
Signalsending system
« Reply #8 on: April 07, 2009, 02:43:10 pm »
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.
Developer and Maker of rbSFML and Programmer at Paradox Development Studio

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Signalsending system
« Reply #9 on: April 07, 2009, 03:12:21 pm »
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.
Laurent Gomila - SFML developer

Groogy

  • Hero Member
  • *****
  • Posts: 1469
    • MSN Messenger - groogy@groogy.se
    • View Profile
    • http://www.groogy.se
    • Email
Signalsending system
« Reply #10 on: April 07, 2009, 03:17:01 pm »
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
Developer and Maker of rbSFML and Programmer at Paradox Development Studio

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Signalsending system
« Reply #11 on: April 07, 2009, 04:58:37 pm »
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.
Laurent Gomila - SFML developer

Groogy

  • Hero Member
  • *****
  • Posts: 1469
    • MSN Messenger - groogy@groogy.se
    • View Profile
    • http://www.groogy.se
    • Email
Signalsending system
« Reply #12 on: April 07, 2009, 05:14:08 pm »
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 ^^
Developer and Maker of rbSFML and Programmer at Paradox Development Studio

Astrof

  • Full Member
  • ***
  • Posts: 135
    • View Profile
Signalsending system
« Reply #13 on: April 07, 2009, 06:11:30 pm »
just a very small minor thing: Receiver is spelled wrong.  I don't know if that was intentional or not.  

Grammar police out.

Groogy

  • Hero Member
  • *****
  • Posts: 1469
    • MSN Messenger - groogy@groogy.se
    • View Profile
    • http://www.groogy.se
    • Email
Signalsending system
« Reply #14 on: April 07, 2009, 06:41:31 pm »
Quote from: "Astrof"
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.
Developer and Maker of rbSFML and Programmer at Paradox Development Studio

 

anything