Async addition to ListenerList

What do you think on this one ? Something on the line of this could be added to the library… i find it very useful in case i need to mix sync/async listener callbacks from different scenarios.

template <class ListenerClass,
          class ArrayType = juce::Array<ListenerClass*> >
class AsyncListenerList
{
    // Horrible macros required to support VC7..
    #ifndef DOXYGEN
     #if JUCE_VC8_OR_EARLIER
       #define LL_TEMPLATE(a)   typename P##a, typename Q##a
       #define LL_PARAM(a)      Q##a& param##a
     #else
       #define LL_TEMPLATE(a)   typename P##a
       #define LL_PARAM(a)      typename juce::TypeHelpers::ParameterType<P##a>::type param##a
     #endif
    #endif

public:
    typedef AsyncListenerList<ListenerClass, ArrayType> ThisType;
    typedef ListenerClass ListenerType;

    //==============================================================================
    /** Creates an empty list. */
    AsyncListenerList()
    {
    }

    /** Destructor. */
    ~AsyncListenerList()
    {
        masterReference.clear();
    }

    //==============================================================================
    /** Adds a listener to the list.
        A listener can only be added once, so if the listener is already in the list,
        this method has no effect.
        @see remove
    */
    void add (ListenerClass* const listenerToAdd)
    {
        // Listeners can't be null pointers!
        jassert (listenerToAdd != nullptr);

        const juce::ScopedLock sl (listenerLock);

        if (listenerToAdd != nullptr)
            listeners.addIfNotAlreadyThere (listenerToAdd);
    }

    /** Removes a listener from the list.
        If the listener wasn't in the list, this has no effect.
    */
    void remove (ListenerClass* const listenerToRemove)
    {
        // Listeners can't be null pointers!
        jassert (listenerToRemove != nullptr);

        const juce::ScopedLock sl (listenerLock);

        listeners.removeFirstMatchingValue (listenerToRemove);
    }

    /** Returns the number of registered listeners. */
    int size() const noexcept
    {
        const juce::ScopedLock sl (listenerLock);

        return listeners.size();
    }

    /** Returns true if any listeners are registered. */
    bool isEmpty() const noexcept
    {
        const juce::ScopedLock sl (listenerLock);

        return listeners.size() == 0;
    }

    /** Clears the list. */
    void clear()
    {
        const juce::ScopedLock sl (listenerLock);

        listeners.clear();
    }

    /** Returns true if the specified listener has been added to the list. */
    bool contains (ListenerClass* const listener) const noexcept
    {
        const juce::ScopedLock sl (listenerLock);

        return listeners.contains (listener);
    }

    //==============================================================================
    /** Calls a member function on each listener in the list, with no parameters. */
    void call (void (ListenerClass::*callbackFunction) ())
    {
        callChecked (static_cast <const DummyBailOutChecker&> (DummyBailOutChecker()), callbackFunction);
    }

