27 June 2014

Passing default ByRef parameters to a Sub, a flaw?

In my opinion the GFA-BASIC’s Sub procedure type is an anomaly. When possible you should use Procedure instead. Oh, and when you are too lazy to type Procedure than just use its abbreviation Proc. A Procedure takes all arguments by value by default and is completely backwards compatible to previous GFA-BASIC versions.

On the other hand, if you are stubborn enough and want to use Sub anyway, make sure you use ByRef and ByVal explicitly with all parameters. Do not expect the default ByRef passing will work, because it won’t. We discussed this earlier in The Sub-ByRef flaw. This posting ended with the quote “Why this is happening is unclear, but it is easily repaired by using ByRef explicitly in Sub headings.” I was never convinced this was a real bug and I always assumed a reasonable explanation would come up some day. And it did ….

VB is the flaw; ByRef turns into ByVal
Let us consider the default Sub by reference implementation in VB, VBA, and VBScript. An argument is passed by reference only when the caller uses a very specific syntax. Only when the program executes the subroutine without parentheses the sub gets a reference to the actual argument. When the programmer puts the arguments between parentheses the arguments are passed by value. The next sample demonstrates how and when VB(A/Script) executes the default:

' Normal VB call of a Sub
' passing a reference to Hello$
SPrintStr Hello$ Sub SPrintStr(sArg As String)

When VB encounters a call with arguments inside parentheses, the arguments are evaluated first. In other words, before something is put on the stack the expression inside the parentheses is evaluated (executed, calculated, etc) and a local copy of the variable is assigned the outcome of the evaluation. The local copy is passed by reference to the subroutine, which than becomes a by value passing of the original parameter. Using the parentheses-syntax in the next example will pass the address of a local variable to the Sub SPrintStr.

' VB call of a Sub using ()
’ making and passing a copy of Hello$
Sub SPrintStr(sArg As String)

Conclusion VB
When VB calls the subroutine with parentheses the sub gets a reference to a copy of the argument and the default ByRef situation suddenly turns into a ByVal passing. The VB Sub and its implicit, default, by reference passing introduces performance loss and unnecessary memory consumption! Remember this when you port VB code to GB32.

You’ll find the whole story on http://blogs.msdn.com/b/ericlippert/archive/2003/09/15/52996.aspx

GFA-BASIC supports with parentheses
You can almost feel the agony FO must have felt when he found out about this VB quirk. How to maintain backward compatibility to previous GFA-BASIC versions and VB? In GB we are used to place parameters between parentheses. Should we suffer from this VB anomaly as well? Either way, FO decided to support the Sub implementation with parentheses only. Passing arguments without parentheses is equal to passing them with parentheses.

Although the Sub-ByRef problem is much clearer now, the GB32 implementation is still a bit ambiguous. Because GB32 mimics the call with parentheses only the subroutine always receives a by-value argument to a default ByRef parameter. You may omit the parentheses in GB32, but it still executes the version with parentheses. Only when a global variable is passed to a default by reference argument the actual variable address is put on the stack.

Conclusion GB32
Despite the ambiguity between global and local parameters, you should, as a general rule, no longer consider a default by reference parameter as [in/out] when you omit ByRef in a Sub declaration. Inside the Sub you don’t know if you are dealing with the actual variable [in/out] or with a copy [in]. Always use ByVal and ByRef explicitly in a Sub declaration.