sendChangeMessage


#1

sendChangeMessage(void* objectThatHasChanged)

description says : If a message has already been sent and hasn’t yet been delivered, this method won’t send another

Yes allright! But i think it should also send another message, if the message that hasn’t yet been delivered has another “objectThatHasChanged”, or am i wrong?

sendChangeMessage(objectA);
sendChangeMessage(objectA);
// will send one message, fine!

sendChangeMessage(objectA);
sendChangeMessage(objectB);
//will only send the first message :frowning:

I know there is sendSynchronousChangeMessage and ActionListener, but i think the current behaviour is not logical.


#2

Why are you trying to have one ChangeBroadcaster send messages for two different objects?


#3

yes, good question, one thing is that i use ChangeBroadcaster for my data-objects, they sending change messages itself, but when my application loads a whole document, these objects will be generated, and i have to send change messages for all of them, to update the GUI

ok, i can use instead:

objectA->sendChangeMessage(objectA)
objectB->sendChangeMessage(objectB)

the other thing is i have dynamically created/deleted data-objects, and dynamically created/deleted gui-objects, which have to be linked together via ChangeListeners.
If i link them directly, this would be a problem, often these objects are not existing when the change messages arrives.

So, i use some a kind of MessageHub, which is a ChangeListener and a ChangeBroadCaster. So any object registers / de-registers itself to the MessageHub.
This MessageHub is a ChangeBroadcaster which also sends messages for different objects.

class MessageHub : public DeletedAtShutdown , public ChangeListener, public ChangeBroadcaster { public: void changeListenerCallback( void* objectThatHasChanged ) { sendSynchronousChangeMessage(objectThatHasChanged); } };


#4

It sounds to me like you are trying to over-use events. If you have lots of UI components constantly popping into and out of existence, it probably doesn’t make sense to have each by a broadcaster. Equally though, trying to use a messaging hub sounds to me like you’re heading in the wrong direction.

Why do your data objects need to be in asynchronous communication with your UI objects? If they are fairly tightly paired to start with, and assuming there is good reason for them to be so, you could just hook them up with a standard synchronous callback. That way you’ll never run into scoping issues.

I’m not doubting your logic, but I’m having a little trouble coming up with a scenario that would require many dynamic UI components to be tightly bound to non-persistent data object peers. As a result it’s hard for me to see how you might otherwise approach your initial problem.


#5

its not so complex you might thing, if you have multiple editor/display objects, which are connected to the same data-object (which is embedded in a complex data-structure), you have to be sure that they get informed. The GUI-Component, can check "Is this the data-object i’m currently displaying, ok i have to update my display)
I use ChangeBroadcaster, to be sure that these GUI-objects will accessed from the Message-Thread, because the changeMessages can be initiated from other threads (for example a audio-render thread), so a direct synchronous callback is not a good solution

i found a work-around for both cases,
-after loading the document (see above)
-the Message Hub uses a sendSynchronousChangeMessage, because its called by a asynchronus ChangeMessage, so its already on the message thread.

My initial thought is not about that, its simply about this behaviour, i think its not logical

sendChangeMessage(objectA); // objectThatHasChanged is objectA
sendChangeMessage(objectA); // objectThatHasChanged is objectA
// will send one message, fine!

sendChangeMessage(objectA); // objectThatHasChanged is objectA
sendChangeMessage(objectB); // objectThatHasChanged is objectB
//will only send the first message


#6

[quote]sendChangeMessage(objectA); // objectThatHasChanged is objectA
sendChangeMessage(objectB); // objectThatHasChanged is objectB
//will only send the first message[/quote]

I agree! That’s not logical, but it’s also really not how the ChangeBroadcaster was designed to be used.

The void* parameter is provided for situations where you have a single ChangeListener which is registered with many ChangeBroadcasters. The listener can use the parameter to find out which of its broadcasters just fired. The intention was for each ChangeBroadcaster to always stick to a constant parameter value… a sort of “call sign” for that broadcaster.

The reason that it’s confusing is that it’s the sendChangeMessage method which takes the parameter - in retrospect I think I should have designed it so that you set that parameter value once for the entire ChangeBroadcaster, and given sendChangeMessage() no parameters…


#7

Are we supposed to have some kind of RTTI (or use the C++ built in RTTI) for the classes we use with sendMessage ?
Something like : (very simplified and probably wrong code but it’s just so you get the picture :slight_smile: )

SomeClassA : public ChangeBroadcaster
{

    someMethod()
    {
        doStuff();
        sendChangeMessage(&someClassAId);
    }

};

SomeClassB : public ChangeBroadcaster
{

    someMethod()
    {
        doStuff();
        sendChangeMessage(&someClassBId);
    }

};


