Format - Expected Array - vba

I keep getting an error when I try to format this number. I've done it in VBA before and I tried to change the SOPID to a variant, a string, and an integer.
Dim SOPID As Integer
SOPID = DMax("file_id", "tblSOP") + 1
'Output test data
MsgBox (format(SOPID, "000"))
I have no idea what I am doing wrong.

Assuming the code was pasted directly from your IDE, the casing of format is suspicious; that would be Format, unless there's a format variable or function that's in-scope, ...and that's being invoked here.
Look for a public (could be implicitly Public, or if it's in the same module then it could also be Private) format function procedure that expects an array argument: that's very likely what's being invoked here.
Rubberduck (free, open-source; I manage this project) can help you easily identify exactly what's being invoked and an inspection would tell you about any shadowed declarations, but to be sure you can fully-qualify the function call to avoid inadvertently invoking another function that's in scope:
MsgBox VBA.Strings.Format$(SOPID, "000")
Note that there are no parentheses around the argument list of a parameterized procedure call in VBA; the parentheses you have there are surrounding the first argument and making the expression be evaluated as a value that is then passed to the invoked function: this isn't necessary.
Also note the $: the VBA.Strings.Format function takes and returns a Variant; VBA.Strings.Format$ takes and returns a String. If you aren't dealing with any Null values (an Integer couldn't be Null), consider using the String-returning alias.

Related

Assign Bang argument programmatically?

I'm not successful in using the Bang (!) operator without it's argument being hardcoded, i.e., Me.VBProject.References!Excel. In this example, the Excel reference is hardcoded.
Out of frustration I've tried all permutations I can think of in an attempt to utilize it:
[Me.VBProject.References!(str)]
[Me.VBProject.References! & (str)]
["Me.VBProject.References!" & str]
["Me.VBProject.References!" & (str)]
and many more with parens added to ensure proper pre-evaluation including the longhand Application.evaluate method. Nada!
No, you can't do that.
The bang operator is just a shortcut for calling the default member of the object, and passing the text after the bang as a string to the first parameter of the default member:
The bang notation:
Me.VBProject.References!Excel
is exactly equivalent to:
Me.VBProject.References.Item("Excel")
and, because it is the default member, you can omit the Item function call:
Me.VBProject.References("Excel")
So, to use your (really badly named) variable str:
str = "Excel"
Debug.Print Me.VBProject.References.Item(str).Name
This is an X-Y problem.
Bang notation is a means to an end.
It's one of the tools made available to you, to retrieve an item from a collection.
Nothing more, nothing less.
What you want isn't to use the bang operator with a variable.
What you want is to retrieve an item from a collection using a variable.
Collection types have a default member, typically named Item. Default members can be specified explicitly, or implicitly accessed:
Dim foo As New Collection
foo.Add Item:=42, Key:="test"
Debug.Print foo.Item("test") 'explicit reference to the default member
Debug.Print foo("test") 'implicit reference to the same default member
The bang operator is just another way to make an implicit call to the collection's default member:
Debug.Print foo!test
All 3 Debug.Print statements above will call the foo.Item default member to output the 42 associated with the key "test".
Square brackets
As you can see, what comes immediately after the ! operator is really a string literal. Because a string literal can contain whitespace, the VB6/VBA parser needed a way to support them.
That's what the [square brackets] are for:
foo.Add 72, "spaces in the key"
Debug.Print foo![spaces in the key]
When they're not delimiting a string literal for bang notation, square brackets are usually1 interpreted as a run-time expression for the host application to evaluate. For example this is legal (though questionably advisable) in Excel VBA:
Debug.Print [A1]
The VBA parser identifies a bracketed expression and defers its evaluation to the host application - here Excel; at run-time, the instruction ultimately equates to:
Debug.Print ActiveSheet.Range("A1").Value
If you don't believe the evaluation of a bracketed expression is deferred to the host application, consider what needs to happen for this instruction to print 4:
Debug.Print [Sum(2,2)]
Therefore, every single one of the attempts/permutations in your post missed the mark and made Excel try to evaluate an expression that only VBA would be able to resolve, because Me.VBProject.References means absolutely nothing to Excel.
Square-bracket expressions should usually be avoided, because you lose compile-time checks and any error can only be caught at run-time.
1Usually, because they can also be used in some identifiers,
for example in Enum types, to make a [_hidden] enum member.
Bottom Line
Bang notation is a way to retrieve an item from a collection by leveraging default members and making string literals look like an identifier. You can't make it work without "hard-coding" the string literal, because it requires a string literal.
If you want to parameterize the collection retrieval, you can't use the bang operator.
It's useful for typing the code faster. If you don't know exactly how it works and what it does for you though, it's a double-edged blade that hides what's really going on and ultimately makes the code harder to read and understand. Code shouldn't be written just to be run. Code should be written to be read and understood.
Note: Bang notation isn't actually only for collections. It actually passes its argument as a string literal to the first parameter of anything that has a default member. I would strongly advise avoiding it for anything other than a collection class though (e.g. Collection.Item, Workbooks.Item, Worksheets.Item, Recordset.Fields, etc.), for the sake of future maintainers' sanity.

Meaning of `:=` Syntax in VBA methods

