Native stack overflow on _get

Started by EK.IceFlake, Feb 28, 2017, 12:18 PM

Previous topic - Next topic

EK.IceFlake

Well... I have this code
CPlayer.Account <-
{

};

dAccount <-
{
    function _set(obj, val)
    {
        print(obj + ":" + val);
    }
    function _get(obj)
    {
        print(obj);
        return null;
    }
}

CPlayer.Account.setdelegate(dAccount);

function onPlayerJoin(player)
{
    player.Account.TestVal = "000";
    print(player.Account.TestVal);
}
which I wrote to try and create an account system.
However, for some reason, this line:
    player.Account.TestVal = "000";
is returning a native stack overflow. I'm using the VCMP squirrel plugin.
It doesn't return the error if I don't have the _get function.

How should I resolve this?

jWeb

#1
First of all. You should stop treating classes like tables. Even though they are tables. They do have some differences. To Alter the members of a class one should use somelcass.newmember(...) or somelcass.rawnewmember(...) with the former being recommended.

And second. Please read the documentation:

_set:
Quoteinvoked when the index idx is not present in the object or in its delegate chain. _set must 'throw null' to notify that a key wasn't found but the there were not runtime errors(clean failure). This allows the program to defferentieate between a runtime error and a 'index not found'.

_get:
Quoteinvoked when the index idx is not present in the object or in its delegate chain. _get must 'throw null' to notify that a key wasn't found but the there were not runtime errors(clean failure). This allows the program to defferentieate between a runtime error and a 'index not found'.

EK.IceFlake

Quote from: jWeb on Feb 28, 2017, 01:11 PMFirst of all. You should stop treating classes like tables. Even though they are tables. They do have some differences. To Alter the members of a class one should use somelcass.newmember(...) or somelcass.rawnewmember(...) with the former being recommended.
I want to intercept it, however, so that if I do, for example, player.Account.Password = "something"; I can update the database with it, and when I do player.Account.Password, I could return the value from the database rather than from the table/class.

jWeb

I was talking about:
CPlayer.Account <-
{

};

EK.IceFlake

Quote from: jWeb on Feb 28, 2017, 01:36 PMI was talking about:
CPlayer.Account <-
{

};
Yes, when I use
1.
CPlayer.Account <-
class
{
};
I can't setdelegate to it
2.
CPlayer.Account <-
class
{
    function _set(obj, val)
    {
        print(obj + ":" + val);
    }
    function _get(obj)
    {
        print(obj);
        return null;
    }
}
It returns a 'trying to set class' error on this line:
    player.Account.TestVal = 0;

jWeb

How much do I have to point this out:

CPlayer.newmember("Account", {});
But you should be weary about a behavior unique to Squirrel here. Which is that table and array members of a class are by all instances of that class. If you don't want that then you should create the member as null and initialize it after the instance was created.

EK.IceFlake

Quote from: jWeb on Feb 28, 2017, 01:45 PMHow much do I have to point this out:

CPlayer.newmember("Account", {});
But you should be weary about a behavior unique to Squirrel here. Which is that table and array members of a class are by all instances of that class. If you don't want that then you should create the member as null and initialize it after the instance was created.
Okay. However, I get the same error. How do I fix that?

DizzasTeR

function _set(obj, val) {
    print(obj + ":" + val);
    dAccount.rawset( obj, val );
}

EK.IceFlake

Quote from: Doom_Kill3R on Feb 28, 2017, 02:56 PMfunction _set(obj, val) {
    print(obj + ":" + val);
    dAccount.rawset( obj, val );
}
It's nothing about _set.
The error is caused by the _get function being present.

DizzasTeR

Why are you returning null in it?

jWeb

class CPlayer
{

}

local delegates = {
    function _get(idx) {
        // If we omit :: it'll call _get("print") on `this` environment
        // therefore entering an infinite recursive function
        ::print(idx);
        return null;
    }

    function _set(idx, val) {
        ::print(idx + ":" + val);
        return idx;
    }
};

CPlayer.newmember("Account", {}.setdelegate(delegates));

local p = CPlayer();

p.Account.test = "1";

EK.IceFlake

#11
Now I'm facing a huge problem which I think can't be solved.
The this environment is player.Account instead of player. I thought it should be player as I'm using a table and not a class. I also can't find this anywhere in the documentation. How do I get the player environment? I tried a stack trace using getstackinfos(); but for that to work I need to know the name of the players variable.

jWeb

Why would it be the player if the meta-table is bound to the Account table? The environment is not something which is inherited.

EK.IceFlake

Quote from: jWeb on Feb 28, 2017, 05:23 PMWhy would it be the player if the meta-table is bound to the Account table? The environment is not something which is inherited.
Well... how do I get the player environment? I'm guessing it's impossible, but I just want to confirm.
p.s. SLC @happymint was right in the fact that the official plugin is an incredibly s@#t implementation.

.

#14
With some serious workarounds this is achievable. Not pretty but achievable.

class CPlayer
{
    value = null;
    function constructor(v) { value = v; }
}

// We use this approach so that squirrel can see and use
//  the function names in debug messages even after we clone them.
AccountDelegates <- {
    function _get(idx) {
        ::print(this.value);
        return null;
    }
    function _set(idx, val) {
        ::print(this.value);
        return idx;
    }
};

CPlayer.newmember("Account", null);
// Delegates are referenced with a weak reference.
// Therefore, they must be referenced somewhere with a strong reference.
CPlayer.newmember("AccountDelegates", null);

// Construct an instance
local p = CPlayer(23);

// Forcefully bind a predefined environment
p.AccountDelegates = {
    _get = AccountDelegates._get.bindenv(p),
    _set = AccountDelegates._set.bindenv(p),
};
// Finally, create the table with our delegates
p.Account = {}.setdelegate(p.AccountDelegates);

p.Account.test = "1";
.