Bunch more SUGGESTIONs

Started by SSH, Mon 31/01/2005 20:54:53

Previous topic - Next topic

SSH

Third-party preprocessor: before AGS runs its compiler, it could run an arbitrary 3rd party tool on a temporary text file containing all the scripts to allow full preprocessing

Parametrisable #define macros

When a sprite is imported, make part of its property box the name of the original import file (or "Clipboard"): this is becuase it is sometime hard to tell sprites apart!

12

RickJ

SSH, hope you don't mind me adding a suggestion to you list.  I've taken this liberty in light of your subject line and your previous suggestion post. 

The array bounds check is very helpful but is annoying when trying to impliment a while loop.   In the example below the while loop is should stop looping when variable i reaches a value of -1 as detected by the first part of the conditional expression.    In this case the second part of the conditional erpression and the value of buf are irrelevant .  However,  the bounds check creates a runtime error, when i reaches -1 because of the second part of the conditional expression.  The workaround requires additional code complexity to achieve the same functionality. 
while ((i>=0)&&(buf==' ')) {
   buf = 0;
   i--;
}

I know I brought something like this up recently and the answer I got was to make the code work better.   I have been working around, since but now with a better understanding of the issue and the experience working around, I am of the opinion that this is really annoying and that it is legitimate for an array index to exceed the bounds of the array in the context of a while conditional expression. 

Would it be reasonable to disable the bounds check on while loop conditional expressions or perhaps all conditional expressions?  Or is there some other way to achieve the same result? 


Quote
When a sprite is imported, make part of its property box the name of the original import file (or "Clipboard"): this is becuase it is sometime hard to tell sprites apart!
... or make the filename into an enum having the same numerical value as the sprite id number.

 

Gilbert

#2
Code: ags

while ((i>=0)&&(buf[i]==' ')) {
Ã,  Ã, buf[i] = 0;
Ã,  Ã, i--;
}


I think what matters is whether it's really safe to access out-of-bound array elements, it certainly depends on how arrays were implemented in the engine. In my opinion, this can be easily worked around (not as complex as you mentioned), I think it's not worth ditching the check just because someone wants to use it in a while loop (and it's not really practical and consistent to make it check everywhere except in while conditions). For example your piece of code can be worked around like:
Code: ags

while (i>=0) {
Ã,  if (buf[i]==' ') {
Ã,  Ã, buf[i] = 0;
Ã,  Ã, i--;
Ã,  } else i=-1; //breaks out whenever it reaches a nonspace character
}

Which I think is quite readible and safe.
Alternatively, maybe if there's an advance option that experienced coders can turn off is a better thing.

Snarky

Quote from: RickJ on Tue 01/02/2005 00:00:34
In the example below the while loop is should stop looping when variable i reaches a value of -1 as detected by the first part of the conditional expression.Ã,  Ã,  In this case the second part of the conditional erpression and the value of buf are irrelevant .Ã,  However,Ã,  the bounds check creates a runtime error, when i reaches -1 because of the second part of the conditional expression.
while ((i>=0)&&(buf==' '))

Isn't the real problem here that AGS evaluates the right-hand side of an "&&" conditional expression even when the left-hand side failed?

RickJ

Quote
Isn't the real problem here that AGS evaluates the right-hand side of an "&&" conditional expression even when the left-hand side failed?
Yeah.  That occured to me as well but I don't think it's practical.  it seems to me that the entire expression needs to be evaluated before you can know for sure if the final result will be true or false.   Although in this case it doesn't seem all that complicated I don't think that's true for all possibilities.

Quote
Quote
The workaround requires additional code complexity to achieve the same functionality.
this can be easily worked around (not as complex as you mentioned)
Gilbot, your workaround adds an if-else structure and several lines of code.  Further, the index i is not preserved in your example.  If it's needed then an additinal variable is required  to break the loop.   At this point it's easy enough for me to live with but it is never the less annoying.   It will likely be troublesome to the uninitiated.  Although I can't say for certain, I suspect it's not difficult to disable the bounds check for while loop (or other) conditional expressions and that what is lacking is a clear definition of what should be done.  If so I think it is at least worth having a short discussion about it.

As far as access safety is concerned, is not the bounds check a relatively new feature?  I thouhgt it was added to aid in locating array index type bugs rather than preventing memory access exceptions?  If access saftey is indeed an issue then the bounds check could perhaps substitute a value in place of the invalid array element and then allow the evaluation to continue instead of aborting? 

SSH:  I'm sorry for hijacking your thread.  I didn't intend to take up so much space here; I thought this annoyance was worth a mention on your list.  I owe you one.  ;)