When writing the following VBA, what is the root cause of the error "Expected =" given that we are using the Format:=2.
Workbook.Open (filename, Format:=2)
I understand that this format works when setting the variable as in the following code, but why does it work here and not in the above format?
Set wrkb = Workbook.Open (filename, Format:=2)
Also what is this operator called, := and how is it used?
It's not an operator, it's a named argument.
You can chose the order of the arguments/parameters by directly specifying what they are.
The concept of named arguments also exists in multiple modern languages, such as c# (its optional, just like in VBA) and swift (by default it's required, but you can disable it).
Named arguments also allow you to omit arguments that are optional altogether, but pass an argument that is further back in the list of arguments. A good way to try named arguments out is the messagebox, since it has many optional arguments with default values.
Example: MsgBox only specifying title:
MsgBox Title:="wew lad"
Or, in the more modern way of writing vb(a) code:
Call MsgBox(Title:="wew lad")
The MsgBox is a good example, since you can call it normally, and then specify a parameter further back directly (works with other methods too):
Call MsgBox("Yes!", Title:="wew lad")
Once there are named parameters, you can't add ordered parameters after:
'Call MsgBox (Prompt:="Yes!", vbOkCancel Title:="wew lad")
'this doesnt work!
Now, why does this raise an error:
MsgBox ("Hello", Title:="test")
This is some of the weirder parts of vba. Calling functions with return values but ignoring the value while using parentheses is a bit broken.
You can circumvent this by adding a Call in front of it (vba then knows it can ignore the result).
This would look like this:
Call MsgBox ("Hello", Title:="test")
Honestly, I don't know why this is, but I've had it cause really weird bugs. When you use the parentheses syntax, I can really recommend using the Call keyword before the method call.
As Macro Man mentioned, you can also omit the parentheses, instead of using Call:
MsgBox "Hello", Title:="test"
And it will work, too. This was the original vba coding style and isn't really used anymore.

Passing parameters of varying Types

I am using a procedure which involves parameter passing and the parameter being passed is a variable. Because I have explicitly declared the data type of another parameter, I need to do the same for this one. What data type do I declare the parameter as if it is a variable?
Thanks
An example of what you are doing and what Types you are dealing with would have been nice. You can implement Overloading to provide for different parameter Types:
Friend Function FooBar(n As Integer) As Integer
Friend Function FooBar(n As Int64) As Integer
Friend Function FooBar(n As Short) As Integer
The compiler will pick the function which matches the data type being passed. Internally, they might do whatever based on the Type passed, then call another procedure to perform any stuff common to them all.
There is probably a finite number of types you need it to work with. For instance Font, Point and Rectangle probably make no sense. Even Date is dubious because you cannot do stuff to a date in the same way as with an Int or Long. String is also not likely needed because you can always pass it as FooBar(CInt(someString)) provided it does contain a valid integer or whatever.
You can also use a generic to tell the function what you are passing:
Private Function FooBar(Of T)(parm As T) As Integer
' called as:
ziggy = FooBar(Of Int32)(n)
zoey = FooBar(Of String)(str)
This might even be Private Function FooBar(Of T)(parm As T) As T if the function return varies depending on the parameter Type passed. There are many uses for this (one of which is to avoid passing a param as Object), but as a general purpose way of passing any type you want it is not a good idea: internally you will likely have to have a big If/Else to handle the different types their own way.
Turning off Option Strict is never advisable since all sorts of unwanted type conversions can take place.
In VB.NET, you can use Object as the type but with Option Strict Off. You can pass any kind of parameter in that case.
for more information, refer : https://stackoverflow.com/a/2890023/3660930

Objective-c Ensure that var-arg parameters are of the correct type and count

In objective-c how do you ensure that if you have a function that takes variable parameters that the format specifiers align with the actual parameters that are passed to the functoin?
This is done through the use of the NS_FORMAT_FUNCTION macro.
Let's say that you have a function like this:
LOG(int level,NSString *format,...);
The level is the log level while the format contains the format string and the variable arguments are the parameters to the format string.
In order to ensure at compile time that the count and type of the parameters is correct one defines the function as this:
LOG(int level,NSString *format,...) NS_FORMAT_FUNCTION(2,3);
Note that the 2 and 3 here refer to the position in the arguments list of the format string and the start of the variable parameter list.
C doesnt check either by default.
nowadays:
with LLVM both the number and the type of parameters can be checked(enforced) via the CF macro NS_FORMAT_FUNCTION

How do I use Thread.VolatileWrite with reference types with Option Strict On?

Wrapping the argument in CObj or DirectCast shuts the compiler up, but the value is still not written.
Option Strict On
Imports System.Threading
Module Module1
Dim str As String
Sub Main()
Thread.VolatileWrite(str, "HELLO") ' Compiler error.
Thread.VolatileWrite(CObj(str), "HELLO") ' Fails silently.
Thread.VolatileWrite(DirectCast(str), "HELLO") ' Fails silently.
Console.WriteLine(str)
End Sub
End Module
There is no overload of Thread.VolatileWrite which takes a String argument. The only reference type supported is Object.
Because VolatileWrite is updating the variable str and Option Strict is On the compiler complains because in theory VolatileWrite could attempt to write a value to that variable which is not of type String (the compiler only sees that it might write any Object). In fact, as the VolatileWrite method also only takes a String you could write code which would attempt to do this. It would fail for reasons beyond the scope of this question.
When you wrap the expression in a COjb/CType/DirectCast expression (really anything with parenthesis) then the variable is no longer considered a variable but a value - it's treated the same way as if you'd just type a string literal there. Since values don't have storage locations the ByRefness of VolatileWrite is ignored which means it no longer writes which means it can no longer write a bad value which means the compiler doesn't need to warn anymore.
To get the behavior you want with a string type variable use the System.Threading.Thread.MemoryBarrier method before your writes and after your reads. See this thread for additional information: How do I specify the equivalent of volatile in VB.net?