Jump to content
Sign in to follow this  
afp

Scripting question - passing parameters

Recommended Posts

Is there a way to pass a variable to a subscript by reference, I mean the subscript being able to alter the parameter?

I don't want to use a global variable since the script is called multiple times.

Share this post


Link to post
Share on other sites

If you pass an array, it will be passed by reference.

Just make sure not to overwrite the array and to use 'set' for modifications.

But if you don't need to have them run parallel, you simple get the result of the subscript.

Share this post


Link to post
Share on other sites

Indeed, I prefere them in paralel plus there are multiple parameters. I figured out in the end what I did wrong, I performed the following test and first time the result was "3".

The problem was that even execVM doesn't wait for the calling function to end. If I enable that "sleep", then the result is 8... Thank you, all clear now.

"caller.sqf":

_myArray = [3, 3];

null = [_myArray] execVM "subscript.sqf";

// sleep 3;

Player sideChat format["%1", _myArray select 1];

"subscript.sqf":

_array = _this select 0;

_array set [1, 8];

Share this post


Link to post
Share on other sites

Take a look at call, spawn and execVM and their differences.

Share this post


Link to post
Share on other sites

One more question, when modifying a global array from a spawned script and from main caller (I have two loops), is there any kind of internal protection mechanism (critical section, mutex etc) provided for globals or you need to avoid this with your own code? Like a gobal variable switching to "busy" state or something?

Also, if array is growing inside the called script, a copy of it will be created so you cannot recover it in the caller...

Edited by afp

Share this post


Link to post
Share on other sites

"caller.sqf":

_myArray = [3, 3];

null = [_myArray] execVM "subscript.sqf";

sleep 3;

Player sideChat format["%1", _myArray select 1];

"subscript.sqf":

_array = _this select 0;

_array = _array +[8]; // <<<<<< here

after this, the array in caller is still [3,3], not [3,3,8]. Kinda weird behaviour somehow, I would expect pointing to the same array. Thanks for the blog entry, all clear.

EDIT:

I was using something like this:

script1.sqf:

while (true)

{

sleep 3;

waitUntil (!ArrayProtected);

ArrayProtected = true;

modify GlobalArray;

ArrayProtected = false;

}

script2.sqf:

while (true)

{

sleep 3;

waitUntil (!ArrayProtected);

ArrayProtected = true;

modify GlobalArray;

ArrayProtected = false;

}

After your article I just realise I can get some weird things if the one script suspends after checking condition. :)

Edited by afp

Share this post


Link to post
Share on other sites
Is there a way to pass a variable to a subscript by reference, I mean the subscript being able to alter the parameter?

I don't want to use a global variable since the script is called multiple times.

You really need to learn about the difference between virtual machines and functions, resp. execVM and call.

After that, you might wanna learn something about scope, for...

There is another strategy you might pursue, that doesn't involve the variable to be altered beeing passed as a parameter at all, but a function that bubbles up in scope to access it.

This strategy is particularly nice for private functions, since everything is still nicely encapsulated. Though you can "abuse" this technique.

Pseudo-code with a private function:

// let this be the contents of a sqf file.

private ["_objects", "_valueA", "_valueB", "_results"];

_objects = _this select 0;
_valueA = _this select 1;
_valueB = _this select 2;
_results = [];

private ["_fun"];

_fun = {
  // magic
  _results = _results + [
     ((_this * _valueA) + _valueB)
  ];

  true
};

{
  if (someCondition) then
  {
     _valueA = _valueA * 1.2;
     _valueB = _valueB * 0.5;
  };
  if (someOtherCondition) then
  {
     // we may even alter _fun on the go!
     _fun = {
        _results = _results + [0];
     };
  };

  (_x select 2) call _fun;
} forEach _objects;

// return results
_results

Now note how we only pass some value from the current object to _fun, while _valueA, _valueB and _results aren't defined at all in the scope of _fun.

Since there is no statement like private ["_valueA", "_valueB", "_results"]; in _fun, and these values aren't defined in the scope of _fun, we bubble up from the private function _fun into the scope of the sqf, where _valueA, _valueB and _results are defined.

Let's assume we would look for _valueC in _fun: 1) there is no such thing defined in _fun, and it isn't private either, thus 2) let's bubble up to the scope of the sqf, where again we finde this variable neither defined, nor private. Thus 3) let's bubble to the scope of the caller of the sqf... and so on...

Since we're bubbling upwards in scope, there is no need to pass any pointer. You can access any variables in a higher scope directly. And aslong as you carefully consider to what scope a variable should be private (thus stoping the bubbling/search in higher scopes), everything is still safe/robust too, e.g. we do not run into the risk of accidentally overriding some variable in higher scope (consider _valueC in the example above).

Now consider the next example:

// let this be the contents of a fun.sqf, compiled to FUN

private ["_a", "_b"];

_a = _this select 0;
_b = _this select 1;

_score = _score + ((_a * _a) / _b);

// we do not return anything

and

// let this be the contents of mainLoop.sqf, run per execVM
private ["_score"];

_score = 0;

while {true} do
{
  // some magic
  [someValue, someOtherValue] call FUN;
  sleep 10;
};

What we do here should be quit clear by now. there is no _score defined in FUN, neither is it declared private, thus we look in the scope of the caller which is mainLoop.sqf, where we manipulate _score. The declaration of private ["_score"] in here doesn't make much sense, since this is run in a virtual machine where we can't bubble up any further (hm, if I'm not mistaken.. not 100% sure about that.), except for globals of course.

The reason I came up with two examples is this: there is an important difference between the two examples. While the first example is considered "good" style, for it's a secure blackbox with well defined in and outputs and no side-effects, the second example is clearly dangerous. You can't call FUN from everywhere, the function has become fragile.

Also you have to hardcode the name of the variable from the upper scope... pretty ugly.

And yeah, only array's are passed by reference, so if you need to pass anything other by reference, just pack it into an array and pass this along...

Edited by ruebe
pseudo code bug, lol

Share this post


Link to post
Share on other sites

I knew about inline functions, didn't know about redefinition on the fly which it's really interesting. Also I found out that you can define them in external files, they will use variables from the caller scope just like in your sample, this is nice too, but as you said, unsafe.

Everything started while trying to do some AI calculations on a separate thread so I can control the exact timining, since that was the heavy processing and let other things in main. It was difficult because if I used "spawn", I cannot pass the variables like above.

I also found in some script but I cannot find them again, some mambo-jambos with some kind of dynamic code like function pointers, I guess easy to simulate since "code" is a data type in BIS scripting. They were packing functions in some strings or something and called them later.... not sure as it was at the time when I didn't look closely to it.

If you know what am I talking about, that would be interesting to discuss too, or I try to find it out again. Thank you very much, cheers.

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
Sign in to follow this  

×