Goodbye Listeners and Callbacks, Hello Lambdas?


#1

Are there any plans to modernize the callback system used for GUI elements to take lambdas, now that you’re using c++11 for Juce5? It was mentioned here by @otristan :


#2

There’s at least one downside to using lambdas/std::functions for the callbacks, namely that is tricky to remove them later after they’ve been set. Still, I think it would be quite valuable to have support for lambdas for the basic callbacks from buttons, sliders and similar components.


#3

This was a large part of my talk last year at ADC but the video hasn’t made it online yet…

You can see the notes here though: https://github.com/drowaudio/presentations/blob/master/ADC%202016%20-%20Using%20Modern%20C%2B%2B%20to%20Improve%20Code%20Clarity/Using%20Modern%20C%2B%2B%20to%20Improve%20Code%20Clarity.pdf (Look for ButtonClickCallback on slide 28)

Why is it hard to remove the callbacks? Just set them to nullptr?


#4

If the system needs support for multiple “listeners”…


#5

This pdf is amazing!!! Why isn’t any of this already in JUCE? that PopupMenu Callbacks!!! come on now! that is amazing!


#6

Yes, I agree with that but to be honest since I’ve started using this style I’ve never actually needed to attach more than one listener to something.

The more common and dare I say “better” approach would be to have a single button callback update your model and then other listeners respond to this.

I’m not saying I’d replace the ListenerList system, on the contrary there are many examples of its use in that talk but for simple UI elements (pop up menus, sliders, buttons etc.) lambda based callbacks can be much more terse and quicker to use.


#7

Just for the discussion, what would be some instances where you would need to attach multiple listeners to a component/object?


#8

Whatever use cases there are for the current Juce Components to support multiple listeners. The method is named addListener, after all, not just setListener…


#9

oh, like if you have a component with several buttons as children? and the component is who listens for button interaction, not the buttons themselves?

But wouldn’t being able to attach lambdas directly to the buttons eliminate that problem?


#10

In my opinion, the best way (that I know of) to deregister lambda listeners is to let the AddListener function return a “deregister function” that can be called any time later (with no arguments). I replaced my Listener classes with lambdas and this deregister method some time ago and never looked back :slight_smile:


#11

please share the code!!! @idearcos


#12

@matkatmusic Just in case my post was misleading, I meant that I replaced listener with lambdas in my own code, not in the interaction with Juce. Sorry if anyone thought I applied this method directly to Juce.

However, just in case anyone is interested, let me add a quick example of it here. You can easily implement it in your own code, and I think it could be a good alternative way to use listeners in Juce too, if the users and developers like it.

class Foo
{
public:
    using Listener = std::function<void(int value)>;

    std::function<void()> AddListener(Listener &&listener_function)
    {
        auto it = listeners_.emplace(listeners_.begin(), listener_function);
        return [this, it]() { listeners_.erase(it); };
    }

private:
    std::list<Listener> listeners_;
}

Edit: fixed a typo


#13

Yes, and it may be convenient at times, but in other circumstances doing so would spread around code that would be more maintainable and readable if it were centralized in a parent/handler object.

I think that adding lambda support alongside keeping the possibility to register listeners is the best way to go.
It doesn’t break existing code, and gives the possibility to choose the approach that better suits the problem at hand without forcing a “policy”.


#14

http://timj.testbit.eu/2013/01/25/cpp11-signal-system-performance/