|
-
Mar 29th, 2014, 11:18 AM
#1
Can a Function return an Array
I have several functions that need to return an Arrays.
Can this be done without having the array as a ByRef argument in the argument list
'How I do it now:
Code:
Private Sub SomeSub()
'
'
Dim MyArray(10) As Integer
UpdateArray MyArray
'
'
End Sub
'
'
'
Private Function UpdateArray(ByRef CallersArray() As Integer) As Integer
'
'
'
End Function
Something how I would like to do it:
Code:
Private Sub SomeSub()
'
'
Dim MyArray(10) As Integer
MyArray = UpdateArray()
'
'
End Sub
'
'
'
Private Function UpdateArray() As Integer()
'
'
'
End Function
Anything I post is an example only and is not intended to be the only solution, the total solution nor the final solution to your request nor do I claim that it is. If you find it useful then it is entirely up to you to make whatever changes necessary you feel are adequate for your purposes.
-
Mar 29th, 2014, 11:31 AM
#2
Re: Can a Function return an Array
Of course. This was one of the many improvements VB6 offered over VB5.
Supposedly to do this optimally you create a local array and then as the last step in the procedure assign this array to the procedure name, which avoids a copying operation.
However I can't be sure that "last statement" includes those before an Else or End If, before an Exit Sub fencing off an error handler, etc.
How To Return and Assign Arrays with Visual Basic 6.0
-
Mar 29th, 2014, 12:14 PM
#3
Re: Can a Function return an Array
According to Matt Curland's tip that appeared in the 11th Edition of the VBPJ Technical Tips Supplement:
 Originally Posted by Matt Curland
