patternMinor
Functional Framework
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
The "func" function needs to be set as the default member of the class by exporting it to a text file, adding the line
An IFunction can be called as if it were a function,
I created a helper functions to make this more "safe". Si
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 FunctionThe "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
Use meaningful names
Your use of
Use independent loop bounds
Your Loop uses
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:
But the
Check bounds before using
In this instance, the
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 VariantUse 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 + 1But the
1 would ideally be defined as a constant instead of as a magic number in inline code.Check bounds before using
ReDimDim 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 VariantFor i = i To UBound(args)vbObjectError + 1Dim var() As Variant
ReDim var(0 To UBound(arr))Context
StackExchange Code Review Q#138557, answer score: 4
Revisions (0)
No revisions yet.