Kweepa

Quote from: RickJ on Tue 01/02/2005 03:12:36
Quote from: Snarky
Isn't the real problem here that AGS evaluates the right-hand side of an "&&" conditional expression even when the left-hand side failed?
Yeah.Ã,  That occured to me as well but I don't think it's practical.Ã,  it seems to me that the entire expression needs to be evaluated before you can know for sure if the final result will be true or false.Ã,  Ã, Although in this case it doesn't seem all that complicated I don't think that's true for all possibilities.
It's entirely practical - a lot of C code relies on it.
And I think it's going to cause problems, eg this won't work...
Code: ags

if (ptr != NULL && ptr->Func())
{
  // blah
}

Perhaps it's like the mathematical evaluation that goes right to left :=
Still waiting for Purity of the Surf II

Gilbert

#6
Quote from: RickJ on Tue 01/02/2005 03:12:36
Gilbot, your workaround adds an if-else structure and several lines of code.Ã, 
It's nothing in my opinion, and it's worth it, just to ensure stuff works in order.
Quote
Further, the index i is not preserved in your example.Ã,  If it's needed then an additinal variable is requiredÃ,  to break the loop.Ã,  Ã, At this point it's easy enough for me to live with but it is never the less annoying.Ã,  Ã, It will likely be troublesome to the uninitiated.
True. But I think it helps in general, especially for people not quite familiar in programming, to figure out their mistakes.
Quote
Although I can't say for certain, I suspect it's not difficult to disable the bounds check for while loop (or other) conditional expressions and that what is lacking is a clear definition of what should be done.Ã,  If so I think it is at least worth having a short discussion about it.
Why? I think if there're checkings elsewhere, why leave while (and probably a few things like 'if' conditions, etc.) alone? One mistake that people can make is thing like this:

while (buf!='A') i++; //Store the position of first 'A' in buf. Weee!1!

which can obviously cause problems when there's no 'A' in buf, if the bound checking is not applied to while loops it can kill the programmers finding bugs, unless the compiler is REALLY smart that it can analyse the conditions and decide on which while condition should be checked and which not, which is not practical.

Quote
As far as access safety is concerned, is not the bounds check a relatively new feature?Ã,  I thouhgt it was added to aid in locating array index type bugs rather than preventing memory access exceptions?Ã,  If access saftey is indeed an issue then the bounds check could perhaps substitute a value in place of the invalid array element and then allow the evaluation to continue instead of aborting?Ã, 
Well, maybe aborting is a harsh punishment, but checkings DO help finding bugs, a better compromise, maybe, is to abort the game with an error if it's going to assign/modify the value of an off-bound element, but just add to the warning.log (no aborting) if it's just a reference to it.

SSH

#7
This kind of "only evaluate what you NEED to" is called lazy evaluation and almost every compiler I've seen does it. Even dumb scripting langauges like csh... so I'd be all in favour of it in AGS, espeically since we're going to need to check a whole lot more nulls these days. Of course, if you had paramterisable macros, then you could write a macro to insted the extra if/else structure...

Which bring me back to my sugestions: Rick, I'll forgive you as long as you wholeheartedly endorse eveyrhting I suggested at the top of the thread  :=


EDIT:
And here's another suggestion:

Functions with no paramteres don't need the empty brackets when being called. It's not as if they have a separate namespace, so the compiler could work out that they are functions without the brackets, couldn't it?

12

RickJ

SSH:  Hehe,  Ok.  How's this ...

Quote
When a sprite is imported, make part of its property box the name of the original import file (or "Clipboard"): this is becuase it is sometime hard to tell sprites apart!
This is a really great idea!  I would even go further and generate enum values from the file name so that individual sprites could be accessed by name rather than number. 

Quote
Parametrisable #define macros
This is also a good idea, I support it.   

Quote
Third-party preprocessor: before AGS runs its compiler, it could run an arbitrary 3rd party tool on a temporary text file containing all the scripts to allow full preprocessing.
This is the kind of crazy ideas I usually have and I usually don't talk about them because I don't want people to think I'm crazy  *** mad scientist laugh ***.   So I am uncertain if I should support this or not ;).    Having said that, I am crazy enough to use such a thing if it were available.   

