I have 2 modules: A and B. A uses B. A reads in two numbers, then calls a subroutine in B that needs these numbers. How do I make these two numbers, read in in A, available in the subroutine in B without having them to add to the call to the subroutine?
There are two possibilities that I'm aware of:
Add them to the call.
Move the two numbers into a third module C and use these in both A and B.
You've said you don't want option 1 but, option 2 might be ok for you.
An (untested) example is
module A
contains
subroutine readNumbers()
use C, only: a1, a2
use B, only: theFinalRoutine
!code to set a1 and a2
call theFinalRoutine
end subroutine readNumbers
end module A
module B
contains
subroutine theFinalRoutine()
use C, only: a1, a2
!do some things with a1 and a2
end subroutine theFinalRoutine
end module B
module C
real :: a1, a2
end module C
program test
use A, only: readNumbers
call readNumbers()
end program test
This isn't always a good idea, but it does help avoid circular dependencies in the case of data (rather than dependencies between routines).
Related
According to this documentation by Microsoft, the following code can be used to make sure;
a, b, and c are all Single; x and y are both Double
Dim a, b, c As Single, x, y As Double, i As Integer
> a, b, and c are all Single; x and y are both Double
The logic behind this is as follows
You can specify different data types for different variables by using a separate As clause for each variable you declare. Each variable takes the data type specified in the first As clause encountered after its variable name part.
However, when I checked with the debugger or MsgBox VarType(a) output, this is not the case.
As you can see, it appears that As is only working for the variables right before itself, ie., c, y and i. All others are Variant/Empty and VarType returns 0.
Is this just the documentation being wrong, or am I missing something obvious?
Microsoft Visual Basic for Application 7.1.1056
Excel 2016 (Windows 10)
The documentation you've linked to isn't wrong, but it's written for VB.NET and not VBA.
In VBA, as you've observed, any variable declarations that aren't immediately followed by As <type> will be Variant.
Therefore you'd need to write:
Dim a As Single, b As Single, c As Single, x As Double, y As Double, i As Integer
In VBA, when you declare
Dim a, b, c As Single
What it does is equivalent to this:
Dim a As Variant, b As Variant, c As Single
I believe the best practice is to always declare variable in a separate row. It prevents this bug and also allows for faster scanning of the code. So the code would look like this:
Dim a As Single
Dim b As Single
Dim c As Single
Consider the pseudo code:
function test()
print ('First Method')
test()
When the interpreter reads this code it enters the symbol 'test' into the symbol table then compiles the test() function into bytecode
When it comes to test(), it first looks up test in the symbol table, finds it, and emits code to push that symbol onto the VM stack. It then sees () and emits a 'call' bytecode. We end up with this sequence of calls
push test
call
What happens if we also have a second test declared in the same scripts such as:
function test()
print ('First Method')
test()
function test()
print ('Second Method')
Byte code:
push test
call
push test <- but which test?
When the interpreter encounters the second function, it has the same name as the first function, what do we do? If we replace the symbol table entry with the new test function we lose the entry that the test() call was relying on. I noticed that interpreters such as R and Python preserve the call to the original test function.
My question is how is this done? The only way I can think of is that there is some kind of scoping going on. Thus the first function and the call to test() all are in one scope with its own symbol table and the second function is in a separate scope and has its own symbol table. The second scoping would also have to be inside the first scope. For example
function test()
print ('First Method')
test()
x = "This is in the first scope"
function test()
print ('Second Method')
print (x)
The print (x) would first search for the symbol x in the local scope, then in the enclosing scope.
I'm quite a beginner at coding and the problem that I'm having is that the procedure is too large. I read that this can be fixed by dividing the code into smaller modules. However I want to know if the values of the variables defined outside the modules can be taken into account when you call the variable inside the modules to modify it. Also, after being modified can you use the new value outside the module and in another module? Can someone explain me how the VBA works in these cases? Thank you so much.
I read the answer you gave me but while i understand the concept there is no example that fit my case. I would really appreciate an example so I can fully understand how to solve my issue. What i have is basically:
sub simulador()
Sheets(2).Activate
With ActiveSheet
a=1
b=1
c=1
for t=2 to 20
procedure using and modifying variables a and c
b=cells(t,15)
for a=2 to 20
code using variable b
next a
d=3
j=9
procedure using the variables a b and j
for ga=2 to 20
code using variable d
next ga
next t
end with
end sub
And what i need to do is create a sub1 whit:
procedure using and modifying variables a and c
b=cells(t,15)
A sub 2 whit:
for a=2 to 20
code using and modifying variable b
next a
An sub 3 whit:
procedures using the variables a b and j
for ga=2 to 20
code using variable d
next ga
So at the end would be like:
sub simulador()
Sheets(2).Activate
With ActiveSheet
a=1
b=1
c=1
for t=2 to 20
Call sub 1
Call sub 2
d=3
j=9
Call sub 3
next t
end with
end sub
In all the cases i want to call the subroutines passing the variables Byref so once they are modified stay modified so another sub can use them with the new value. Also the active sheet should remain active when the procedures in those subroutines are executed. I can't figure out how everything works exactly. Thanks for your help.
I don't have the will to spend time in explain how this works, because these are there very basics of programming, so.... search on google this:
vb function passing parameter difference byref and byval
I learned from internet that our vba macros (subs and functions) should be more or less the size of the screen.
But all my code is written exactly in the opposite: a have a huuuge main sub that calls functions and small subs.
My question is: if I decide to convert my main huge sub into small subs (sub1, sub2, sub3, etc), then what should be the best way to keep the value of a variable that was assigned in sub1 and use it in sub2, that was assigned in sub2 and use it in sub3, and so on? If I have no answer to this question, I am going to save those values in a sheet, but maybe this way is going to cause me trouble that I cannot see by now. Thanks for any help.
A Sub or a Function should have a single and clear purpose, told by its name. If you break down your code in such chunks, you will know what information those methods will need and deliver. At the same time they will become smaller. If you have to choose between size and clarity, go for clarity.
Do NOT put your values in a sheet. That is just a different and less efficient way of using global variables, which is something you should avoid (not at all cost, but almost).
Both methods below would work. In my personal opinion, method 1 keeps code more simple (you avoid to pass big bunches of parameters to each macro) but depending on your need you might choose one or the other.
Global variables
Your code would look something like this:
'Global variables declaration
Dim a As Integer
Dim b As Integer
'
Public Sub mainRoutine()
setA '<-- call first macro
setB '<-- call second macro
MsgBox a + b '<-- show A + B (2 + 3 = 5)
End Sub
'code of sub-routines
Private Sub setA()
a = 2 '<-- since a is global, it will keep the value
End Sub
Private Sub setB()
b = 3 '<-- since b is global, it will keep the value
End Sub
Please note: global means that the value of this variable will live for all execution of your program. Memory will be released once the program ends.
Pass variables as ByRef parameters
Public Sub mainRoutine()
Dim a As Integer '<-- a is local
a = 2 '<-- setting a to 2
setA a '<-- passing a as ByRef parameter
MsgBox a '<-- a is now 3
End Sub
Private Sub setA(ByRef a As Integer)
a = 3 '<-- setting a as 3.
End Sub
Of course, the above method would only allow to keep a = 2/3 during the execution of mainRoutine. But if at some point you execute another macro called by another stack (for example another button in the spreadsheet), you wouldn't be able to access the value of a as you would in method 1.
Important: no, variables on the spreadsheet is not a good idea, unless you don’t need to keep the value after closing and reopening the spreadsheet (in that case you would be using the spreadsheet as a sort of database).
VBA can be considered as an object oriented programming language (OOP).It has 3 of the 4 pillars of OOP:
Abstraction
Encapsulation
Polymorphism
This is discussed in Is VBA an OOP language, and does it support polymorphism?
OOP means understanding the SOLID principles.
Single Responsibility Principle
Open Closed Principle
Liskov Substitution Principle
Interface Segregation Principle
Dependency Inversion Principle
Best practice is to adhere to these principles and the first is the Single Responsibility Principle.
This states that your functions/subs should have one responsibility and focus on doing one thing.
Advantages (amongst others):
The reduced code complexity and dependencies mean:
1) Easier to debug as your track down which sub/function the error is raised in
2) Units tests can be more easily be built around code that is based on single responsibility.
3) Can be more easily slotted into a wider code base
To use variables between subs/functions consider:
1) Either public variables, and/or
2) Passing variables as arguments
Other info:
OOP VBA pt.1: Debunking Stuff
I have a script (csh) which calls a fortran executable.
Each time the script calls the fortran code a counter should be incremented and using that counter I have to create a new output file.
Can I pass a variable to my fortran code or is there a simple way of doing the same.
I have tried this code:
program callsave
c
implicit none
integer i,j
c
do j = 1, 10
call trysave(i)
print *, i
end do
stop
end
c
subroutine trysave(i)
integer k
data k /1/
save k
i = k
k = k + 1
end subroutine
c
This works fine individually. But when I call this subroutine separately in my fortran code through the script, it is not getting incremented. It just has the initial value '1', and the output files been overwritten.
Any type of help/suggestion would greatly be appreciated.
Thanks
Praveen.
Is it that you're looking for a way of passing an integer value from the shell script to the fortran code?
In that case, one way would be to use command line arguments, see e.g. here: http://gcc.gnu.org/onlinedocs/gfortran/GETARG.html
I'm not sure what is the official status of the getarg() routine, but by experience it works fine in gfortran, intel and PGI compilers.