Custom callbacks in VBA
No other answers in a week...for resolution's sake here's the best I could come up with:
- I built a helper module that resolves a ParamArray to individual arguments for the sake of calling
CallByName
. If you pass a ParamArray through toCallByName
it will mash all the arguments into a single, actualArray
and pass that to the first argument in the method you attempt to invoke. - I built two
ForEach
methods: one that invokesApplication.Run
, and another that invokesCallByName
. As noted in the question,Application.Run
only works for user-defined global (public module) methods. In turn,CallByName
only works on instance methods, and requires an object argument.
That still leaves me without a way to directly invoke built-in global methods (such as Trim()
) by name. My workaround for that is to build user-defined wrapper methods that just call the built-in global method, for example:
Public Function FLeft( _ str As String, _ Length As Long) As String FLeft = Left(str, Length)End FunctionPublic Function FLTrim( _ str As String) As String FLTrim = LTrim(str)End FunctionPublic Function FRight( _ str As String, _ Length As Long) As String FRight = Right(str, Length)End Function...etc...
I can now use these to do things like:
' Trim all the strings in an array of stringstrimmedArray = ForEachRun(rawArray, "FTrim")' Use RegExp to replace stuff in all the elements of an array' --> Remove periods that aren't between numbersDim rx As New RegExprx.Pattern = "(^|\D)\.(\D|$)"rx.Global = TrueresultArray = ForEachCallByName(inputArray, rx, "Replace", VbMethod, "$1 $2")
Very old question but for those looking for a more general approach please use stdCallback
and stdLambda
alongside stdICallable
. These can be found as part of the stdVBA
library.
sub Main() 'Create an array Dim arr as stdArray set arr = stdArray.Create(1,2,3,4,5,6,7,8,9,10) 'Can also call CreateFromArray 'Demonstrating join, join will be used in most of the below functions Debug.Print arr.join() '1,2,3,4,5,6,7,8,9,10 Debug.Print arr.join("|") '1|2|3|4|5|6|7|8|9|10 'Basic operations arr.push 3 Debug.Print arr.join() '1,2,3,4,5,6,7,8,9,10,3 Debug.Print arr.pop() '3 Debug.Print arr.join() '1,2,3,4,5,6,7,8,9,10 Debug.Print arr.concat(stdArray.Create(11,12,13)).join '1,2,3,4,5,6,7,8,9,10,11,12,13 Debug.Print arr.join() '1,2,3,4,5,6,7,8,9,10 'concat doesn't mutate object Debug.Print arr.includes(3) 'True Debug.Print arr.includes(34) 'False 'More advanced behaviour when including callbacks! And VBA Lamdas!! Debug.Print arr.Map(stdLambda.Create("$1+1")).join '2,3,4,5,6,7,8,9,10,11 Debug.Print arr.Reduce(stdLambda.Create("$1+$2")) '55 ' I.E. Calculate the sum Debug.Print arr.Reduce(stdLambda.Create("Max($1,$2)")) '10 ' I.E. Calculate the maximum Debug.Print arr.Filter(stdLambda.Create("$1>=5")).join '5,6,7,8,9,10 'Execute property accessors with Lambda syntax Debug.Print arr.Map(stdLambda.Create("ThisWorkbook.Sheets($1)")) _ .Map(stdLambda.Create("$1.Name")).join(",") 'Sheet1,Sheet2,Sheet3,...,Sheet10 'Execute methods with lambdas and enumerate over enumeratable collections: Call stdEnumerator.Create(Application.Workbooks).forEach(stdLambda.Create("$1#Save") 'We even have if statement! With stdLambda.Create("if $1 then ""lisa"" else ""bart""") Debug.Print .Run(true) 'lisa Debug.Print .Run(false) 'bart End With 'Execute custom functions Debug.Print arr.Map(stdCallback.CreateFromModule("ModuleMain","CalcArea")).join '3.14159,12.56636,28.274309999999996,50.26544,78.53975,113.09723999999999,153.93791,201.06176,254.46879,314.159 'Creating from an object property Debug.Print arr.Map(stdCallback.CreateFromObjectProperty(arr,"item", vbGet)) '1,2,3,4,5,6,7,8,9,10 'Creating from an object method Debug.Print arr.Map(stdCallback.CreateFromObjectMethod(someObj,"getStuff"))End SubPublic Function CalcArea(ByVal radius as Double) as Double CalcArea = 3.14159*radius*radiusEnd Function