Is it possible to pass an array as a parameter for a function in d3 pick? I have tried doing that and it seems to generate an error message:
B14 Bad Stack Descriptor
The error message appears if you try to pass an array as a parameter for a function. This leads me to two questions.
1) Is it is even possible to pass an array as a parameter in d3 pick?
2) If it is not possible to pass an array directly, is there some kind of workaround that will allow one to achieve the same result?
The terminology in the question is a bit ambiguous but try this:
DIM ARRAY(5)
ARRAY(1) = "FOO"
CALL MYSUB( ARRAY )
CRT ARRAY(1) ; * should be BAR
END
And in the called item:
SUBROUTINE MYSUB( MYARRAY )
DIM MYARRAY()
MYARRAY(1) = "BAR"
RETURN
Another solution to this is to pass it indirectly through Common:
COMMON ARRAY(5)
ARRAY(1) = "FOO"
CALL MYSUB
CRT ARRAY(1) ; * should be BAR
END
And in the called item:
SUBROUTINE MYSUB
COMMON MYARRAY(5) ; * need to agree
MYARRAY(1) = "BAR"
RETURN
To avoid the need to have each program know how many elements are required, put that code in an Include item:
Include item APP.COMMON:
COMMON ARRAY(5)
* nothing else here unless you have other things going on
Mainline code:
INCLUDE APP.COMMON
ARRAY(1) = "FOO"
CALL MYSUB
CRT ARRAY(1) ; * should be BAR
END
And in the called item:
SUBROUTINE MYSUB
INCLUDE APP.COMMON
ARRAY(1) = "BAR" ; * need to use same variable declared in the include item
RETURN
Yes you can...
Create a program "MainProgram"
Program MainProgram
MyArray = ""
MyArray<1> = "ParameterValue2"
MyArray<2> = "ParameterValue2"
call MySubroutine(MyArray)
Stop
Create the Subroutine "MySubroutine"
Subroutine MySubroutine(PassedParam)
for x = 1 to dcount(PassedParam,#AM)
print PassedParam<x>
next x
return
The Subroutine needs to be compiled and cataloged.
Related
I am trying to write a simple function where, based on the parameter I am passing, I need to return a string value. I am getting error Compile error: Syntax error.
Public Function getServer(env As String) As String
Dim serverName As String
Select Case env
Case "DEV"
serverName = "abc"
Return serverName;
Case "TEST"
serverName = "def"
Return serverName;
Case "Prod"
serverName = "xyz"
Return serverName;
End Select
End Function
VBA doesn't use Return to exit a function early, or to specify the returned value of the function. In VBA, you specify early exit using Exit Function; and in order to return a value or object from a function, you have to assign / set the name of the function to the value / object you want to return:
Public Function getServer(env As String) As String
Select Case env
Case "DEV"
getServer = "abc"
Case "TEST"
getServer = "def"
Case "Prod"
getServer = "xyz"
End Select
End Function
In VBA the Return statement (which does exist) serves an entirely different purpose; it's used in conjunction with the legacy GoSub statement, to return from a subprocedure jump:
bar = 42
GoSub Foo
Exit Sub
Foo:
Debug.Print bar
Return
This type of construct is present in the language to support earlier versions/dialects of BASIC, and shouldn't be seen in modern VBA code.
Functions and Property Get procedures return their return value by assigning to the procedure's identifier:
getServer = "abc"
Note that the procedure's identifier is essentially a local variable, and thus the assignment doesn't return. Use Exit Function statements to bail out.
Also, {NEWLINE} is the end-of-instruction marker in VBA, not ; semicolon ;-)
The semicolon is used in VBA to control the behavior of string-printing, e.g. Debug.Print and Write# statements.
Sub test()
Debug.Print 1; 2; 3;
Debug.Print 4; 5; 6; ' prints on the same line as the previous statement
End Sub
Whereas this would output on 2 separate lines:
Sub test()
Debug.Print 1; 2; 3
Debug.Print 4; 5; 6 ' prints on the next line
End Sub
The commented lines below continue to return to me the error Run Time Error'424': Object Required. I am working on this as part of a self learn class, and I have tripled checked to make sure I am entering the code properly and am still returning this error. I tried looking at other examples of this error on here, but none were helpful to this instance because I do not know the code well enough yet.
Thank you!
Sub FirstArray()
Dim Fruit(2) As String
Fruit(0) = "Apple"
Fruit(1) = "Banana"
Fruit(2) = "Cherry"
Range("A1").Text = "First Fruit: " & Fruit(1)
' ^ RUN TIME ERROR 424
Dim Veg(1 To 3) As String
Veg(1) = "Artichoke"
Veg(2) = "Broccoli"
Veg(3) = "Cabbage"
Range("B1").Text = "First Veg:" & Veg(1)
' ^ RUN TIME ERROR 424
Dim Flower() As String
ReDim Flower(1 To 3)
Flower(1) = "Azalea"
Flower(2) = "Buttercup"
Flower(3) = "Crocus"
Range("C1").Text = "Final Flower:" & Flower(3)
' ^ RUN TIME ERROR 424
End Sub
You just need to change .Text to .Value
Changing .Text for .Value will fix it, but in case you're wondering how "Object required" even remotely makes sense as an error message, here's why.
You know the syntax for implicit value assignment:
foo.Bar = 42
The explicit value assignment syntax is still supported, but obsolete/deprecated:
Let foo.Bar = 42
This assignment calls the Property Let accessor of the Bar property of the foo object, which could look something like this:
Public Property Let Bar(ByVal value As Long)
internalBar = value
End Property
Public Property Get Bar() as Long
Bar = internalBar
End Property
In the case of Range.Text, the property might look something like this:
Public Property Get Text() As String
Text = GetStringRepresentationOfValue
End Property
Notice there's no Property Let accessor, so this:
someRange.Text = "foo"
Isn't legal, because the Text property doesn't expose a Property Let accessor.
So what's the deal with object required? Getting to it.
But first you need to know what a default member is. You see every class module can define a "default member". For collections that member is usually the Item property, by convention.
This means whether you do this:
foo = myCollection.Item(12)
Or that:
foo = myCollection(12)
You get the exact same foo, exactly the same way - the two are exactly the same.. except the latter implicitly calls the default member, and the former explicitly does so.
A class' default member is determined by a hidden member attribute
that you can only see if you export the class module and open it in a
text editor:
Public Property Get Item(ByVal Index As Long) As Variant
Attribute Foo.VB_UserMemId = 0
Item = internalCollection(Index)
End Property
Only 1 member in a class can be the default.
So what the error message is saying, is that when it sees this:
foo.Bar = 42
And knows that Bar is a read-only property that only exposes a Property Get accessor, then the only way for this code to be legal, is if Bar returns an object that has a default member that can be assigned to take that value.
And since Range.Text returns a String and not an Object, VBA complains that an object is required.
Perhaps
Range("B1") = "First Veg:" & Veg(1)
Same for
Range("C1") = "Final Flower:" & Flower(3)
I have a query regarding creating an array of variables declared as string.
Below is my code. On debugging, the variables show no value.
Need help..
Module Module1
Public Status, PartStat, HomeStat, ClampStat, SldCylStat, PrsCylP1Stat,
PrsCylP2Stat, PrsCylP3Stat, PrsCylP4Stat, PunchStat, SysInProc, Home1,
Home2, Home3, CyclTim, TrqP1Stat, TrqP2Stat, TrqP3Stat, TrqP4Stat,
AngleP1Stat, AngleP2Stat, AngleP3Stat, AngleP4Stat As String
Function AutoReadStatus()
Dim StatArray = {HomeStat, ClampStat, SldCylStat, Home1, PrsCylP4Stat,
PrsCylP2Stat, Home2, PrsCylP3Stat, PrsCylP1Stat, Home3, PunchStat,
AngleP4Stat, AngleP2Stat, AngleP3Stat, AngleP1Stat, TrqP4Stat,
TrqP2Stat, TrqP3Stat, TrqP1Stat}
Status = ReadMultiReg(FormAuto.SP1, "03", "1258", "0013")
For i = 0 To ((Status.Length / 4) - 1)
StatArray(i) = CInt("&H" & Status.Substring(i * 4, 4))
Next
Return Nothing
End Function
End Module
It is not even showing the index of any variable from above array..
Label1.Text = Array.IndexOf(StatArray, SldCylStat)
When you assign a new value to the item inside the array, you assign a new value to the item inside the array (pun intended).
What that means is that item's array now reference the string (or rather the Integer implicitely converted to string as you don't have Option Strict On) you gave and the precedent reference (on your public field) is dropped.
Test this sample code and I think you will understand
Public item As String
Sub Test()
Dim array = {item}
Console.WriteLine(array(0) Is item) ' True
array(0) = "new value"
Console.WriteLine(array(0) Is item) ' False
End Sub
You can see now array(0) reference another object than the one referenced by the item field
As for how to solve it,yYou could pass all those string ByRef that way assignment inside the method would reflect outside of it but that would be tedious.
A "better" way IMO, would be to make a type (a Class) to hold all those string and pass an instance of that type to your method, that way you just mutate the same existing object.
Quick, contrived example :
Class SomeType
Property Item As String
End Class
Sub Test(instance As SomeType)
instance.Item = "new value"
End Sub
' Usage
Dim sample As New SomeType
' here sample.Item is Nothing
Test(sample)
' here sample.Item is "new value"
I'm trying to return a value from my code. It's much easier to just show the code:
Function writeHeaderData() As IXMLDOMNode
Dim xmlDoc As New MSXML2.DOMDocument30
xmlDoc.async = False
xmlDoc.LoadXML "<Foo></Foo>"
Dim Foo As IXMLDOMNode
Set Foo = xmlDoc.DocumentElement
'code snip; includes appending lots of things to Foo
'the error is on this line:
writeHeaderData = Foo
Exit Function
End Function
I've already Google searched, but it's been of no avail. This function is being called from the main subroutine, and I'm trying to append the returned IXMLDOMNode to a bigger one, but I keep getting an "Object variable or With block variable not set" error on the writeHeaderData = Foo line. What's up here?
In VB(A), when you want to assign to an object variable, including assigning the return value of a function, you need to use Set, so:
'the error is on this line:
writeHeaderData = Foo
should be
Set writeHeaderData = Foo
I have created a user defined type to contain some data that I will use to populate my form. I am utilizing an array of that user defined type, and I resize that array as I pull data from an off-site server.
In order to make my program easier to digest, I have started to split it into subroutines. However, when my program is initialized, I cannot tell when a particular array has been initialized, and so I cannot be certain that I can call a size function to see if the array is empty.
Is there a way to initialize an empty user type or detect a null user type? Currently, I am hard-coding it in and I would prefer a more elegant solution.
In addition to isempty(array) solution -
If IsNull(array) then
msgbox "array is empty"
End If
AFAIK, you cannot check whether user-defined type was initialized before it was sent as an argument to a procedure/function.
I am quoting this example from VBA help
Type StateData
CityCode(1 To 100) As Integer ' Declare a static array.
County As String * 30
End Type
The County field is initialized to some value, which you can use a base value.
If the user sets this field explicitly, it means it holds some value & remains uninitialized, otherwise.
for e.g.
Sub main()
Dim example As StateData
MsgBox IsInitialized(example)
Dim example2 As StateData
example2.County = "LA"
MsgBox IsInitialized(example2)
End Sub
Function IsInitialized(arg As StateData) As Boolean
Dim initCounty As String * 30
IsInitialized = (arg.County <> initCounty)
End Function
Try:
dim v
if isempty(v) then
msgbox "is empty"
end if
If myObjectVariable is Nothing
should work to detect if an object has been initialized.
Edit: "is nothing" DOES work, if it is an object variable:
Dim blah As Object
If blah Is Nothing Then
MsgBox "blah is nothing!"
End If
Dim foo as variant
If IsEmpty(foo) Then
MsgBox "foo is empty!"
End If
If you need to check whether the whole dynamic array of custom types was initialized or not (not just particular element) in VBA, then this might not be possible directly (as none of the IsEmpty etc. functions works on custom types). However you might be able to easily restructure your program to return an array of custom types of size 0 to indicate that nothing was read/initialized.
Private Function doStuff() As customType()
Dim result() As customType
' immediately size it to 0 and assing it as result
ReDim result(0)
doStuff = vysledek
' do real stuff, ... premature "Exit Function" will return an array of size 0
' possibly return initialized values
End Function
' then you can all
If (UBound(tabulky) = 0) Then
MsgBox "Nope, it is not initialized."
End If