2019-02-21 08:06:46

Hi,
I'm stuck on the idea of handles with BGT. At the moment, I develop mainly in Java, and I'm used to passing objects directly to functions. For instance, I can do this with an array:
Person person = new Person();
Person[] personArray = new Person[1];
personArray[0] = person;
In BGT, that would look something like this:
Person person;
Person[]@ personArray = Person[1];
@personArray[0] = @person;
I'm not sure if my BGT was 100 percent right but the idea is there. How do the @ signs work, when do you use them, and are handles basically references? Why can you not pass the object like this:
Person[] personArray = Person[1];
personArray[0] = person;
Thank you.

2019-02-21 12:20:14

Hi,

you can do so too, but this will internally create copies of the data you move around. When coming from the Java world, you most probably won't get in touch with the call by reference and call by value patterns.
Call by value means that whenever you assign a value to a different variable or call a function with a given parameter which isn't a handle, this data will be copied. Take this one for example:

string teststring="this is a test";

void testfunc(string text)
{
  // we will append some data here to demonstrate the difference
  text = text + " in bgt";
  alert("text", text);
  alert("teststring", teststring);
}

void main()
{
  testfunc(teststring);
}

What you will notice is the following:
* the first alert, which prints the text variable, will show "this is a test in bgt", because we appended the " in bgt" part.
* the second alert however will still print "this is a test", because we called by value here, means, the content of teststring got copied to a new variable which then got passed to testfunc(), means we can do whatever changes we like to text within testfunc and the original variable teststring won't know anything about it.

Call by reference however passes only the handle to that variable into testfunc, which results in two things:
* less time consuming function calls, since in-memory copies still are time-consuming operations
* the variable can be modified from within the called function. Lets adopt the above example to make things clear.

string teststring="this is a test";

void testfunc(string@ text)
{
  // we will append some data here to demonstrate the difference
  text = text + " in bgt";
  alert("text", text);
  alert("teststring", teststring);
}

void main()
{
  // note that you don't need to use the @ sign in the two lines below, bgt will do this for you already
  testfunc(@teststring);
  testfunc(@teststring);
}

What you will notice here is the following:
* the first call to testfunc() will end up alerting two things, and two times the same thing: this is a test in bgt. Thats because the editing of text ended up editing teststring as well, because we passed in a reference here, not a value.
* the second call to testfunc will also print the same thing: this is a test in bgt in bgt, because we again appended stuff to the text variable which is just another name for teststring.

Conclusion:
call by reference can be performed in a given time, no matter the data you are actually handling, since the operation of receiving a handle to a given variable is performed in constant time, whereas copying large arrays to calling a function by value or copying the array between variables can get pretty time consuming. when copying arrays with objects in them you can also run into the problem of shallow copies, meaning that the array itself is actually a copy, but the objects aren't. This can lead to trouble when you're believing that you can freely edit your objects without editing the objects stored separately, but in fact edit both of them at once. Understanding how handles work can therefore improve your programming skills and also bring some significant performance boosts to the programs you're developing, and since handles (or pointers) aren't just important when developing in bgt, but also in C/C++ and other languages, its recommended to get used to them, even if newer high-level languages usually don't require to know about them, since they will do all the stuff for you in the background.

Hope that helped.

Best REgards.
Hijacker
PS: I didn't test the code snippets above and thus don't guarantee them to be working.

2019-02-21 15:47:43

