Why are there so few "return" functions with SA:MP?
#1

Something that I've always been intrigued about is why the native SA:MP functions seem to shy away from using a return whenever possible. What I mean by this is that the "get" functions are generally of the type where you must supply a variable as an argument, and then the result is saved to this variable (I forget the name for this type but let's call it a "supplied receiver function".) There seem to be a lot of times where the ease of scripting is impeded upon by being forced to use "supplied receiver functions" where the code could be a lot shorter if we were able to just use a function that directly returns the result, and so I'm just wondering why it's programmed that way.

An example of how code could be shortened if more "return" functions were used:
pawn Code:
new health;
GetPlayerHealth(playerid, health);
SetPlayerHealth(playerid, health + 5);
Simply becomes:
pawn Code:
SetPlayerHealth(playerid, GetPlayerHealth(playerid) + 5));
Whilst it may look more complex, I think that it does look neater as well and I know a lot of others agree.

Why do you think the SA:MP natives have been programmed this way?

Also, I'm not complaining. I do have a function that simply returns the player's name, health, armour, etc, because I find it a lot easier to program with them - I haven't found any uses where I need to store the result to a variable where I couldn't just directly allocate it, and that's why I'm curious.
Reply
#2

Because one function stores the float and the other manipulates. You can easily modify that to suit your need. This, suppose, is just the way PAWN works.

Code:
stock Float:GetHP(playerid) {
new Float:health;
GetPlayerHealth(playerid, health);
return _:health;
}

// Now you can do your simplified action using GetHP.
//Not sure about tagging the Float:
Reply
#3

Good question. I have wondered the proper reason myself.
Reply
#4

Despite what the wiki might say, most functions do also have a return value. Most of them return 1 on success and 0 on failure (i.e. not connected or invalid vehicleid). GetPlayerHealth is no exception. This means that doing something like this (common practice) is redundant;

pawn Code:
if(IsPlayerConnected(target))
{
    GetPlayerHealth(target, health);
}
This could be shortened to
pawn Code:
if(GetPlayerHealth(target, health))
Reply
#5

pawn Code:
stock Float:GetPlayerHealthEx(playerid) {
    new Float:health;
    GetPlayerHealth(playerid, health);
    return health;
}
It wouldn't be possible the other way around, maybe that's a reason. That or it was more efficient to do it with call-by-reference functions.
Reply
#6

This is what I presume is happening inside the SAMP server when I execute GetPlayerHealth:
pawn Code:
stock GetPlayerHealth(playerid, &health)
{
    RequestUpdateFromClient(playerid);
    health = playerHealth[playerid];
}
Surely it would be just as simple to do this:
This is what I presume is happening inside the SAMP server when I execute GetPlayerHealth:
pawn Code:
stock Float:GetPlayerHealth(playerid, &health)
{
    RequestUpdateFromClient(playerid);
    return playerHealth[playerid];
}
This would be a lot more useful because then everything could be in one line.
If you want to store the value:
pawn Code:
pHealth = GetPlayerHealth(playerid);
If you want to use the value:
pawn Code:
format(string, sizeof(string), "Your health is: %f", GetPlayerHealth(playerid));
I just really can't see why it was programmed the other way.

Quote:
Originally Posted by Vince
View Post
Despite what the wiki might say, most functions do also have a return value. Most of them return 1 on success and 0 on failure (i.e. not connected or invalid vehicleid). GetPlayerHealth is no exception. This means that doing something like this (common practice) is redundant;
I know that most functions either return 0 or 1 to indicate the success of the action but they could easily just return -1 in the event of a failure, 0 to indicate that the player is dead and otherwise return their health.
Reply
#7

@ CaveDweller: perhaps that's not what actually happens, even if simplified. I would assume that one line of GetPlayerHealth does not actually trigger a packet exchange between the server and the client, but returns the known health value which is updated when the client's health changes. But alright, enough of that...

The reason might very well be that it is indeed faster. When coding C++, it is wise to pass larger elements, classes, etc. by reference to functions so it won't have to be copied onto the stack.

My tests in PAWN from perhaps a year ago have shown that this:
Code:
cache_get_rowcount(rows);
is faster than:
Code:
rows = cache_get_rowcount();
(with the C++ code changed accordingly)

Perhaps this?
Reply
#8

Most of each client's data is indeed cached in the server. In this case passing-by-reference has little to no impact on the code speed, it's more probable that return value was intended to be used as success/failure value, which is good design decision but in the end not used at all because of unknowledgable scripters. Note about measuring things: I bet you forgot to measure cache misses/hits, context switches, privilege switches etc., it's good idea to measure timings of e.g. two different ini systems or similar, but bad to measure things like you measured @AndreT.
Reply
#9

Quote:
Originally Posted by AndreT
View Post
@ CaveDweller: perhaps that's not what actually happens, even if simplified. I would assume that one line of GetPlayerHealth does not actually trigger a packet exchange between the server and the client, but returns the known health value which is updated when the client's health changes. But alright, enough of that...
Yeah I wasn't sure if it was updated whenever the function was called (which would make sense but could cause strain on the server if you were to index the health of 500 online players at once).
Reply
#10

Quote:
Originally Posted by JoBullet
View Post
Note about measuring things: I bet you forgot to measure cache misses/hits, context switches, privilege switches etc., it's good idea to measure timings of e.g. two different ini systems or similar, but bad to measure things like you measured @AndreT.
Something makes me think that the variables you mentioned aren't very relevant when the difference between two methods is a couple hundred milliseconds per 1000000 calls. It gives you a hint that something is being done differently. And you're right about the necessity of such tests, but I don't see anything bad about it if one has a lot of free time on their hands and a need to explore how something works. Got to admit, after taking a job in the field, I wouldn't do such measurements again.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)