    /** Calls a member function on each listener in the list, with no parameters and a bail-out-checker.
        See the class description for info about writing a bail-out checker. */
    template <class BailOutCheckerType>
    void callChecked (const BailOutCheckerType& bailOutChecker,
                      void (ListenerClass::*callbackFunction) ())
    {
        const juce::ScopedLock sl (listenerLock);

        for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);)
            (iter.getListener()->*callbackFunction) ();
    }

    //==============================================================================
    template <class BailOutCheckerType>
    class CallbackMessage0 : public juce::MessageManager::MessageBase
    {
    public:
        CallbackMessage0 (const ThisType* all,
                          const BailOutCheckerType& bailOutChecker,
                          void (ListenerClass::*callbackFunction) ()) noexcept
            : listenerList (const_cast<ThisType*> (all)),
            bailOutChecker (bailOutChecker),
            callback (callbackFunction)
        {}

        void messageCallback() override
        {
            if (const ThisType* const all = listenerList)
                listenerList->callChecked(bailOutChecker, callback);
        }

    private:
        juce::WeakReference<ThisType> listenerList;
        const BailOutCheckerType& bailOutChecker;
        void (ListenerClass::*callback) ();

        JUCE_DECLARE_NON_COPYABLE (CallbackMessage0)
    };

    void callAsync (void (ListenerClass::*callbackFunction) ())
    {
        (new CallbackMessage0<DummyBailOutChecker>
            (this, static_cast <const DummyBailOutChecker&> (DummyBailOutChecker()), callbackFunction))->post();
    }

    template <class BailOutCheckerType>
    void callAsyncChecked (const BailOutCheckerType& bailOutChecker,
                           void (ListenerClass::*callbackFunction) ())
    {
        (new CallbackMessage0<BailOutCheckerType>
            (this, bailOutChecker, callbackFunction))->post();
    }

    //==============================================================================
    /** Calls a member function on each listener in the list, with 1 parameter. */
    template <LL_TEMPLATE(1)>
    void call (void (ListenerClass::*callbackFunction) (P1), LL_PARAM(1))
    {
        for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();)
            (iter.getListener()->*callbackFunction) (param1);
    }

    /** Calls a member function on each listener in the list, with one parameter and a bail-out-checker.
        See the class description for info about writing a bail-out checker. */
    template <class BailOutCheckerType, LL_TEMPLATE(1)>
    void callChecked (const BailOutCheckerType& bailOutChecker,
                      void (ListenerClass::*callbackFunction) (P1),
                      LL_PARAM(1))
    {
        for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);)
            (iter.getListener()->*callbackFunction) (param1);
    }

    //==============================================================================
    template <class BailOutCheckerType, LL_TEMPLATE(1)>
    class CallbackMessage1 : public juce::MessageManager::MessageBase
    {
    public:
        CallbackMessage1 (const ThisType* all,
                          const BailOutCheckerType& bailOutChecker,
                          void (ListenerClass::*callbackFunction) (P1),
                          LL_PARAM(1)) noexcept
            : listenerList (const_cast<ThisType*> (all)),
            bailOutChecker (bailOutChecker),
            callback (callbackFunction),
            param1 (param1)
        {}

        void messageCallback() override
        {
            if (const ThisType* const all = listenerList)
                listenerList->callChecked(bailOutChecker, callback, param1);
        }

    private:
        juce::WeakReference<ThisType> listenerList;
        const BailOutCheckerType& bailOutChecker;
        void (ListenerClass::*callback) (P1);
        LL_PARAM(1);

        JUCE_DECLARE_NON_COPYABLE (CallbackMessage1)
    };

    template <LL_TEMPLATE(1)>
    void callAsync (void (ListenerClass::*callbackFunction) (P1), LL_PARAM(1))
    {
        (new CallbackMessage1<DummyBailOutChecker, P1>
            (this, static_cast <const DummyBailOutChecker&> (DummyBailOutChecker()), callbackFunction, param1))->post();
    }

    template <class BailOutCheckerType, LL_TEMPLATE(1)>
    void callAsyncChecked (const BailOutCheckerType& bailOutChecker,
                           void (ListenerClass::*callbackFunction) (P1),
                           LL_PARAM(1))
    {
        (new CallbackMessage1<BailOutCheckerType, P1>
            (this, bailOutChecker, callbackFunction, param1))->post();
    }

    //==============================================================================
    /** Calls a member function on each listener in the list, with 2 parameters. */
    template <LL_TEMPLATE(1), LL_TEMPLATE(2)>
    void call (void (ListenerClass::*callbackFunction) (P1, P2),
               LL_PARAM(1), LL_PARAM(2))
    {
        for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();)
            (iter.getListener()->*callbackFunction) (param1, param2);
    }

    /** Calls a member function on each listener in the list, with 2 parameters and a bail-out-checker.
        See the class description for info about writing a bail-out checker. */
    template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2)>
    void callChecked (const BailOutCheckerType& bailOutChecker,
                      void (ListenerClass::*callbackFunction) (P1, P2),
                      LL_PARAM(1), LL_PARAM(2))
    {
        for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);)
            (iter.getListener()->*callbackFunction) (param1, param2);
    }

    //==============================================================================
    template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2)>
    class CallbackMessage2 : public juce::MessageManager::MessageBase
    {
    public:
        CallbackMessage2 (const ThisType* all,
                          const BailOutCheckerType& bailOutChecker,
                          void (ListenerClass::*callbackFunction) (P1, P2),
                          LL_PARAM(1), LL_PARAM(2)) noexcept
            : listenerList (const_cast<ThisType*> (all)),
            bailOutChecker (bailOutChecker),
            callback (callbackFunction),
            param1 (param1),
            param2 (param2)
        {}

        void messageCallback() override
        {
            if (const ThisType* const all = listenerList)
                listenerList->callChecked(bailOutChecker, callback, param1, param2);
        }

    private:
        juce::WeakReference<ThisType> listenerList;
        const BailOutCheckerType& bailOutChecker;
        void (ListenerClass::*callback) (P1, P2);
        LL_PARAM(1);
        LL_PARAM(2);

        JUCE_DECLARE_NON_COPYABLE (CallbackMessage2)
    };

    template <LL_TEMPLATE(1), LL_TEMPLATE(2)>
    void callAsync (void (ListenerClass::*callbackFunction) (P1, P2), LL_PARAM(1), LL_PARAM(2))
    {
        (new CallbackMessage2<DummyBailOutChecker, P1, P2>
            (this, static_cast <const DummyBailOutChecker&> (DummyBailOutChecker()), callbackFunction, param1, param2))->post();
    }

    template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2)>
    void callAsyncChecked (const BailOutCheckerType& bailOutChecker,
                           void (ListenerClass::*callbackFunction) (P1, P2),
                           LL_PARAM(1), LL_PARAM(2))
    {
        (new CallbackMessage2<BailOutCheckerType, P1, P2>
            (this, bailOutChecker, callbackFunction, param1, param2))->post();
    }

    //==============================================================================
    /** Calls a member function on each listener in the list, with 3 parameters. */
    template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3)>
    void call (void (ListenerClass::*callbackFunction) (P1, P2, P3),
               LL_PARAM(1), LL_PARAM(2), LL_PARAM(3))
    {
        for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();)
            (iter.getListener()->*callbackFunction) (param1, param2, param3);
    }

    /** Calls a member function on each listener in the list, with 3 parameters and a bail-out-checker.
        See the class description for info about writing a bail-out checker. */
    template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3)>
    void callChecked (const BailOutCheckerType& bailOutChecker,
                      void (ListenerClass::*callbackFunction) (P1, P2, P3),
                      LL_PARAM(1), LL_PARAM(2), LL_PARAM(3))
    {
        for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);)
            (iter.getListener()->*callbackFunction) (param1, param2, param3);
    }

    //==============================================================================
    template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3)>
    class CallbackMessage3 : public juce::MessageManager::MessageBase
    {
    public:
        CallbackMessage3 (const ThisType* all,
                          const BailOutCheckerType& bailOutChecker,
                          void (ListenerClass::*callbackFunction) (P1, P2, P3),
                          LL_PARAM(1), LL_PARAM(2), LL_PARAM(3)) noexcept
            : listenerList (const_cast<ThisType*> (all)),
            bailOutChecker (bailOutChecker),
            callback (callbackFunction),
            param1 (param1),
            param2 (param2),
            param3 (param3)
        {}

        void messageCallback() override
        {
            if (const ThisType* const all = listenerList)
                listenerList->callChecked(bailOutChecker, callback, param1, param2, param3);
        }

    private:
        juce::WeakReference<ThisType> listenerList;
        const BailOutCheckerType& bailOutChecker;
        void (ListenerClass::*callback) (P1, P2, P3);
        LL_PARAM(1);
        LL_PARAM(2);
        LL_PARAM(3);

        JUCE_DECLARE_NON_COPYABLE (CallbackMessage3)
    };

    template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3)>
    void callAsync (void (ListenerClass::*callbackFunction) (P1, P2, P3), LL_PARAM(1), LL_PARAM(2), LL_PARAM(3))
    {
        (new CallbackMessage3<DummyBailOutChecker, P1, P2, P3>
            (this, static_cast <const DummyBailOutChecker&> (DummyBailOutChecker()), callbackFunction, param1, param2, param3))->post();
    }

    template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3)>
    void callAsyncChecked (const BailOutCheckerType& bailOutChecker,
                           void (ListenerClass::*callbackFunction) (P1, P2, P3),
                           LL_PARAM(1), LL_PARAM(2), LL_PARAM(3))
    {
        (new CallbackMessage3<BailOutCheckerType, P1, P2, P3>
            (this, bailOutChecker, callbackFunction, param1, param2, param3))->post();
    }

    //==============================================================================
    /** Calls a member function on each listener in the list, with 4 parameters. */
    template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4)>
    void call (void (ListenerClass::*callbackFunction) (P1, P2, P3, P4),
               LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4))
    {
        for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();)
            (iter.getListener()->*callbackFunction) (param1, param2, param3, param4);
    }

    /** Calls a member function on each listener in the list, with 4 parameters and a bail-out-checker.
        See the class description for info about writing a bail-out checker. */
    template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4)>
    void callChecked (const BailOutCheckerType& bailOutChecker,
                      void (ListenerClass::*callbackFunction) (P1, P2, P3, P4),
                      LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4))
    {
        for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);)
            (iter.getListener()->*callbackFunction) (param1, param2, param3, param4);
    }

    //==============================================================================
    template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4)>
    class CallbackMessage4 : public juce::MessageManager::MessageBase
    {
    public:
        CallbackMessage4 (const ThisType* all,
                          const BailOutCheckerType& bailOutChecker,
                          void (ListenerClass::*callbackFunction) (P1, P2, P3, P4),
                          LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4)) noexcept
            : listenerList (const_cast<ThisType*> (all)),
            bailOutChecker (bailOutChecker),
            callback (callbackFunction),
            param1 (param1),
            param2 (param2),
            param3 (param3),
            param4 (param4)
        {}

        void messageCallback() override
        {
            if (const ThisType* const all = listenerList)
                listenerList->callChecked(bailOutChecker, callback, param1, param2, param3, param4);
        }

    private:
        juce::WeakReference<ThisType> listenerList;
        const BailOutCheckerType& bailOutChecker;
        void (ListenerClass::*callback) (P1, P2, P3, P4);
        LL_PARAM(1);
        LL_PARAM(2);
        LL_PARAM(3);
        LL_PARAM(4);

        JUCE_DECLARE_NON_COPYABLE (CallbackMessage4)
    };

    template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4)>
    void callAsync (void (ListenerClass::*callbackFunction) (P1, P2, P3, P4), LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4))
    {
        (new CallbackMessage4<DummyBailOutChecker, P1, P2, P3, P4>
            (this, static_cast <const DummyBailOutChecker&> (DummyBailOutChecker()), callbackFunction, param1, param2, param3, param4))->post();
    }

    template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4)>
    void callAsyncChecked (const BailOutCheckerType& bailOutChecker,
                           void (ListenerClass::*callbackFunction) (P1, P2, P3, P4),
                           LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4))
    {
        (new CallbackMessage4<BailOutCheckerType, P1, P2, P3, P4>
            (this, bailOutChecker, callbackFunction, param1, param2, param3, param4))->post();
    }

    //==============================================================================
    /** Calls a member function on each listener in the list, with 5 parameters. */
    template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5)>
    void call (void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5),
               LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5))
    {
        for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();)
            (iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5);
    }

    /** Calls a member function on each listener in the list, with 5 parameters and a bail-out-checker.
        See the class description for info about writing a bail-out checker. */
    template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5)>
    void callChecked (const BailOutCheckerType& bailOutChecker,
                      void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5),
                      LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5))
    {
        for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);)
            (iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5);
    }

    //==============================================================================
    template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5)>
    class CallbackMessage5 : public juce::MessageManager::MessageBase
    {
    public:
        CallbackMessage5 (const ThisType* all,
                          const BailOutCheckerType& bailOutChecker,
                          void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5),
                          LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5)) noexcept
            : listenerList (const_cast<ThisType*> (all)),
            bailOutChecker (bailOutChecker),
            callback (callbackFunction),
            param1 (param1),
            param2 (param2),
            param3 (param3),
            param4 (param4),
            param5 (param5)
        {}

        void messageCallback() override
        {
            if (const ThisType* const all = listenerList)
                listenerList->callChecked(bailOutChecker, callback, param1, param2, param3, param4, param5);
        }

    private:
        juce::WeakReference<ThisType> listenerList;
        const BailOutCheckerType& bailOutChecker;
        void (ListenerClass::*callback) (P1, P2, P3, P4, P5);
        LL_PARAM(1);
        LL_PARAM(2);
        LL_PARAM(3);
        LL_PARAM(4);
        LL_PARAM(5);

        JUCE_DECLARE_NON_COPYABLE (CallbackMessage5)
    };

    template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5)>
    void callAsync (void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5), LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5))
    {
        (new CallbackMessage5<DummyBailOutChecker, P1, P2, P3, P4, P5>
            (this, static_cast <const DummyBailOutChecker&> (DummyBailOutChecker()), callbackFunction, param1, param2, param3, param4, param5))->post();
    }

    template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5)>
    void callAsyncChecked (const BailOutCheckerType& bailOutChecker,
                           void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5),
                           LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5))
    {
        (new CallbackMessage5<BailOutCheckerType, P1, P2, P3, P4, P5>
            (this, bailOutChecker, callbackFunction, param1, param2, param3, param4, param5))->post();
    }

    //==============================================================================
    /** Calls a member function on each listener in the list, with 5 parameters. */
    template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5), LL_TEMPLATE(6)>
    void call (void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5, P6),
               LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5), LL_PARAM(6))
    {
        for (Iterator<DummyBailOutChecker, ThisType> iter (*this); iter.next();)
            (iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5, param6);
    }

    /** Calls a member function on each listener in the list, with 5 parameters and a bail-out-checker.
        See the class description for info about writing a bail-out checker. */
    template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5), LL_TEMPLATE(6)>
    void callChecked (const BailOutCheckerType& bailOutChecker,
                      void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5, P6),
                      LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5), LL_PARAM(6))
    {
        for (Iterator<BailOutCheckerType, ThisType> iter (*this); iter.next (bailOutChecker);)
            (iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5, param6);
    }

    //==============================================================================
    template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5), LL_TEMPLATE(6)>
    class CallbackMessage6 : public juce::MessageManager::MessageBase
    {
    public:
        CallbackMessage6 (const ThisType* all,
                          const BailOutCheckerType& bailOutChecker,
                          void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5, P6),
                          LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5), LL_PARAM(6)) noexcept
            : listenerList (const_cast<ThisType*> (all)),
            bailOutChecker (bailOutChecker),
            callback (callbackFunction),
            param1 (param1),
            param2 (param2),
            param3 (param3),
            param4 (param4),
            param5 (param5),
            param6 (param6)
        {}

        void messageCallback() override
        {
            if (const ThisType* const all = listenerList)
                listenerList->callChecked(bailOutChecker, callback, param1, param2, param3, param4, param5, param6);
        }

    private:
        juce::WeakReference<ThisType> listenerList;
        const BailOutCheckerType& bailOutChecker;
        void (ListenerClass::*callback) (P1, P2, P3, P4, P5, P6);
        LL_PARAM(1);
        LL_PARAM(2);
        LL_PARAM(3);
        LL_PARAM(4);
        LL_PARAM(5);
        LL_PARAM(6);

        JUCE_DECLARE_NON_COPYABLE (CallbackMessage6)
    };

    template <LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5), LL_TEMPLATE(6)>
    void callAsync (void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5, P6), LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5), LL_PARAM(6))
    {
        (new CallbackMessage6<DummyBailOutChecker, P1, P2, P3, P4, P5, P6>
            (this, static_cast <const DummyBailOutChecker&> (DummyBailOutChecker()), callbackFunction, param1, param2, param3, param4, param5, param6))->post();
    }

    template <class BailOutCheckerType, LL_TEMPLATE(1), LL_TEMPLATE(2), LL_TEMPLATE(3), LL_TEMPLATE(4), LL_TEMPLATE(5), LL_TEMPLATE(6)>
    void callAsyncChecked (const BailOutCheckerType& bailOutChecker,
                           void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5, P6),
                           LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5), LL_PARAM(6))
    {
        (new CallbackMessage6<BailOutCheckerType, P1, P2, P3, P4, P5, P6>
            (this, bailOutChecker, callbackFunction, param1, param2, param3, param4, param5, param6))->post();
    }

    //==============================================================================
    /** A dummy bail-out checker that always returns false.
        See the ListenerList notes for more info about bail-out checkers.
    */
    class DummyBailOutChecker
    {
    public:
        inline bool shouldBailOut() const noexcept     { return false; }
    };

    //==============================================================================
    /** Iterates the listeners in a ListenerList. */
    template <class BailOutCheckerType, class ListType>
    class Iterator
    {
    public:
        //==============================================================================
        Iterator (const ListType& listToIterate) noexcept
            : list (listToIterate), index (listToIterate.size())
        {}

        ~Iterator() noexcept {}

        //==============================================================================
        bool next() noexcept
        {
            if (index <= 0)
                return false;

            const int listSize = list.size();

            if (--index < listSize)
                return true;

            index = listSize - 1;
            return index >= 0;
        }

        bool next (const BailOutCheckerType& bailOutChecker) noexcept
        {
            return (! bailOutChecker.shouldBailOut()) && next();
        }

        typename ListType::ListenerType* getListener() const noexcept
        {
            return list.getListeners().getUnchecked (index);
        }

        //==============================================================================
    private:
        const ListType& list;
        int index;

        JUCE_DECLARE_NON_COPYABLE (Iterator)
    };

    const ArrayType& getListeners() const noexcept          { return listeners; }