SSH, Steve, Snarky:
[quote  author=SSH]
This kind of "only evaluate what you NEED to" is called lazy evaluation and almost every compiler I've seen does it.
Quote
[quote  author=SteveMcCrea]
It's entirely practical - a lot of C code relies on it.
Quote
I stand corrected.  This is probably the reason I expect to be able to itterate through a list using "while (conditional)"  whithout the need for extra machinations.   I think most programmers would have the same expectation regarding this. 

Gilbot:
I think we have a fundamental disagreement about "while ((i>=0)&&(buf==' '))".  I don't agree that there is an error in the conditional expression.  As the others point out , since in the case i=-1,  the the index i does not meet the first test, the second test is not necessary and so it need not access a non-existent array element.  As the others have said, this is the case in nearly every modern day compiler, so one would think that most programmers expect the above example to work.   As the others point out, these sitiuations will only become more frequent in AGS V2.7 and when the man says "pay me now or pay me later"  it's usually better to pay now, IMHO. 






{
   buf = 0;
   i--;
}

===========

Room and Module Templates
Here is one more suggestion.  If the files  exist in the Have the editor commands "New Room" and "New Module"  populate the newly created room or module with the contents of the corresponding template files template.crm, template scm.  The
files could be located in either the AGS install directory or in the game directory.   The game directory would be searched first and if a template was not found there it could then search the GAS directoy.  If a template file is not found then it would do what it does now.   This would allow people to automatically include header and other comments when making a new module or room. 


Scorpiorus

#9
QuoteI have been working around, since but now with a better understanding of the issue and the experience working around, I am of the opinion that this is really annoying and that it is legitimate for an array index to exceed the bounds of the array in the context of a while conditional expression.
I think, while it is legitimate for an index variable to exceed the bounds, it still shouldn't be legitimate to dereference array index, it should return a value that's really in array rather then something outside. Yeah, it's not important for the example you provided but on the other hand there are circumstances when we should really get an error message, for instance:

while ((i>=0) && SomeFunction(buf[ i ])) ...

this then could cause problems when SomeFunction processes quite a random value which can be both the valid and invalid parameter for the function.


QuoteIsn't the real problem here that AGS evaluates the right-hand side of an "&&" conditional expression even when the left-hand side failed?
I think AGS should always evaluate before applying operator. Personally, I always treat && etc operators as functions that need parameters (two in case of &&) and that return a value. Therefore, both parameters (left and right expressions) must be evaluated first before processing. Some sort of a "lazy" evalution is possible, I think, but in my opinion, besides the hassle to implement it could also lead to many hard to track down errors since they can only be traced at run-time.

And the problem is how to implment it, for instance:

if (func1() && func2()) ...

it is not realy possible to tell which one is needed and which is not (if any), without running at least one function (which function to run is also an issue).


EDIT:

Quote
QuoteAs far as access safety is concerned, is not the bounds check a relatively new feature?Ã,  I thouhgt it was added to aid in locating array index type bugs rather than preventing memory access exceptions?Ã,  If access saftey is indeed an issue then the bounds check could perhaps substitute a value in place of the invalid array element and then allow the evaluation to continue instead of aborting?

Well, maybe aborting is a harsh punishment, but checkings DO help finding bugs, a better compromise, maybe, is to abort the game with an error if it's going to assign/modify the value of an off-bound element, but just add to the warning.log (no aborting) if it's just a reference to it.
I belive, the engine should abort in any case really. Developer can't always track down all the possible run-time errors and it would be worse if instead of run-time error message players started experiencing strange behaviour playing the game.

Kweepa

Quote from: Scorpiorus on Tue 01/02/2005 17:57:51
I think AGS should always evaluate before applying operator. Personally, I always treat && etc operators as functions that need parameters (two in case of &&) and that return a value. Therefore, both parameters (left and right expressions) must be evaluated first before processing. Some sort of a "lazy" evalution is possible, I think, but in my opinion, besides the hassle to implement it could also lead to many hard to track down errors since they can only be traced at run-time.

I'm surprised that
(a) we're having this discussion
(b) there's disagreement over the desired behaviour

