YetAnotherForum
Welcome Guest Search | Active Topics | Log In | Register

Behaviour I don't understand with callbacks
JonHodgson
#1 Posted : Friday, June 24, 2016 8:10:36 PM(UTC)
Rank: Member

Groups: Registered
Joined: 5/19/2016(UTC)
Posts: 15
Location: UK

Thanks: 0 times
Was thanked: 0 time(s) in 0 post(s)
Hi,

So I have a C++ class, of which this is slightly stripped down version
Code:

typedef std::basic_string<SQChar> sq_string_t;

class SQMouseObserver : public IMouseObserver
{
public:
    SQMouseObserver(CFrame* frame, Sqrat::Object sq_callback_object);
    ~SQMouseObserver();
    SQMouseObserver& setOnMouseEntered(const Sqrat::Function sq_callback_onMouseEntered);
    SQMouseObserver& setOnMouseExited(const sq_string_t& sq_callback_onMouseExited);

    void releaseCallbacks();
    virtual void onMouseEntered() override;
    virtual void onMouseExited() override;


private:
    SQMouseObserver(){}
    CFrame* frame_;
    Sqrat::Object sq_callback_object_;
    Sqrat::Function sq_callback_onMouseEntered_;
    Sqrat::Function sq_callback_onMouseExited_;
};

SQMouseObserver::SQMouseObserver(CFrame* frame, Sqrat::Object sq_callback_object)
    : frame_(frame)
    , sq_callback_object_(sq_callback_object)
{
    frame_->registerMouseObserver(this);
}

SQMouseObserver::~SQMouseObserver()
{
    frame_->unregisterMouseObserver(this);
}

SQMouseObserver& SQMouseObserver::setOnMouseEntered(const Sqrat::Function sq_callback_onMouseEntered)
{
    sq_callback_onMouseEntered_ = sq_callback_onMouseEntered;
    return *this;
}

SQMouseObserver& SQMouseObserver::setOnMouseExited(const sq_string_t& sq_callback_onMouseExited)
{
    sq_callback_onMouseExited_ = Sqrat::Function(sq_callback_object_, sq_callback_onMouseExited.c_str());
    return *this;
}

void SQMouseObserver::releaseCallbacks()
{
    sq_callback_onMouseEntered_ = Sqrat::Function();
}

void SQMouseObserver::onMouseEntered()
{
    current_view_ = view;
    if (!sq_callback_onMouseEntered_.IsNull())
    {
        sq_callback_onMouseEntered_.Execute();
    }
}

void SQMouseObserver::onMouseExited()
{
    current_view_ = view;
    if (!sq_callback_onMouseExited_.IsNull())
    {
        sq_callback_onMouseExited_.Execute();
    }
}

Now the eagle eyed amongst you will have spotted that I'm passing the callbacks in two different ways, that's because I was testing that one of them worked when I encountered this issue.

