HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Functional Framework

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
functionalframeworkstackoverflow

Problem

First Class Functions

So, VBA doesn't support functions as first class objects - no passing functions as arguments or storing them in variables.

I eventually found a way to implement function pointers using AddressOf and DispCallFunc, but it was rather dangerous - if you pass the wrong number or type of parameters, you risk hanging/crashing VBA. To get around the problem of types, I made everything a ByRef Variant - any parameters provided can be converted to Variants for passing and then cast back to whatever types they should be inside the function. However, passing incorrect numbers of parameters was still a huge problem because there is no way to detect it, and as soon as DispCallFunc tries to dereference a null pointer, VBA goes BOOM.

Since I wasn't going to have type safety anyway (at least not natively), I started wondering how I could implement function pointers through classes and interfaces. VBA has a parameter keyword "ParamArray" that allows you to define variadic functions.

I created an interface, IFunction, to encapsulate this behavior.

IFunction

'This function is set as the default member of the interface. This means instead of writing f.func(...), you can write f(...)'
Public Function func(ParamArray args()) As Variant
    'Do some kind of validation of the arguments'

    'Logic!'
End Function

Public Function funcByArray(args As Variant) As Variant
    'Do some kind of validation of the arguments'

    'Logic!'
End Function


The "func" function needs to be set as the default member of the class by exporting it to a text file, adding the line Attribute func.VB_UserMemId = 0 just below the Public Function func... line, then reimporting. A class which implements this interface can be "run" as a function, but also passed as an object.

An IFunction can be called as if it were a function, ClassName(arg1, arg2, arg3), which passes the parameters to the default func member.

I created a helper functions to make this more "safe". Si

Solution

Implicit Variant Types
You've been diligent with Type declarations, but in IFunction you have omitted the Variant type of the ParamArray:

Public Function func(ParamArray args()) As Variant


Use meaningful names

Your use of i, f and var are 3 examples of meaningless variable names. Consider more meaningful names.

Use independent loop bounds
Your Loop uses i as the loop variable and the starting index:

For i = i To UBound(args)


That can be confusing to read, and difficult to debug. Consider using independent variables

Magic error numbers
You've done half the work of creating custom error numbers:

vbObjectError + 1


But the 1 would ideally be defined as a constant instead of as a magic number in inline code.

Check bounds before using ReDim

Dim var() As Variant
ReDim var(0 To UBound(arr))


In this instance, the ReDim statement does away with the need for the Dim statement, but if args is an empty array, then you'll get a subscript out of range error when you try to ReDim with an upper bound of -1. It may be the cases that args will never be empty, but it is still good practice to check.

Code Snippets

Public Function func(ParamArray args()) As Variant
For i = i To UBound(args)
vbObjectError + 1
Dim var() As Variant
ReDim var(0 To UBound(arr))

Context

StackExchange Code Review Q#138557, answer score: 4

Revisions (0)

No revisions yet.