Are handles supported for strings in BGT? It's kinda weird about handles for things like vectors.
@1: Handles make more sense if you have experience with C / C++ / C#. Except they do some confusing things at times, even then, compared to their low-level counterparts. Handles are kinda like pointers, which are references to the memory address where the variable in question is stored. At the lowest level, you're dealing with how things are handled in the CPU and RAM, except CPUs, afaik, don't have a way to deal with object-oriented programming on a hardware level, and what's being passed around are values in CPU registers. Unless they're wrapped in a high-level construct, arrays are basically a pointer to the first entry, and saying x[y] is equivalent to a pointer to x+(y×sizeof(&x)).
Naturally, that can get messy real quick with complex programs, and Java simplifies it by making everything function as a handle/pointer/reference. This means you need methods for cloning objects (ex: System.arraycopy), which normally isn't that big a deal. For some reason, Angelscript (in which BGT is built) goes with something in between but closer to C#.
So anything done in BGT without an @ is trying to act on the data directly. To make matters more confusing, if you call a function like myfunc(x), it works regardless of whether the function takes a handle or its referent.
Generally speaking, if you want it to work the way it does in Java, you'll be using lots of @s. And that's usually what you want.
BGT doesn't let @ work on ints, floats, etc, which I believe is how it works in Java, too? But if you declare a function or method that takes "int &out" instead of just "int", that's effectively the same as sending a reference rather than a copy. ... But you don't do anything different when modifying the variable, because that would make sense -_-

Generally, you want to use handles in these situations:

  • Comparing class instances. If (@a == @b), if (@x == @this). This is the same as Java if (a == b) and if (x == this).

  • When a function or method needs to permanently modify the objects passed to it. If you wanted to write an implementation of quicksort in BGT, for instance, your parameters would be declared as int[]@, rather than just int[]. Same holds if it's a different type of array.

  • Assigning. "@a = b" makes it so that a and b reference the same instance. "a = b" attempts to clone b to a, which will usually break on an already-initialized object without an opAssign method. You're probably wondering why it's "@a = b", and not "@a = @b", and I have no idea.

  • Dictionaries. BGT is sufficiently weird about this that I try to always have functions to simplify it. If you want to add a sound to a dictionary, you'd do something like "dict.add(key, @handle)", and to get something from a dictionary, you need to have a handle to assign it to: "dict.get(key, @handle)".

  • Factory functions should generally return handles. "sound@ get_sound(string filename)", for instance.

  • Object arrays, especially as members of another class. This shows up a lot in games (ex, a class for a map, level, etc, with an array of items or enemies). "enemy@[] enemies". This gets confusing, because you can say "enemy@[]", "enemy[]@", "enemy@[]@", or "enemy[]", and they all mean slightly different things. If the @ is after the brackets, it's a handle to the array. If it's before the brackets, it's an array of handles. I generally use arrays of handles more than handles to arrays, and handles to arrays of handles is probably useful but hasn't come up much.

  • Remember that null is already treated like a handle, so you should never say "@null", but should always say "@a = null" when nullifying a reference. I think "a=null" would cause a null pointer error.

As a general rule, when in doubt, use handles. Also, order of operations can be a jerk and require you use parentheses to avoid ambiguity regarding to what the @ refers, but I haven't worked out the exact order and just use parentheses for anything with a dot or brackets, just to be safe.

看過來!
"If you want utopia but reality gives you Lovecraft, you don't give up, you carve your utopia out of the corpses of dead gods."
MaxAngor wrote:
    George... Don't do that.

2019-02-21 17:18:52

CAE_Jones wrote:

Are handles supported for strings in BGT? It's kinda weird about handles for things like vectors.

Not sure here, I just used it to clarify things, didn't test it though and creating copies of strings doesn't take too long usually, don't use it anywhere as far as I remember.

CAE_Jones wrote:

BGT doesn't let @ work on ints, floats, etc, which I believe is how it works in Java, too? But if you declare a function or method that takes "int &out" instead of just "int", that's effectively the same as sending a reference rather than a copy. ... But you don't do anything different when modifying the variable, because that would make sense -_-