"Lazy evaluation" (I don't like to call it that as it sounds rather negative - "short-circuit evaluation" is better I think) is not just in every modern compiler, it's written into the specification of languages.

Quote
And the problem is how to implment it, for instance:

if (func1() && func2()) ...

it is not realy possible to tell which one is needed and which is not (if any), without running at least one function (which function to run is also an issue).

Well, of course you have to run at least one function. Which function to run is trivial. They are evaluated left to right until the result is known - with a sequence of &&s, until one is false; with a sequence of ||s, until one is true. You can verify for yourself that that's a sufficient condition. eg
((a && b && c && d) || e || f)
(a || (b && c) || d)

I see your point about hard to track down errors, but in my opinion there will be many more errors introduced by evaluating everything and then going through the logic. But, maybe that's because I've been programming in C for 15 years.
Still waiting for Purity of the Surf II

Pumaman

Firstly, yes this whole issue is because AGS does not do lazy evaluation on && and ||. (while it's called lazy, it's actually more complicated since the compiler would need to check the first side and generate JUMP instructions depending on the result).

And yes, it's something I would like to fix to make it behave in common with C, especially as you point out, statements like this will become more common and should be valid:

if ((ptr != null) && (ptr.ID > 0))

I do not intend to remove the array bounds checking, because it is for memory access safety as well as for catching bugs. With 2.62 you could do this:
int i[1];
i[50000] = 20;
and crash the game.

QuoteRoom and Module Templates
Here is one more suggestion.  If the files  exist in the Have the editor commands "New Room" and "New Module"  populate the newly created room or module with the contents of the corresponding template files template.crm, template scm.

This is already semi-supported -- if you put a room file called "_blank.crm" in your game folder, it will be used as the template for New Room. Bit of an undocumented feature, oops.

QuoteThird-party preprocessor: before AGS runs its compiler, it could run an arbitrary 3rd party tool on a temporary text file containing all the scripts to allow full preprocessing

Do you have a use in mind for this? It sounds a bit of a far fetched feature, to be honest.

QuoteParametrisable #define macros

That would be nice, but I do try to discourage #define usage since it's not type safe and can make bugs hard to track down.

Quote
When a sprite is imported, make part of its property box the name of the original import file (or "Clipboard"): this is becuase it is sometime hard to tell sprites apart!

Effectively what you're asking for is a way to name sprites, which has been requested before. In fact, it's probably this very request:
http://www.adventuregamestudio.co.uk/tracker.php?action=detail&id=227

RickJ

Quote
Firstly, yes this whole issue is because AGS does not do lazy evaluation on && and ||. (while it's called lazy, it's actually more complicated since the compiler would need to check the first side and generate JUMP instructions depending on the result).
Why not just make comparisons with a non-existent entities always return FALSE?  Well maybe there a couple exceptions but couldn't the bounds check do something like the following when encountered in a conditional expression?   If something doesn't exist it can never be equal to, greater than, etc to anything. 
  (buf[-1]==A)  =  FALSE   
  (buf[-1]!=A)  =  FALSE   
  (buf[-1]>A)   =  FALSE         
  (buf[-1]<A)   =  FALSE                       
  (buf[-1]>=A)  =  FALSE
  (buf[-1]<=A)  =  FALSE
  (buf[-1]&&A)  =  FALSE
  (buf[-1]||A)  =  FALSE

I don't know if this mimic's the behaviour of C in all cases but it may be easier to implement than doing all those partial evals and jumps?  It seems like this would work for the examples given thus far though.  Hmmm,  this would also, on ocassion , negate the need to check pointers for null as well?   For example:
instead of this....
   if ((ptr != null) && (ptr.ID > 0))

if (ptr.ID>0) evaluates to FALSE  when ptr==null, you could 
just do this... 
   if (ptr.ID > 0)


Quote
if you put a room file called "_blank.crm" in your game folder,...
Great!! Maybe one of these days you can get around to doing the same for modules.  Thanks...

Anyway CJ, thanks for taking the time to listen to us.   8)



Gilbert

Quote from: SteveMcCrea on Tue 01/02/2005 20:00:57
"Lazy evaluation" (I don't like to call it that as it sounds rather negative - "short-circuit evaluation" is better I think) is not just in every modern compiler, it's written into the specification of languages.
The problem is, it was not written into the specification of AGS. If the behaviour is written clearly, it poses no problem to anyone, but if not, it's better kept safe as not many people using AGS are actual C programmers (I'm not a programmer of any computer languages, so common programming practice doesn't apply to me as I had never learned about that).
And rick's suggestion does not solve the problem. (And it's actually illogical in mathematical sense to treat these conditions as FALSE, they're actually all TRUE, but that's not important, as what we need is ease in programming). The reason is, if you use a single condition like that it can cause hard to track bugs, like:

