VoidableCallbackMessage


#1

Hi!

I'm often faced to the problem, that I want to create CallbackMessages that rely on the existence of an object. Now, it could happen, that this object is destroyed while the CallbackMessage is still waiting to be dispatched. When it finally is dispatched, the pointer to the target object is dangling.

That's why I eventually came up with this solution: A callback message that registers itself to its target class and gets invalidated when the target class is destroyed.

Maybe you find this usefull as well.

header:

class VoidableCallbackMessage;


/** This is the base class for objects that can invalidate a VoidableCallbackMessage

 upon their destruction.

 */

class VoidableCallbackMessageTarget

{

public:

    VoidableCallbackMessageTarget() {};

    ~VoidableCallbackMessageTarget();

    

    void invalidateAllCallbacks();

    

    void attachCallbackMessage(VoidableCallbackMessage* message);

    void removeCallbackMessage(VoidableCallbackMessage* message);

    

private:

    ListenerList<VoidableCallbackMessage> callbacks;

};


/** \brief A callback message for the message thread that can be invalidated.

    

 Often, a callback message relies on an object, which might be deleted before the callback

 message has been dispatched. This base class attaches itself to

 a target object and invalidates itself, when the object is destroyed.

 */

class VoidableCallbackMessage: public CallbackMessage

{

public:

    VoidableCallbackMessage();

    virtual ~VoidableCallbackMessage();

    

    virtual void handleCallback(VoidableCallbackMessageTarget* target_) = 0;

    

    void invalidate();

    bool isValid() { return isvalid; }

    void removeFromTarget();

    void postAttachedToTarget(VoidableCallbackMessageTarget* target_);

    virtual void messageCallback();

    

private:

    VoidableCallbackMessageTarget* target;

    bool isvalid;

};

c file

VoidableCallbackMessageTarget::~VoidableCallbackMessageTarget()

{

    invalidateAllCallbacks();

}


void VoidableCallbackMessageTarget::invalidateAllCallbacks()

{

    callbacks.call(&VoidableCallbackMessage::invalidate);

}


void VoidableCallbackMessageTarget::attachCallbackMessage(VoidableCallbackMessage* message)

{

    if (!callbacks.contains(message))

        callbacks.add(message);

}

void VoidableCallbackMessageTarget::removeCallbackMessage(VoidableCallbackMessage* message)

{

    if (callbacks.contains(message))

        callbacks.remove(message);

}


VoidableCallbackMessage::VoidableCallbackMessage()

{

    target = nullptr;

    isvalid = true;

}


VoidableCallbackMessage::~VoidableCallbackMessage()

{

    removeFromTarget();

}


void VoidableCallbackMessage::removeFromTarget()

{

    if (target != nullptr)

    {

        target->removeCallbackMessage(this);

        target = nullptr;

    }

}


void VoidableCallbackMessage::postAttachedToTarget(VoidableCallbackMessageTarget* target_)

{

    removeFromTarget();

    target = target_;

    target->attachCallbackMessage(this);

    post();

}


void VoidableCallbackMessage::invalidate()

{

    isvalid = false;

    target = nullptr;

}


void VoidableCallbackMessage::messageCallback()

{

    if (!isvalid)

    {

        target = nullptr;

        return;

    }

    

    handleCallback(target);

    removeFromTarget();

}

I was actually pretty suprised to find no Juce class for this. Maybe I missed something?
Maybe this concept (or any other that does the same trick) can be added to juce?


#2

What I normally do for that situation is to give the callback object a WeakReference or Component::SafePointer. Then when it arrives, it can just check whether the pointer is null or not.


#3

std::promise<> and std::future<>, maybe in combination with std::function<> or lambdas are your friends here.