Thats not all that needs to be said here. AngelScript knows three different ways of handling function parameters:

  • &in: only used for input references. Modifying the variable given with that parameter will either raise a compilation error or simply do nothing (don't know right now). I guess using that one isn't very useful, at least I never did that (it probably doesn't matter if you pass the handle or the variable here, since we're talking about primitive data types like int and float).

  • &out: only used to return a value to the outer world, the variable passed into the function won't be initialized and you can't read from it.

  • &inout (or simply &) does both and thus is a full call by reference call, you can read and also write to this variable.

CAE_Jones wrote:

You're probably wondering why it's "@a = b", and not "@a = @b", and I have no idea.

Its again AngelScript (BGT) that takes your hand here. @a = @b works as well, but AngelScript knows that @a can only be set to another handle and thus creates the handle of b automatically to assign it to @a.

Best Regards.

Hijacker

2019-02-21 17:45:37 (edited by Kyleman123 2019-02-21 17:52:46)

Another thing to consider when using pass by value and pass by reference, is that of the nature of the call. Note: all code is in c++. I do not know bgt. I'm assuming that bgt has some equivalent as it is a distant derivative of C, but so apply this to bgt with a bit of salt.

slow and unsafe.
This is equivalent to a normal function call where all of the arguments passed into the function are copied. Any changes made to those variables are not reflected in the variables of the calling function.

#include <iostream>
using namespace std;

void variableTest(int x, int y);

int main()
{
    int x = 1;
    int y = 2;

cout << x << ", " << y << endl;

    variableTest(x, y);

cout << x << ", " << y << endl;

    return 0;
}

int variableTest(int x, int y)
{
    x += y;
    y -= x;

cout << x << ", " << y << endl;
}

slow and safe.
This is adding a const qualifier to the variables you pass into the function. This is basically not use and virtually pointless because if you already are passing by value, the changes you make will not effect the original values. If you want the variables to be read-only however, be my guest.
The following code probably wont work, as i'm trying to modify a constant variable, but that could be useful though if you were trying to minimize access and keeping grubby little fingers where they don't belong.

#include <iostream>
using namespace std;

void variableTest(const int x, const int y);

int main()
{
    int x = 1;
    int y = 2;

cout << x << ", " << y << endl;

    variableTest(x, y);

cout << x << ", " << y << endl;

    return 0;
}

int variableTest(const int x, const int y)
{
    x += y;
    y -= x;

cout << x << ", " << y << endl;
}

fast and unsafe
This is the most dangerous of the 4 options. If you pass by reference, the function can modify anything it wants to as it pertains to your variables. It is fast though. A pass by reference is basically pass by pointer. It copies 4 or 8 bytes (the size of a memory address depending on your operating system) instead of passing the class instance or object you have defined that is a  dozen or more bytes. copying dozens and dozens of bytes just to make a function call will slow your program down to a crawl.

#include <iostream>
using namespace std;

void variableTest(int & x, int & y);

int main()
{
    int x = 1;
    int y = 2;

cout << x << ", " << y << endl;

    variableTest(x, y);

cout << x << ", " << y << endl;

    return 0;
}

int variableTest(int ^ x, int ^ y)
{
    x += y;
    y -= x;

cout << x << ", " << y << endl;
}

fast and safe.
The best of both worlds fast and safe is pass by constant reference. If you are still with me you can probably guess what this means. We get the speed and efficiency of pass by reference, but the function can not modify the variables. This is especially useful for operating system / API calls where you are getting data from the user or application program. You as the system level software or operating system have no right to modify the data you've been given. plus if they have given you a struct of of employee data that has several variables like name, id, and address, you would much rather be shuffling around pointers and not big bulky employee structs.

#include <iostream>
using namespace std;

void variableTest(const int x, const int y);

int main()
{
    int x = 1;
    int y = 2;

cout << x << ", " << y << endl;

    variableTest(x, y);

cout << x << ", " << y << endl;

    return 0;
}

int variableTest(const int x, const int y)
{
    x += y;
    y -= x;

cout << x << ", " << y << endl;
}
I don’t believe in fighting unnecessarily.  But if something is worth fighting for, then its always a fight worth winning.
check me out on Twitter and on GitHub

2019-02-21 22:37:14 (edited by ricardo0922 2019-02-22 01:08:21)

Thank you to all for the information on pointers, passing by value versus reference, and everything in between. I just want to address one topic, that being creating copies of arrays.
In java, we would write a for-loop that would iterate over both arrays and copy each element over to the other array. However, if we have an array of handles in bgt,, how would this be done? It seems that copying the handles to this new array would be pointless since the handles in the new array would still be referencing to the original object, since there would be two handles pointing to the same place. So, how would this work if you want an indipendent copy of the array?  Or am I totally misunderstanding? Thanks!