private:
    //==============================================================================
    typename juce::WeakReference<ThisType>::Master masterReference;
    friend class juce::WeakReference<ThisType>;

    ArrayType listeners;
    juce::CriticalSection listenerLock;

    JUCE_DECLARE_NON_COPYABLE (AsyncListenerList)

    #undef LL_TEMPLATE
    #undef LL_PARAM
};
5 Likes

was this implemented into the ListenerList class?

2 Likes

nops. c++17 will come before this will get in :smiley:

1 Like

That’s exactly what I am looking for at the moment: ListenerList::callAsync !
Is there any chance that this will be added to the ListenerList?

I have a thread that reads and synchronises FFmpeg streams, which provides a VideoListener to receive the frames and to send a presentationTimestampChanged. When I use that callback to set a slider, it triggers a repaint, which throws an assert, because it’s not the message thread.

Thanks so much!

+1 on this. It just makes sense, considering how often listeners are used in JUCE for UI related work on and off the message thread.

1 Like

yes, but i would actually rewrite this macro mess with variadic templates. type safety and (mostly) unlimited numbers of callback arguments with lesser and cleaner lines of code. you need to ask the companies still trying to target old dino-users if we are allowed to do that

4 Likes

probably now with c+11 as minimum for juce this can be done. will see what i can come up with