while (k[-1]=='a'){...

unless it generates an error or a warning to remind the programmer.

So my suggestion is, either:
1) Leave it alone - safe
2) Really implement lazy-evaluations, and DOCUMENTS the behaviour in the manual, like "When using &&, if the left condition is FALSE the right condition will not be evaluated; when using ||, if the left condition is TRUE the right condition will not be evaluated..." or whatever it should be.

So I think we should leave it alone until lazy-evalution is implemented.

RickJ

Quote
(And it's actually illogical in mathematical sense to treat these conditions as FALSE, they're actually all TRUE ...
I believe that from a rigorous mathematical point of view, the result of operations on non-existent entities is undefined.    So when something like a divide by zero happens in a computer program an exception handler is invoked so that something can be done about it.  If the program was making scientific calculations to model the orbit of a space craft it would perhaps just stop and report what happened.  If it were on board the space craft and actually controling the orbit, instead of stopping, it would likely return a sensible value (likely a really big number)  and continue on.     

From a practical and logical point of view I don't see how a non-existent entity could ever be considerd to be equal to an existing entity of any value.  If it don't exist it can't have that value, whatever it is.  In the example you give "while (k[-1]=='a') {...}" the while loop would never execute because k[-1] is not equal to 'a', so I would think the programmer would realize the loop wasn't executing, and then go figure out why. 

IMHO, the best reason to not use my suggestion is that it may not mimick C as closely as one would desire and could therefore cause some confusion or that there is some unforseen, terrible side affect that's a show stopper.  Oh well, I think we should stop talking about this anyway.  CJ knows about the issue now and will most likely do something we all will be satisfied with as he always does. 

Thaks for the intersting converstion Gilbot, I sincerely enjoyed hashing this out with you.    :)


Gilbert

