Excel cell formula scope rules for selecting VBA modules - worksheet-function

Suppose I have a macro-enabled Excel workbook Q.xlsm containing two user-defined modules A and B, and each module has a public function named xyz() where A::xyz() returns a STRING object, and B::xyz() returns an INTEGER value.
Q.xlsm
`-- Modules
|-- A
| `-- Public Function xyz() As String
`-- B
`-- Public Function xyz() As Integer
Q1) What is the worksheet cell formula scoping syntax that allows me to invoke either A::xyz() or B::xyz()?
Another case would be two macro-enabled workbooks P.xlsm and Q.xlsm, each with a user-defined module A having a public function xyz(). Suppose workbook P is saved as an add-in module P.xlam, and that this add-in is in fact "added in" to Excel (e.g., File > Options... > Add-Ins > ...). Now when I open workbook Q.xlsm I have two public functions P.xlam::A::xyz() and Q::A::xyz(),
P.xlam (add-in)
|-- Modules
| `-- A
| `-- Public Function xyz() As String
Q.xlsm
`-- Modules
`-- A
`-- Public Function xyz() As Integer
Q2) If I enter =xyz() into a worksheet cell, how do I know which function is invoked (i.e., what are the scope rules for VBA function names within worksheet cells)?
Q3) What syntax do I use in the worksheet cell formula to invoke either P.xlam::A::xyz() or Q.xlsm::A::xyz()?

Q1) What is the worksheet cell formula scoping syntax that allows me to invoke either A::xyz() or B::xyz()?
A1) The worksheet cell formula scoping syntax seems to be MODULE_NAME.FUNCTION_NAME(), e.g.,
=A.xyz() // Returns the String object
=B.xyz() // Returns the Integer value
Q2) If I enter =xyz() into a worksheet cell [in workbook Q.xlsm] , how do I know which function is invoked (i.e., what are the scope rules for VBA function names within worksheet cells)?
A2) After some testing with Excel 2016, invoking =xyz() within a worksheet cell in workbook Q.xlsm invokes the add-in version P.xlam!A.xyz() and not Q.xlsm!A.xyz().
Q3) What syntax do I use in the worksheet cell formula [in workbook Q.xlsm] to invoke either P.xlam::A::xyz() or Q.xlsm::A::xyz()?
A3) The scoping syntax seems to be WORKBOOK_FILE_NAME!MODULE_NAME.FUNCTION_NAME(),
=P.xlam!A.xyz() // returns STRING, becomes `=A.xyz()`
=Q.xlsm!A.xyz() // returns INTEGER
If function xyz() is not overloaded within any other modules within the workbook Q.xlsm, then the shorthand scoping syntax WORKBOOK_FILE_NAME!FUNCTION_NAME() also works, e.g.,
=P.xlam!xyz() // returns STRING, becomes `=xyz()`
=Q.xlsm!xyz() // returns INTEGER
EDIT 2017-11-27
Another useful scope syntax that can be used in cell formulas:
'[workbook_file_path]worksheet_name'!range_within_worksheet_name
Examples:
'[C:\Temp\Workbook1.xlsx]Scores'!$B$10
'[Workbook2.xlsx]Roster'!Teams
If anyone knows of a built-in, shortcut syntax that describes "this workbook's full path" and is suitable for use as the "[workbook_file_path]" field, please comment. (n.b., I'm not talking about creating an INDIRECT() kludge that uses =CELL("filename",A1) or some variant thereof.)

Related

Importing class and using its methods accross files

Im trying to save myself some headache in a project im working on by splitting it up in files but I cannot seem to be able to cross call functions. (inside file_a, call a function in file_b which calls a function in file_a)
Is this just not possible or am i doing it wrong?
file_a:
local imported = require("fileb")
m = imported:new(BC)
m.print_message("hi")
file_b:
class_b = {
BC = {}
}
function class_b:print_message(message)
print(message)
end
function class_b:new (BC)
local AB = {}
AB = BC or {}
AB._index = class_b
setmetatable(AB, self)
return AB
end
return class_b
When i run this i get: attempt to call nil value (field 'print_message')
My project is much larger and complex at the moment but i extracted the logic i am working with right now to show what im trying to do to be able to cross call the functions.
You're calling m["print_message"] which is a nil value. Calling nil values is not allowed as it doesn't make any sense to call a nil value.
If you index a field that does not exist in a table, Lua will check wether the table has a metatable and if that metatable has a field __index.
While that field exists in m it does not exist in its metatable class_b.
class_b.__index must refer to class_b to make this work.

Late binding with COM objects (flexgrid) is 2 times slower than early binding

Public Function gridloop(MSFG1 As Object) As Long
For i= 0 To MSFG1.rows - 1
A = MSFG1.TextMatrix(i,1)
Next
End Function
The above code is 2 times slower than below
Public Function gridloop(MSFG1 As MSHFlexGrid) As Long
Public Function gridloop(MSFG1 As MSFlexGrid) As Long
Any solution to speed-up?
Not a lot of details in the question, I presume you have two (or more?) different controls where you're trying to essentially overload your gridloop function so it'll work with multiple types of controls?
The following might provide a performance improvement. I have not tested this, not even confirmed that it is free of compile errors. Idea is to determine the control type, then assign it to a variable of a matching type, then the references to the methods and properties might be early bound (thus faster).
Public Function gridloop(MSFG1 as Object) as Long
Dim myMSHFlexGrid As MSHFlexGrid
Dim myMSFlexGrid As MSFlexGrid
Dim i As Integer
Dim A As Long
If TypeOf MSFG1 Is MSHFlexGrid Then
Set myMSHFlexGrid = MSFG1
For i = 0 To myMSHFlexGrid.rows - 1
A = myMSHFlexGrid.TextMatrix(i,1)
Next
ElseIf TypeOf MSFG1 Is MSFlexGrid Then
Set myMSFlexGrid = MSFG1
For i = 0 To myMSFlexGrid.rows - 1
A = myMSFlexGrid.TextMatrix(i,1)
Next
End If
End Function
Alternative is to define two gridloop functions, one for each type. A form of manual overloading.
Public Function gridloop_MSHFlexGrid(MSFG1 As MSHFlexGrid) As Long
Public Function gridloop_MSFlexGrid(MSFG1 As MSFlexGrid) As Long
Advantage to this is that trying to call one of the gridloop functions with an 'incorrect' control will result in a compile error - catching a problem early that could otherwise require spending some significant time performing runtime debugging.
Building on MarkL's answer, you could use actual VB interface overloading to get what you want.
The idea would be to create an interface exposing whatever properties or functions you need on the grids, and then create two classes, each one implementing that interface and internally manipulating the actual grid.
This wrappering substitutes for the fact that the two grid types do not intrinsically share a common interface. (I looked in the IDL using OLEView).
You can then use the interface as the type in every location you currently are using Object to stand in for the actual grid class. If the interface is comprehensive AND its methods / properties are named appropriately then you would not need to make any other code changes.
Sample (pseudo)code...
Interface:
'In file for interface IGridWrapper
Function Rows
End Function
Function TextMatrix(i as Integer, j as Integer)
End Function
'Any others...
Wrapper class 1:
' In file for class "MSFlexGridWrapper"
Implements IGridWrapper
Private m_grid as MSFlexGrid
Sub Init(MSFlexGrid grid)
Set m_grid = grid
End Sub
Function IGridWrapper_Rows
IGridWrapper_RowCount = m_grid.Count
End Function
Function IGridWrapper_Textmatrix(i as Integer, j as Integer)
'etc.
End Function
'Any others...
Wrapper class 2:
' In file for class "MSHFlexGridWrapper"
Implements IGridWrapper
Private m_grid as MSHFlexGrid
Sub Init(MSHFlexGrid grid)
Set m_grid = grid
End Sub
Function IGridWrapper_Rows
IGridWrapper_RowCount = m_grid.Count
End Function
Function IGridWrapper_Textmatrix(i as Integer, j as Integer)
'etc.
End Function
'Any others...
Code using the wrappers:
Public Function gridloop(MSFG1 As IGridWrapper) As Long
(Note - none of this has been put through a compiler for exact syntax checking)
The basic reason that late binding (binds at runtime) is slower than early binding (binds at compile time) is that you have to use the iDispatch interface's GetIDsOfNames and Invoke methods to access properties in the object's interface, rather than accessing them directly from the vtable. For more information, have a look at this.
The reason that DaveInCaz's and MarkL's suggestions will probably speed things up is that they are ways to allow your gridloop function to accept a type that can be bound early rather than an Object type. DaveInCaz's solution is also a fine example of a practical application of polymorphism.

Error: use of deleted function when adding element to a map in C++

I'm getting a wall of text when I try to compile my code using g++ 11 in a Linux environment. I've tracked the cause of the error to one line of code where I'm trying to add elements to a map. Here's some of the relevant code:
set<Tuple> trash;
Relation garbage(trash, name, scheme_list);
Relations.insert({name, garbage});
Where the Tuple and Scheme classes (scheme_list is of type Scheme) are both simply vector<string>. The Relation class is defined as
public:
Relation(set<Tuple> TUPLES, string NAME, Scheme SCHEMES);
~Relation();
and the Relation constructor is
Relation::Relation(set<Tuple> TUPLES, string NAME, Scheme SCHEMES)
{
Tuples = TUPLES;
Name = NAME;
Schemes = SCHEMES;
}
The intent of the code at the moment is to create an empty set of Tuples, use the existing name in name and the list of schemes in scheme_list to create an instance of class Relation to insert into my map of Relations using the name as the key. Yet when I compile the code, it gives me a gigantic list of "use of deleted function" errors and "Relation::Relation(const Relation&) is implicitly deleted because the default definition would be ill-formed", the latter being the only reference to any of my code given by the compiler. What is causing this error, and why does it only occur when I try to insert elements into a map?

Examine object value between fluent interface calls

Imagine this call which uses three fluent API's to make a very simple and readable function (the IQueryable Where and OrderBy, the AutoMapper Project and To, and ToDataSourceResult from Kendo).
Return Json(DataSource.TemplateItems.Where(Function(x) x.TemplateID= id) _
.OrderBy(Function(x) x.SortOrder) _
.Project() _
.To(Of TemplateIdRowViewModel) _
.ToDataSourceResult(req))
For debugging, I sometimes want to examine the current value being passed around in between calls to the fluent API. For example, I might want to know what my IQueryable looks like after Project and To finish with it but before ToDataSourceResult gets it.
I'm using the VS 2010 debugger. Right now I find myself editing the call to include temporary variable for each step so I can examine them, like this:
Dim where = DataSource.TemplateItems.Where(Function(x) x.TemplateID = id)
Dim ordered = where.OrderBy(Function(x) x.SortOrder)
Dim projected = ordered.Project().To(Of TemplateIdRowViewModel)()
Dim result = projected.ToDataSourceResult(req)
Dim jsonified = Json(result)
Return jsonified

What is the difference between Sub and Function in VB6?

I am going through some old VB code and I run into function definitions like these -
Private Function ExistingCustomer(Index As Integer, Customer As String) As Integer
Private Sub cmdCustomerList_Click()
What's the difference?
Function returns value, Sub doesn't. It's that simple.
A function can also be used in an expression. A Subroutine cannot.
Functions can lend to the readability of your code better than a subroutine.
Here's an example of how a function can increase readability:
If AccountIsLocked("JJones") then Msgbox("This account is locked")
this function would be defined somewhere
public function AccountIsLocked(UserId as string) as boolean
dim usr = uow.AccountRepository.UserInfo(UserId)
return usr.locked
end function
Here's the same example but coded with a subroutine:
CheckIfAccountLocked("JJones")
and elsewhere this sub is defined:
public sub CheckIfAccountLocked(UserId)
if uow.AccountRepository.UserInfo(UserId).locked then
msgbox("Account is locked")
end if
end sub
Also note that checking the value is separated from the action -- this contributes to separation of duties. The function would lend toward re-usability.
With VB6 there are some odd rules governing parenthesis. If there are no parameters to a sub then the parenthesis are not needed (I think Visual Studio might remove the parenthesis). One way around this is to add the keyword "Call" before your sub.
Call CheckIfAccountLocked()
vs
CheckIfAccountLocked
In function we can return values as boolean, string and other data data types.
but sub does not return any thing.
it just executes code and instruction that we give. These are also regarded as methods
Sub is also used in control's events and these events also not return any value.
for example the click event of a command button:
Private sub cmd_click()
end sub
They are both sections to write code however a function must return a value. For example if you had a program in which a complicated mathematical procedure needs to be executed a number of times you would simply make a function and have the complicated maths code in there and any time you need to do the calculation you can just call the function. Hope this helped not sure if I explained it well.
What is the difference between Sub and Function in VB6?
"sub" can perform some action.
"sub" returns no value.
Example:
Form_Load()
"function" can also perform some action but it also returns some value to point from which it was called.
that is, "Functions return a value, often based on a variable"
Example:
Val(), FormatPercentage().
function in vb
a function must return some value/s
Syntax :
private function fun_name(argument/s(optional)) as return_type(integer,string..)
return value
end function
fun_name(arguments(optional) ) is enough for function call
sub in vb
a sub need not to be return any value/s
Syntax :
private sub sub_name(argument/s(optional))
end sub
sub_name(arguments(optional) ) is enough for function call
A function holds data and code. But a subroutine contains only code, but not data.
Syntax of functions will be Function...End function and for Sub will be Sub...End Sub.
Functions may or may not have objects but sub doesn't have objects
Functions are re-usable where Sub doesn't
Functions can return values but sub doesn't
Functions may have object repository but sub doesn't
Extension of functions is .qfl where for sub it's .vba

Resources