Avoid Copying Data
You can use the name of a Function or Property Get procedure as a local variable anywhere in the procedure. If your procedure returns a String or UDT type, writing directly to the function name instead of a temporary variable saves you from making a full copy of your data at the end of a function. Unfortunately, you can’t leverage this technique if your function returns an array, because VB interprets any parentheses after the function name as a call to the function, not as an index into the array. The overloaded parentheses force you to use a local variable and make an expensive array copy at the end of the function. However, if the assignment to the function name happens on the statement before an [End|Exit] [Function|Property], then VB simply transfers ownership of the local variable to the function name instead of copying it. Any intervening statements (including End If) preclude the compiler from making this optimization.
On Local Error Resume Next: If Not Empty Is Nothing Then Do While Null: ReDim i(True To False) As Currency: Loop: Else Debug.Assert CCur(CLng(CInt(CBool(False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
Declare Sub CrashVB Lib "msvbvm60" (Optional DontPassMe As Any)
-
Mar 29th, 2014, 02:26 PM
#4
Re: Can a Function return an Array
In many cases you can just use the function name, you just can't use it indexed (as stated).
Nice to know sticking in an Exit Function after assignment takes care of the "last statement" requirement. Now if only I can burn this little rule (#847 of 2713?) into my memory!
-
Mar 29th, 2014, 03:00 PM
#5
Re: Can a Function return an Array
I don't understand this 'sticking in an Exit Function.....' any more than I understand what Bonnie posted... Any intervening statements (including End If) preclude the compiler from making this optimization
The below works
Code:
Private Sub TestSub()
'Array must be dynamic (ie not MyArray(0 To 4))
Dim MyArray() As Integer
Dim c As Integer
Dim n As Integer
MyArray() = UpdateArray()
For n = 0 To 4
c = MyArray(n)
Next n
End Sub
Private Function UpdateArray() As Integer()
Dim Xarray(0 To 4) As Integer
Dim c As Integer
Xarray(0) = 1
Xarray(1) = 2
Xarray(2) = 3
Xarray(3) = 4
Xarray(4) = 5
UpdateArray = Xarray()
C = 1
If c = 1 Then
c = 2
End If
End Function
There is no Exit Function after assignment takes place
Anything I post is an example only and is not intended to be the only solution, the total solution nor the final solution to your request nor do I claim that it is. If you find it useful then it is entirely up to you to make whatever changes necessary you feel are adequate for your purposes.
-
Mar 29th, 2014, 03:49 PM
#6
Re: Can a Function return an Array
 Originally Posted by jmsrickland
I don't understand this 'sticking in an Exit Function.....' any more than I understand what Bonnie posted... Any intervening statements (including End If) preclude the compiler from making this optimization
Maybe the following example helps...
(It's all about avoiding unnecessary copying of already filled-in Contents of the "Inner-Helper-Array" on function-return)
Code:
Option Explicit
Private Sub Form_Load()
Dim MyArray() As Integer 'Array must be dynamic (ie not MyArray(0 To 4))
Debug.Print vbLf; "First test with your slightly changed original approach"
MyArray() = UpdateArray()
Debug.Print "Start Pointer Of the returned Arrays Data-Range: ", VarPtr(MyArray(0))
Debug.Print vbLf; "Second test with an immediately returning function (after assignment)"
MyArray() = UpdateArray2()
Debug.Print "Start Pointer Of the returned Arrays Data-Range: ", VarPtr(MyArray(0))
Debug.Print vbLf; "Third test with an Exit Function (after assignment)"
MyArray() = UpdateArray3()
Debug.Print "Start Pointer Of the returned Arrays Data-Range: ", VarPtr(MyArray(0))
End Sub
Private Function UpdateArray() As Integer()
Dim Xarray() As Integer 'internal Array must be dynamic, to make "non-copying returns" work
ReDim Xarray(0 To 2)
Xarray(0) = 1
Xarray(1) = 2
Xarray(2) = 3
Debug.Print "Start Pointer Of the internal Arrays Data-Range: ", VarPtr(Xarray(0))
UpdateArray = Xarray()
'since you have additional Code, following the assignment above, a copy of XArray will be returned finally
Dim c As Integer
c = 1
If c = 1 Then c = 2
End Function
Private Function UpdateArray2() As Integer()
Dim Xarray() As Integer 'internal Array must be dynamic, to make "non-copying returns" work
ReDim Xarray(0 To 2)
Xarray(0) = 1
Xarray(1) = 2
Xarray(2) = 3
Debug.Print "Start Pointer Of the internal Arrays Data-Range: ", VarPtr(Xarray(0))
UpdateArray2 = Xarray()
'the assignment above is the last instruction, so XArray can be returned "as is"
End Function
Private Function UpdateArray3() As Integer()
Dim Xarray() As Integer 'internal Array must be dynamic, to make "non-copying returns" work
ReDim Xarray(0 To 2)
Xarray(0) = 1
Xarray(1) = 2
Xarray(2) = 3
Debug.Print "Start Pointer Of the internal Arrays Data-Range: ", VarPtr(Xarray(0))
UpdateArray3 = Xarray()
'the assignment above is the last instruction before Exit Function, so XArray can be returned "as is"
Exit Function
SomeLabel:
Dim SomeVar As Long
SomeVar = 1
End Function
Olaf
-
Mar 29th, 2014, 04:13 PM
#7
Re: Can a Function return an Array
Anything I post is an example only and is not intended to be the only solution, the total solution nor the final solution to your request nor do I claim that it is. If you find it useful then it is entirely up to you to make whatever changes necessary you feel are adequate for your purposes.
-
Mar 30th, 2014, 01:01 AM
#8
Re: Can a Function return an Array
Yes, the goal is to take advantage of a special case "exit optimization" for array-typed Functions. When you meet those requirements the function value "takes over" the local array instead of getting new memory with the element values coied over to it.
Of course now I'm wondering if that impacts the compiler optimization option "Assume No Aliasing," but probably not.
-
Mar 30th, 2014, 03:03 AM
#9
Re: Can a Function return an Array
 Originally Posted by dilettante
Yes, the goal is to take advantage of a special case "exit optimization" for array-typed Functions. When you meet those requirements the function value "takes over" the local array instead of getting new memory with the element values coied over to it.
Of course now I'm wondering if that impacts the compiler optimization option "Assume No Aliasing," but probably not.
No, I don't think so - but unwanted effects due to a checked-in "Assume no Aliasing"-option are not easily
predictable - best to test with an example (if you have one in mind, where you think the compiled App would
choke, when the check-mark is set on this special native-compiler-option).
I've discussed this topic some years ago with Ulrich Korndoerfer, who made some good points -
resulting in a few reviewed code-snippets, brought as an example, where a check-in of this
optimization was doing "more harm than speedup" (It's the only option I sometimes leave off,
especially in those native-compiles, where I made use of SafeArray-trickery in my Sources).
In case you're interested - it's a quite lengthy thread - and the "Assume no Aliasing"-discussion starts
in the last third of it - though I hope I have found the correct link below, which should lead directly
to this sub-topic where Ulrich chimed in:
https://groups.google.com/forum/#!ms...c/diXPEKm_SuoJ
Olaf
Last edited by Schmidt; Mar 30th, 2014 at 03:30 AM.
-
Mar 30th, 2014, 03:19 AM
#10
Re: Can a Function return an Array
Thanks, always good to have more reading material for downtime (like waiting for those clients to email back or call).
-
Mar 30th, 2014, 07:28 PM
#11
Re: Can a Function return an Array
I have a couple of questions regarding the Exit/End Function optimization:
1) Does the optimization still occur when you use line numbering? e.g.:
Code:
Public Function ReturnArray() as Byte()
10 On Error Goto ErrorHandler
20 ReturnArray = SomeByteArray()
30 Exit Function
ErrorHandler:
'Do some error handling
Err.Raise X, , "Error Line: " & Erl
End Function
2) Is the optimization limited to functions, or does it still work for Properties? e.g.
Code:
Public Property Get SomeProperty() As Byte()
SomeProperty = SomeArray()
Exit Property
' Do other work
X=X+1 ' X not defined for this example, but assume it has been
End Property
I'd assume that the optimization works for both functions and properties, but I have a vague memory about dilettante (I believe!) complaining about using properties vs. functions, but unfortunately I can't find the thread, and I can't remember if it was a stylistic complaint, or a technical complaint (i.e. properties are handled differently by the compiler).
Last edited by jpbro; Mar 30th, 2014 at 08:37 PM.
-
Mar 31st, 2014, 03:21 AM
#12
Re: Can a Function return an Array
 Originally Posted by jpbro
I have a couple of questions regarding the Exit/End Function optimization:
1) Does the optimization still occur when you use line numbering? e.g.:
Code:
Public Function ReturnArray() as Byte()
10 On Error Goto ErrorHandler
20 ReturnArray = SomeByteArray()
30 Exit Function
ErrorHandler:
'Do some error handling
Err.Raise X, , "Error Line: " & Erl
End Function
No, apparently "only WhiteSpace" is allowed between the assignment and the exit-point.
Below comes some code for that.
 Originally Posted by jpbro
2) Is the optimization limited to functions, or does it still work for Properties? e.g.
Code:
Public Property Get SomeProperty() As Byte()
SomeProperty = SomeArray()
Exit Property
' Do other work
X=X+1 ' X not defined for this example, but assume it has been
End Property
I'd assume that the optimization works for both functions and properties, but I have a vague memory about dilettante (I believe!) complaining about using properties vs. functions, but unfortunately I can't find the thread, and I can't remember if it was a stylistic complaint, or a technical complaint (i.e. properties are handled differently by the compiler).
The optimization works in the same way as for Functions - never found any differences between Functions and
Property Get ... at least "COM-calling-wise" they are treated the same - and thus you can combine in DispatchCalls
for example: SomeResult = CallByName(Obj, "FuncOrPropGetName", vbMethod Or vbGet, ParamList)
The only difference I'm aware of between the two is, that for Properties the Compiler ensures
a "Live-Value-Resolution" if you hover over a PropertyName with your Mouse.
It will not do so for Functions.
Other than that - maybe dilettante has some more infos...
Below is some test-code.
Code:
Option Explicit
Private Sub Form_Load()
Dim ReturnedBytes() As Byte 'Array must be dynamic (ie not MyArray(0 To 4))
Debug.Print vbLf; "Test in a Function with an ErrHandler and a Line-Nr before Exit Function"
ReturnedBytes() = ArrayFunc1()
Debug.Print "Start Pointer Of the returned Arrays Data-Range: ", VarPtr(ReturnedBytes(0))
Debug.Print vbLf; "Test in a Function with an ErrHandler and NO Line-Nr before Exit Function"
ReturnedBytes() = ArrayFunc2()
Debug.Print "Start Pointer Of the returned Arrays Data-Range: ", VarPtr(ReturnedBytes(0))
Debug.Print vbLf; "Test in a Property with an ErrHandler and NO Line-Nr before Exit Property"
ReturnedBytes() = ArrayFunc3()
Debug.Print "Start Pointer Of the returned Arrays Data-Range: ", VarPtr(ReturnedBytes(0))
End Sub
Public Function ArrayFunc1() As Byte()
Dim B() As Byte 'internal Array must be dynamic, to make "non-copying returns" work
10 On Error GoTo ErrorHandler
20 ReDim B(0 To 2)
30 B(0) = 1
40 B(1) = 2
50 B(2) = 3
60 Debug.Print "Start Pointer Of the internal Arrays Data-Range: ", VarPtr(B(0))
70 ArrayFunc1 = B()
80 Exit Function 'here we have a LineNr before Exit Function (Optimization will not be applied)
ErrorHandler:
'Do some error handling
Err.Raise vbObjectError, , "Error Line: " & Erl
End Function
Public Function ArrayFunc2() As Byte()
Dim B() As Byte 'internal Array must be dynamic, to make "non-copying returns" work
10 On Error GoTo ErrorHandler
20 ReDim B(0 To 2)
30 B(0) = 1
40 B(1) = 2
50 B(2) = 3
60 Debug.Print "Start Pointer Of the internal Arrays Data-Range: ", VarPtr(B(0))
70 ArrayFunc2 = B()
Exit Function 'here we have NO LineNr before Exit Function (Optimization will be applied)
ErrorHandler:
'Do some error handling
Err.Raise vbObjectError, , "Error Line: " & Erl
End Function
Public Property Get ArrayFunc3() As Byte()
Dim B() As Byte 'internal Array must be dynamic, to make "non-copying returns" work
10 On Error GoTo ErrorHandler
20 ReDim B(0 To 2)
30 B(0) = 1
40 B(1) = 2
50 B(2) = 3
60 Debug.Print "Start Pointer Of the internal Arrays Data-Range: ", VarPtr(B(0))
70 ArrayFunc3 = B()
Exit Property 'here we have NO LineNr before Exit Property (Optimization will be applied)
ErrorHandler:
'Do some error handling
Err.Raise vbObjectError, , "Error Line: " & Erl
End Property
Olaf
-
Mar 31st, 2014, 08:21 AM
#13
Re: Can a Function return an Array
The only thing I can recall had to do with Functions vs. Properties in persistable classes.
-
Mar 31st, 2014, 09:29 AM
#14
Re: Can a Function return an Array
Hi Olaf,
Thanks for the clarifications, and also for the test code.
Sorry, I should have thought of using VarPtr myself (and therefore answered my own question), but for some reason I got stuck in the belief that I'd have to use a debugger to examine the machine code to know if the optimization was being used (and my knowledge is sadly lacking in that area).
-
Apr 2nd, 2014, 08:51 PM
#15
Re: Can a Function return an Array
I've extended Olaf's demonstration a bit to try some other scenarios, including:
1) A property (or function) with a Variant return value instead of a Byte(). Pleasantly surprised that the optimization is applied.
2) A property (or function) with the Exit Property/Function on the same line as the return value being set (separated by a colon). No surprised, the optimization is applied.
3) A sub with a Byte() array parameter. The optimization is not applied.
4) A sub with a Variant parameter. The optimization is not applied.
Code:
Option Explicit
Private Sub Form_Load()
Dim ReturnedBytes() As Byte 'Array must be dynamic (ie not MyArray(0 To 4))
Debug.Print vbLf; "Test in a Function with an ErrHandler and a Line-Nr before Exit Function"
ReturnedBytes() = ArrayFunc1()
Debug.Print "Start Pointer Of the returned Arrays Data-Range: ", VarPtr(ReturnedBytes(0))
Debug.Print vbLf; "Test in a Function with an ErrHandler and NO Line-Nr before Exit Function"
ReturnedBytes() = ArrayFunc2()
Debug.Print "Start Pointer Of the returned Arrays Data-Range: ", VarPtr(ReturnedBytes(0))
Debug.Print vbLf; "Test in a Property with an ErrHandler and NO Line-Nr before Exit Property"
ReturnedBytes() = ArrayFunc3()
Debug.Print "Start Pointer Of the returned Arrays Data-Range: ", VarPtr(ReturnedBytes(0))
Debug.Print vbLf; "Test in a Property with Variant return value, an ErrHandler and NO Line-Nr before Exit Property"
ReturnedBytes() = ArrayFunc4()
Debug.Print "Start Pointer Of the returned Arrays Data-Range: ", VarPtr(ReturnedBytes(0))
Debug.Print vbLf; "Test in a Property with Exit Property on same line as return value set (colon separated), with a line number."
ReturnedBytes() = ArrayFunc5()
Debug.Print "Start Pointer Of the returned Arrays Data-Range: ", VarPtr(ReturnedBytes(0))
Debug.Print vbLf; "Test of a sub with a byte array parameter"
ArrayFunc6 ReturnedBytes()
Debug.Print "Start Pointer Of the returned Arrays Data-Range: ", VarPtr(ReturnedBytes(0))
Debug.Print vbLf; "Test of a sub with a variant parameter"
ArrayFunc7 ReturnedBytes()
Debug.Print "Start Pointer Of the returned Arrays Data-Range: ", VarPtr(ReturnedBytes(0))
End Sub
Public Function ArrayFunc1() As Byte()
Dim B() As Byte 'internal Array must be dynamic, to make "non-copying returns" work
10 On Error GoTo ErrorHandler
20 ReDim B(0 To 2)
30 B(0) = 1
40 B(1) = 2
50 B(2) = 3
60 Debug.Print "Start Pointer Of the internal Arrays Data-Range: ", VarPtr(B(0))
70 ArrayFunc1 = B()
80 Exit Function 'here we have a LineNr before Exit Function (Optimization will not be applied)
ErrorHandler:
'Do some error handling
Err.Raise vbObjectError, , "Error Line: " & Erl
End Function
Public Function ArrayFunc2() As Byte()
Dim B() As Byte 'internal Array must be dynamic, to make "non-copying returns" work
10 On Error GoTo ErrorHandler
20 ReDim B(0 To 2)
30 B(0) = 1
40 B(1) = 2
50 B(2) = 3
60 Debug.Print "Start Pointer Of the internal Arrays Data-Range: ", VarPtr(B(0))
70 ArrayFunc2 = B()
Exit Function 'here we have NO LineNr before Exit Function (Optimization will be applied)
ErrorHandler:
'Do some error handling
Err.Raise vbObjectError, , "Error Line: " & Erl
End Function
Public Property Get ArrayFunc3() As Byte()
Dim B() As Byte 'internal Array must be dynamic, to make "non-copying returns" work
10 On Error GoTo ErrorHandler
20 ReDim B(0 To 2)
30 B(0) = 1
40 B(1) = 2
50 B(2) = 3
60 Debug.Print "Start Pointer Of the internal Arrays Data-Range: ", VarPtr(B(0))
70 ArrayFunc3 = B()
Exit Property 'here we have NO LineNr before Exit Property (Optimization will be applied)
ErrorHandler:
'Do some error handling
Err.Raise vbObjectError, , "Error Line: " & Erl
End Property
Public Property Get ArrayFunc4() As Variant
Dim B() As Byte 'internal Array must be dynamic, to make "non-copying returns" work
10 On Error GoTo ErrorHandler
20 ReDim B(0 To 2)
30 B(0) = 1
40 B(1) = 2
50 B(2) = 3
60 Debug.Print "Start Pointer Of the internal Arrays Data-Range: ", VarPtr(B(0))
70 ArrayFunc4 = B()
Exit Property 'here we have NO LineNr before Exit Property (Optimization will be applied)
ErrorHandler:
'Do some error handling
Err.Raise vbObjectError, , "Error Line: " & Erl
End Property
Public Property Get ArrayFunc5() As Byte()
Dim B() As Byte 'internal Array must be dynamic, to make "non-copying returns" work
10 On Error GoTo ErrorHandler
20 ReDim B(0 To 2)
30 B(0) = 1
40 B(1) = 2
50 B(2) = 3
60 Debug.Print "Start Pointer Of the internal Arrays Data-Range: ", VarPtr(B(0))
70 ArrayFunc5 = B(): Exit Property
ErrorHandler:
'Do some error handling
Err.Raise vbObjectError, , "Error Line: " & Erl
End Property
Public Sub ArrayFunc6(pa_Array() As Byte)
Dim B() As Byte 'internal Array must be dynamic, to make "non-copying returns" work
10 On Error GoTo ErrorHandler
20 ReDim B(0 To 2)
30 B(0) = 1
40 B(1) = 2
50 B(2) = 3
60 Debug.Print "Start Pointer Of the internal Arrays Data-Range: ", VarPtr(B(0))
70 pa_Array = B()
Exit Sub 'here we have NO LineNr before Exit Function (Optimization will be applied)
ErrorHandler:
'Do some error handling
Err.Raise vbObjectError, , "Error Line: " & Erl
End Sub
Public Sub ArrayFunc7(pa_Array As Variant)
Dim B() As Byte 'internal Array must be dynamic, to make "non-copying returns" work
10 On Error GoTo ErrorHandler
20 ReDim B(0 To 2)
30 B(0) = 1
40 B(1) = 2
50 B(2) = 3
60 Debug.Print "Start Pointer Of the internal Arrays Data-Range: ", VarPtr(B(0))
70 pa_Array = B()
Exit Sub 'here we have NO LineNr before Exit Function (Optimization will be applied)
ErrorHandler:
'Do some error handling
Err.Raise vbObjectError, , "Error Line: " & Erl
End Sub
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|