#15
Quote from: RickJ on Wed 02/02/2005 04:02:29
I believe that from a rigorous mathematical point of view, the result of operations on non-existent entities is undefined.Ã,  Ã,  So when something like a divide by zero happens in a computer program an exception handler is invoked so that something can be done about it.Ã,  If the program was making scientific calculations to model the orbit of a space craft it would perhaps just stop and report what happened.Ã,  If it were on board the space craft and actually controling the orbit, instead of stopping, it would likely return a sensible value (likely a really big number)Ã,  and continue on.Ã,  Ã,  Ã, 
Sorry if I sounded harsh, you're right that in mathematical sense things like 5/0 are usually not defined (you can define it), but not with comparisons involving nonexisting quantities, logically they're always TRUE as a statement.
For example the following statement is TRUE (though it may not make sense):
"My tail is 10 metres long."
simply because I have no tail. (For cases like 5/0 it's not a statement).
However, if it's used practically it can be different, the result can be just designed to suit one's need.

Quote
From a practical and logical point of view I don't see how a non-existent entity could ever be considerd to be equal to an existing entity of any value.Ã,  If it don't exist it can't have that value, whatever it is.Ã,  In the example you give "while (k[-1]=='a') {...}" the while loop would never execute because k[-1] is not equal to 'a', so I would think the programmer would realize the loop wasn't executing, and then go figure out why.Ã, 
It's just a simple example to demonstrate the scenario, there're many possibilities, for example, if there's no 'a' in k:
Code: ags

while (!(k[j]=='a')){ //to find the first 'a' WEEE!
Ã,  j++;
}

Of course he could have coded it (k[j]!='a'), but it's just a demostration and you can't prevent someone write that. In my opinion it should produce an error rather than continuing.
Quote
IMHO, the best reason to not use my suggestion is that it may not mimick C as closely as one would desire and could therefore cause some confusion or that there is some unforseen, terrible side affect that's a show stopper.Ã,  Oh well, I think we should stop talking about this anyway.Ã,  CJ knows about the issue now and will most likely do something we all will be satisfied with as he always does.Ã, 
Well agreed, but what I was just going to point out, was "lazy"-evaluation is a much better solution (though it may be hard to implement) than that workaround of setting it to some fixed value. SInce in lazy-evaluation, it'll ignore a condition only when it's not required to be checked.

SSH

#16
Quote from: Pumaman on Tue 01/02/2005 20:37:35
QuoteThird-party preprocessor: before AGS runs its compiler, it could run an arbitrary 3rd party tool on a temporary text file containing all the scripts to allow full preprocessing

Do you have a use in mind for this? It sounds a bit of a far fetched feature, to be honest.

Well, this really is an alterantive to you havign to implement #defines being parameterisable, or having experssions in array bounds: a preprocessor could pull these out and turn them into valid AGS code. Not just on these features but on any syntactic sugar that might be desired by the programmer. The GNU C COmpiler can be run in a way that only its preprocessor runs, and there are standard macor langauges like m4, or someone could write a perl script that did the processing...

Quote
QuoteParametrisable #define macros

That would be nice, but I do try to discourage #define usage since it's not type safe and can make bugs hard to track down.

Well, then, are you going to implement const declarations. The mechanism is nearl ythere already with enums. In fact, can you use an expression in an enum initalisation? i.e. (oops I foregt the syntax)

enum (myconst = 2+3);

Quote
Quote
When a sprite is imported, make part of its property box the name of the original import file (or "Clipboard"): this is becuase it is sometime hard to tell sprites apart!

Effectively what you're asking for is a way to name sprites, which has been requested before. In fact, it's probably this very request:
http://www.adventuregamestudio.co.uk/tracker.php?action=detail&id=227


No, its different: I want the filename, not some string to be added later. Mayeb though the default name could be the filename. The problem is with multiple sprite import is that the import order may not match the order in the file list (in my experience it tends to be reversed) and if you want to re-imporrt one of those sprites, then you need to know which was which.

Forum SUGGESTION:

Why doesn't the post a reply function warn you if you are double-posting?

12

SSH

#17
Edit by strazer: Needed Scorp's post for another thread, so here's his message:

Quote from: SteveMcCrea"Lazy evaluation" (I don't like to call it that as it sounds rather negative - "short-circuit evaluation" is better I think) is not just in every modern compiler, it's written into the specification of languages...
Yeah, and I'm not against the lazy evaluation, but implementing it now (by means of changing how "&&"/"||" work) would mean the old code may not work as expected. It reminds me the situation with the mathematical operators of the same precedence, you have mentioned, that are evaluated right to left. And, to be honest, I've already written plenty of code relying on '&&' behaviour in AGS. := Ah well, I will then probably replace '&&' with '*' or '&'.

Of course, if '&&', '||' will be non-strict operators it must be very clearly stated in the manual, as Gilbert says, because in that case the order in which expressions are written does matter and, without being thought out, may hide mistakes to be revealed at run-time but only under certain conditions. I don't think many people scripting in AGS think about ordering such expressions at the moment.

'if' statements, on the other hand, make it intuitively clear as it all depends on nesting:

if (a) {
    ...
    if (b) {

    }
    ...
}

but...

if (b) {
    ...
    if (a) {

    }
    ...
}

There is, of course, an advange of lazy operators if we are too lazy to set up if-blocks :) (especially in case of simple "(ptr != null && ptr.Func()") but for a complex stuff, nested 'if's would really save the time and efforts while debugging.


p.s. Just wanted to say that I think there is no disagreement about the desired behaviour in general as it all depends on what we are after, writing a certain portion of code.


/end Scorpiorus



Lazy evaluation should only chnage the behaviour of code using side-effect in function that are used in a && or || expression. One possibility is to change the behaviour only if Force OO scripting is set on, or to have ||| and &&& operators that are lazy.

For an example of side-effects, this code wouldn't work the same way any more, but then I'd never write code like this, given that it tends to be illegal in hardware description languages...

int glob;
function myfun (int x) {
  glob =x;
  return x*2;
}

if (a>5 && glob(x) >6) {
  // do something
}
12

Scorpiorus

Yep, those usually are specific functions that are expected to do something else than just return a value to operator, like, for example, WaitKey that returns a value but also updates the AGS engine state or some function that returns a value as well as fills a 'buffer' string var passed in as parameter.
Those are minority, I believe, but still this should be considered to avoid any possible confusion.

Pumaman

Yes, adding lazy evaluation would cause a problem if you called functions that had side effects as part of the "if" clause. However, you shouldn't really do that since it's bad practice, but I can appreciate it could become a problem.

Some sort of option to enable/disable it would be required, I guess.

SMF spam blocked by CleanTalk