SomeClassC : public ChangeListener
{
    SomeClassA instanceOfA;
    SomeClassB instanceOfB;

    SomeClassC()
    {
        instanceOfA.addChangeListener(this);
        instanceOfB.addChangeListener(this);
    }

    changeListenerCallback (void *objectThatHasChanged)
    {
        if (*objectThatHasChanged==someClassAId)
        {
            //react to the fact that instanceOfA has changed)
        }
        else if (*objectThatHasChanged==someClassBId)
        {
            //react to the fact that instanceOfB has changed}
        }
    }
};

Is this what we are supposed to do ? It doesn’t seem very “clean” but maybe it’s ok to do that?


#8

No, its not ok to do that! The void* parameter is a bit of a historical artifact, I created that class many many years ago, and if I’d been designing it today now, I’d certainly not have given it that parameter. At some point I’ll probably remove it.

It was intended as a way to be able to recognise which of several possible sources an event has come from - e.g.

[code]class MyListener
{
void register()
{
broadcaster1.addChangeListener (this);
broadcaster2.addChangeListener (this);
}

void changeListenerCallback (void* source)
{
    if (source == broadcaster1)
    {
        ..etc
    }
    else if (source == broadcaster2)
    {
        ..etc
    }
}

};
[/code]

…so a better choice for that parameter would probably have been a ChangeBroadcaster*, but in fact I don’t think I ever use it at all, and it’s probably better if you don’t either!


#9

Ok I see the point. I was surprised to see a “void *” in Juce while generally, you care a lot about enforcing the good practices of C++ :wink:


#10

[quote=“jules”]At some point I’ll probably remove it.
[…]…so a better choice for that parameter would probably have been a ChangeBroadcaster*, but in fact I don’t think I ever use it at all, and it’s probably better if you don’t either![/quote]

I really hope you’re only thinking about the parameter in ChangeBroadcaster::send(Synchronous)ChangeMessage. I’m using it a lot, and I can’t think of a single time I passed it anything else than ‘this’.

But some of my classes heavily rely on that they can attach themselves to multiple ChangeBroadcasters, and can distinguish which one has changed by that parameter. So removing this parameter in ChangeListener::changeListenerCallback would mean heavy refactoring for me. I’d be happy to see the type to change to ChangeBroadcaster* though, as currently I wasn’t able to dynamic_cast the void* pointer (in cases where my ChangeListener knows what type to expect, and wants to call a specific method of that type)


#11

Yes, making the pointer a ChangeBroadcaster would make sense, and that’d also mean that the sendChangeMessage method wouldn’t need to take a parameter.


#12

[quote=“steffen”][quote=“jules”]At some point I’ll probably remove it.
[…]…so a better choice for that parameter would probably have been a ChangeBroadcaster*, but in fact I don’t think I ever use it at all, and it’s probably better if you don’t either![/quote]

I really hope you’re only thinking about the parameter in ChangeBroadcaster::send(Synchronous)ChangeMessage. I’m using it a lot, and I can’t think of a single time I passed it anything else than ‘this’.

But some of my classes heavily rely on that they can attach themselves to multiple ChangeBroadcasters, and can distinguish which one has changed by that parameter. So removing this parameter in ChangeListener::changeListenerCallback would mean heavy refactoring for me. I’d be happy to see the type to change to ChangeBroadcaster* though, as currently I wasn’t able to dynamic_cast the void* pointer (in cases where my ChangeListener knows what type to expect, and wants to call a specific method of that type)[/quote]

My humble opinion here is that using dynamic_cast<> and relying on RTTI (what I mentioned above) is actually the same thing, so it’s far from ideal (many blog articles explain why, but basically, it’s not really standard so implementations might vary depending on which compiler you use, and it’s not efficient)

Looking for inspiration, I had a look at juce’s button class and I really liked the way events are implemented.

Baiscally, Jules defines a class Button::Listener inside Button and this class defines 2 pure virtual methods :

virtual void buttonClicked (Button* button) = 0; virtual void buttonStateChanged (Button*) =0

Then if you make your main component inherit from Button::Listener, you have to implement those 2 methods, and then in your implementation, you definitely know you’re dealing with a button.

Basically, instead of having a single “catch-all” changeListenerCallback method with a bunch of conditional statements to determine which object actually changed, you have different listener methods per object type (Button::Listener::buttonClicked(), MyStuff::Listener::myStuffChanged() and so on …) , and therefore,

  • you don’t need RTTI
  • you don’t have if or switch statements (so better efficiency)
  • you can choose whch parameters you want to pass to your callback instead of relying on a single void* ( MyStuff::Listener::myStuffChanged(int parameter1, someclass parameter2):wink:
  • you have to maintain your own listener list and manually call the callbacks, however, most of this work is already done in Juce (see ListenerList)

HTH