Calling ListenerList::callAsync() with a parameter of type std::shared_ptr does not work for me, I get “stack use after return” undefined behaviour. How are we supposed to use this function?

Can you show the code, that led to that error?
A shared_ptr is a stack object, that bears the ownership (potentially).
If it is used as argument with a move operator, it cannot be used safely afterwards.
But if you share an example, maybe there is a way around, like creating a stack copy (of the ptr, not the object):

std::shared_ptr<Foo> foo = new Foo();
auto bar = foo;

listeners.callAsync [bar] (Listener& l) {l.callback (bar); };

foo.doSomething();  // would fail, if foo was moved into the lambda

I get the shared_ptr as a reference argument to the function that invokes the listener.

void GraphData::addGraphObject(std::shared_ptr<GraphObject> &object) {
	objects.push_back(object);
	listeners.callAsync(&Listener::graphObjectAdded, object);
}

The signature of Listener::graphObjectAdded is virtual void graphObjectAdded(std::shared_ptr<GraphObject> object) = 0;

I’ve tried passing the std::shared_ptr<GraphObject> into GraphData::addGraphObject by value - that fixed the “stack use after return” issue, but my std::shared_ptr sometimes references a nullptr when the listener receives it - do you know why that could be?

Again, the shared_ptr is an object on the stack with a reference count (static counter). If you pass a reference around, it doesn’t increment the reference count, so the object doesn’t know, somebody holds a reference (i.e. is interested in the object). If the original shared_ptr goes out of scope, the reference count is decremented and if that was the last shared_ptr, the object is deleted. So to be safe with shared_ptr, always capture by value.

I am pretty new to the move operator stuff (well, it wasn’t available until recently), so I don’t know all details about what could have happened there.

Thanks for your help. I ended up using JUCE’s Message system directly to post events async, and that worked for my purposes.