Now I bind said class to squirrel, using Sqrat, and have a script which is along these lines (I've stripped out what isn't important to this issue
Code:

class ViewHandler {
    function onViewAttached()
    {
        mouse_observer_ = VstGui.MouseObserver(view_.getFrame(), this);
        mouse_observer_.setOnMouseEntered(onMouseEntered); // #1 This causes the behaviour that I don't understand
        mouse_observer_.setOnMouseExited("onMouseExited"); // #2
    }   

    function onViewRemoved()
    {
        mouse_observer_.releaseCallbacks(); // necessary if I've set OnMouseEntered, but not if I've only set OnMouseExited
        mouse_observer_ = null  // Mouse Observer MUST be destroyed at this point
    }


    function onMouseEntered()
    {
        print ("Mouse Entered\n");
    }

    function onMouseExitedControl()
    {
        print ("Mouse Exited\n");
    }

    mouse_observer_ = null;

}

Now this issue came up because I need the mouse observer destroyed at a known time (so it deregisters itself), now here is what I don't understand

Whether I use the callback assignment style marked #1 or #2 makes no apparent difference to the callback functionality, but it does make a difference to the life of the mouse_observer_ object.

If I use #2 form, then things are as I expect, I can comment out the releaseCallbacks call in onViewRemoved and when mouse_observer_ is set to null, the object is detroyed, its destructor is called, it unregisters itself, and all is hunky dory.

But if I use the #1 style call to set a callback, then setting mouse_observer_ to null DOES NOT destroy the object. UNLESS I first call releaseCallbacks.

How come?
JonHodgson
#2 Posted : Tuesday, June 28, 2016 10:22:05 AM(UTC)
Rank: Member

Groups: Registered
Joined: 5/19/2016(UTC)
Posts: 15
Location: UK

Thanks: 0 times
Was thanked: 0 time(s) in 0 post(s)
Well I've been looking into things a bit more, and although I haven't yet found why passing the callback as a function affects the lifetime of the MouseObserver object, I've also found something else that is unintutitive, at least to me.

If I pass the callback as a function,

be it
Code:
mouse_observer_.setOnMouseEntered(onMouseEntered)


or
Code:
mouse_observer_.setOnMouseEntered(this.onMouseEntered)


the callback that is stored is teh the member of the class, not the object. So if you call it, none of the instance values will be available.

It seems I have to pass the "this" object seperately and create an Sqrat Function in C++.

I haven't yet found a neater way to do this.

Anybody got any input?
absence
#3 Posted : Wednesday, June 29, 2016 12:22:23 PM(UTC)
Rank: Advanced Member

Groups: Registered
Joined: 8/23/2014(UTC)
Posts: 107
Man
Location: Northern Germany & Lincolnshire, U.K.

Thanks: 1 times
Was thanked: 10 time(s) in 10 post(s)
JonHodgson wrote:
Well I've been looking into things a bit more, and although I haven't yet found why passing the callback as a function affects the lifetime of the MouseObserver object, I've also found something else that is unintutitive, at least to me.

If I pass the callback as a function,

be it
Code:
mouse_observer_.setOnMouseEntered(onMouseEntered)


or
Code:
mouse_observer_.setOnMouseEntered(this.onMouseEntered)


the callback that is stored is teh the member of the class, not the object. So if you call it, none of the instance values will be available.

It seems I have to pass the "this" object seperately and create an Sqrat Function in C++.

I haven't yet found a neater way to do this.

Anybody got any input?



Well, I don't know SQRat, so I can't help much with the lifetime issue. I can only GUESS that there is a reference somewhere inside SQRat you're missing (see sq_addref and sq_release) or even inside the VM somewhere for whatever reason.

The issue about the this-object, though, is pretty normal. You always have to pass an environment/this-object when doing a call - for EACH AND EVERY call, to be exact. Usually in most examples, this is done by sq_pushroottable.
But you can push ANY object as "this" for a call. If you pass around closures (function objects), however, you pass the plain closure itself without any this-object (which one is to use then? The one of the caller who passes it, the one of the class instance who takes it? It's choice of the programmer of course. Note again the "this" object is a matter of doing the call rather than passing a closure).

However, you can set a delegate on a closure using pure squirrel code, which probably is what you need.






JonHodgson
#4 Posted : Saturday, July 2, 2016 11:45:57 AM(UTC)
Rank: Member

Groups: Registered
Joined: 5/19/2016(UTC)
Posts: 15
Location: UK

Thanks: 0 times
Was thanked: 0 time(s) in 0 post(s)
absence wrote:
The issue about the this-object, though, is pretty normal. You always have to pass an environment/this-object when doing a call - for EACH AND EVERY call, to be exact. Usually in most examples, this is done by sq_pushroottable.
But you can push ANY object as "this" for a call. If you pass around closures (function objects), however, you pass the plain closure itself without any this-object (which one is to use then? The one of the caller who passes it, the one of the class instance who takes it? It's choice of the programmer of course. Note again the "this" object is a matter of doing the call rather than passing a closure).

I understand that the environment has to be passed, and one is, but it appears to be the class that is passed, not the actual object that the call is being made from. To me that seems a little illogical.
Quote:

However, you can set a delegate on a closure using pure squirrel code, which probably is what you need.

Pardon? I'm pretty new to Squirrel, could you please elaborate
absence
#5 Posted : Friday, July 8, 2016 11:17:40 PM(UTC)
Rank: Advanced Member

Groups: Registered
Joined: 8/23/2014(UTC)
Posts: 107
Man
Location: Northern Germany & Lincolnshire, U.K.

Thanks: 1 times
Was thanked: 10 time(s) in 10 post(s)
sorry, my bad. I remembered using delegates to solve such problem, but you actually can't set delegates on closures. Unfortunately right now I don't have the time to dig into this.
However, you are in control of the call, so it shouldn't be much of an issue.

Quote:
I understand that the environment has to be passed, and one is, but it appears to be the class that is passed, not the actual object that the call is being made from. To me that seems a little illogical.


Not really, it's the same as in C++. Any class function should have the class instance as "this" pointer (in fact, it MUST have it to be able to access its own members at all).

In contrast, when you call a function outside of a class, it's actually in a table, so the this pointer is expected to be that table (though there are some limitations to that).

if you want to know about the caller in a method or function, you simply should pass it's object as parameter, for example

local a=CallAFunction(this) ; //when calling from inside a class instance

or

local a=CallAfunction(callee()) ; //when calling outside a class instance. Callee is the CURRENTLY running closure object.



In case of C++ doing a call, it's job of the C++ code to pass a proper "this". It looks like SQRat correctly passes a class isntance "this" pointer here. Okay, this is "negotiable" when it comes to callbacks :P
Users browsing this topic
Guest (2)
Forum Jump  
You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.

Clean Slate theme by Jaben Cargman (Tiny Gecko)
Powered by YAF 1.9.4 | YAF © 2003-2010, Yet Another Forum.NET
This page was generated in 0.213 seconds.