Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Advanced programming style discussion 2

Status
Not open for further replies.

ESquared

Programmer
Dec 23, 2003
6,129
US
Hi!

My name is Erik. I have been programming for 22 years. Admittedly, I was 9 years old when I started and I don't have all that much "professional programming experience" under my belt, but I'm getting there.

Lately I've been repeatedly running into certain similar problems across all the programs I am writing. My hope is that I can start a discussion with some programmers who are more experienced than I, and get a few tips. I don't know where else to go, there is no one here at work I can ask and I don't know anyone who has the experience I am trying to tap in to.

I am not sure how much to put in one question. My hope is that someone can see what I am driving at and help me ask the questions I don't even know how to ask!

With that somewhat incoherent preamble, I'll begin.


How important is it to avoid global variables? I have worked hard not to use them, one because I have read again and again since I started that they are bad, and two because my programming experience has proven this out. Globals can work when you have two or three variables, but when you have 10 or 20 or 100, it's time to do something different. Yet, sometimes in my quest to avoid globals I run into difficulties finding an elegant solution.

For example, VB does not allow a function to return an array. That is, it can return a variant which contains an array, but it cannot return an array of variants (or longs, or strings, or anything else).

Well, first of all I have been avoiding variants, too, because they seem... well, wrong or sloppy somehow. So to avoid using a variant which contains an array, and avoid using a global variable, I set up the function to receive the array as a parameter, ByRef of course.

Then I run into another problem. The calling function doesn't know how big the array needs to be. For example, Let's say I write a class module which connects to a dictionary of words. The class module does all the work of managing the dictionary: provides methods to load dictionaries from files, add words, delete words, return all words which start with a string or contain a string or are anagrams of a string. It even builds its own 2-level large index to optimize speed. You get the idea.

Now, let's say it makes sense for one function in clsDictionary to "return" an array instead of a collection or a recordset or any other method of containing many values. If I declare an array in my calling code, I don't know how big it needs to be, so I Dim it as empty() and figure I can resize it as necessary.

Wrong. You can't ReDim an array in anything but the scope in which it was declared. You don't get a runtime error, but the code simply doesn't work, and VB help confirms that you can't resize arrays which are passed as parameters.

What is wrong with my thinking? Should I just use variants? Should I use globals? Should I mess around with RtlMoveMemory and VarPtr? Should I use collections or recordsets or classes?

If you do chip in to help me out, please understand that I am looking for high level answers. That is, you could give me an idea on how to do what I'm asking, but what I really want is a why. I guess I am asking for strategies, not tactics.

I think this is enough for one question. I was going to give quick bullets of my other 10 burning programming style problems but every time I tried it came out too long, so I'll leave those for another message.

-E

 
Maybe I am not understanding what you are getting at but take a look at this code. As far as I can tell it returns an array of Strings from a function:

Private Sub Command1_Click()
Dim rtn() As String
Dim iX As Long

rtn = ReturnArray(10)
For iX = 0 To UBound(rtn)
Debug.Print rtn(iX)
Next
rtn = ReturnArray(7)
For iX = 0 To UBound(rtn)
Debug.Print rtn(iX)
Next
End Sub

Private Function ReturnArray(ByVal FillTo As Long) As String()
Dim iX As Long
Dim arr() As String

ReDim arr(FillTo)
For iX = 0 To FillTo
arr(iX) = "Added " & CStr(iX)
Next
ReturnArray = arr
End Function


Did I miss something in your question. [neutral]

If you choose to battle wits with the witless be prepared to lose.

[cheers]
 
Hmmm maybe I did miss something, you CAN return arrays. I could swear I tried that syntax. >:-<

However, my requirements go more deep than this.

When you assign one array to another, I assume VB copies the whole thing, and that it's not like a set statement where both variables reference the same data.

But, the calling module needs to be able to modify the array's contents at will, then call the function again which may add more elements with ReDim Preserve.

So, what's the best way to handle that? Or should I use a collection instead?

I am looking both for programming &quot;correctness&quot; as well as for speed. Collections are painfully slow, arrays are faster.

Back to my dictionary example, let's say in my class I store the elements in a single-dimension array of longs. The dictionary can grow or shrink by large amounts. (I am using dictionary in the loose sense of a series of items, be they numbers or strings or objects.)

My strategy for things of this kind in the past has been to decide a chunk size, say 4k. When the dictionary grows to exceed its currently allocated size, redim it 4k larger.

I suppose I could guess at a maximum achievable size and allocate all that from the start, but this seems like a bad thing, and what if I am wrong? 10 kilobytes? 2 megabytes? 10 megabytes? Who knows.

Maybe I am worrying too much about memory and speed... computers are plenty fast, and nowadays they have lots of RAM. But I want to do it right, not quick. Plus, VB does not really lend itself to efficient memory management. Maybe I am better off switching to C and using linked lists or something.

I guess I can't come up with a firm reason why I want to do it this way, but the idea boils down to this:

Create an array of something in a module.
Pass that array to something else that adjusts its size based on data in the array or out of the array.
Inspect the result and do other stuff to it.

Am I just spinning my wheels? I can move on to another burning programming question! :)

-E
 
>How important is it to avoid global variables

The important thing to understand is why people advise against global variables. Once you understand the rules it is easier to appreciate when and why you can break them

>VB does not allow a function to return an array

Yes, it does

>avoiding variants

As with global variables, there are occassions when using a variant is the most elegant solution. Indeed there are one or two occassions in VB where you can't avoid them

>I set up the function to receive the array as a parameter, ByRef of course

A valid approach. Indeed, most of the Windows API calls that return values do so through the parameters passed to them (C/C++ documentation will talk about [in] and [out] parameters, with the actual function return value simply being used to indicate whether an error occurred or not.

The rest of your problems concerning arrays are not entirely valid. As I said earlier (and Foada has demonstrated) VB functions can indeed return arrays. Not only that, it is quite smart about how it does it and will, in fact, resize the receiving array for you. Here's a (pretty horrible) example that, frankly, just reiterates Foada's point:
[tt]
Option Explicit

Private Sub Command1_Click()
Dim myArray() As Long

myArray = GetAnArray()
MsgBox UBound(myArray)
End Sub


Private Function GetAnArray() As Long()
ReDim GetAnArray(5) As Long
End Function
 
Maybe I was confused because I have also been using Access 97. I will check what its capabilities are in this arena compared to VB6 before I ask more questions.

Thank you!

...returning soon to a message thread near you...
 
<quote>The important thing to understand is why people advise against global variables. Once you understand the rules it is easier to appreciate when and why you can break them</quote>

You know i would be interested in getting the exact point of view of this.We've all been told to avoid them,and we've all been faced with reason why we needed one.But for a final showdown sake, why is it considered bad programmation habit?
 
I too would like to know why its considered bad programming habit since there have been times that to me it makes the most reasonable approach.


 
I can at least partially answer that.

Use of global variables can lead to the most spectacular and extremely hard to find bugs.

It's all about scope. When you declare a global variable, everyone can see it, everyone can modify it. When the time comes for you to check its value, there's no telling what's in it.

You can start having conflicts among modules. If you are in the habit of using global variables, and you are trying to reuse code you've written by adding a module to your project, its variables will conflict with the ones you have declared elsewhere. I don't know if you get a duplicate declaration error or if the scope determines which copy is used.

Using global variables almost defeats the purpose of Option Explicit. Let's say one day you use a global variable called Counter. Great. 8 months later, long after you forget that you used this name in this code, you add the code to a project you're working on that just happens to reference Counter, and plus, you forgot to declare it. Well, your code runs fine even with Option Explicit because it IS declared. Things go well for a while. But when you start using the module that Counter is declared in and that code starts changing Counter's value for its own purposes, everything goes kablornk.
 
Indeed, Access 97 doesn't let you return an array from a function. I recently switched to doing VB6 again.

Access 97 VBA also won't let you use AddressOf, or declare variables WithEvents. Grankle!
 
Hey StrongM,

I second the desire to hear more about global variables!
 
There's a special place in H3ll for programmers who don't comment their code.

If you must resort to using public/global variables, for goodness sake, comment your code. This will provide you (or your 'replacement') a little help in debugging the mess you made! ;-)

[fish] No Dolphins were harmed in the posting of this message... Dolphin Friendly Tuna!
 
I agree with ESquared --
Using global variable indiscriminately is just asking for problems.

You can minimize this by naming any global variables appropiately -- using something like &quot;l&quot; as a variable tells you nothing about how it should be used. But using a name like &quot;glNextInvoiceNumber&quot; is more descriptive.

The problems with using something like a &quot;glNextInvoiceNumber&quot; arise when the value is incremented in a GUI (VB form). Since it's possible for code to be reentered in a form, you can end up with this value being incremented several times.

An acceptable use of a global variable would be something like a database connect string -- something that is set once, and never changed. Another good use would be something like a session identifier (for a web app), that gets set on entering the VB code, and never changed for the life of the call chain.

Chip H.


If you want to get the best response to a question, please check out FAQ222-2244 first
 
&quot;Let's say one day you use a global variable called Counter. Great. 8 months later,..&quot;

Cant this situation be avoided by using a proper naming convention? (eg: GL_Counter is a global variable.) Is this the only valid reason for avoiding global variables?

------------------------------------------
The faulty interface lies between the chair and the keyboard.
 
I don't know. I'm sure my answer was only partial... I'm still hoping someone else chips in.
 
The other main reason for not using global variables is when you've multiple threads in your program. This isn't a problem in VB6 (very seldom, actually). But when they move to a language that does support threading if they've acquired bad programming habits, it will bite them in a bad way.

Consider two threads, one that reads a global variable (whose initial value is 32), and another that updates it:
[tt]
ReadThread
PrintInvoice(Invoice, glNextInvoiceNumber)

UpdateThread
glNextInvoiceNumber = glNextInvoiceNumber + 1
[tt]
The question is: What value will be printed on the invoice?

The answer is: You can't tell.
Some times it'll be 32, other times it'll be 33. It all depends on the order in which the two statements get executed. If the OS schedules them one way, you get 32. If the UpdateThread happens to be run first, it'll be 33. Bugs of this type are *extremely* hard to track down, especially if the production PC is a dual-CPU machine and your developer PC isn't.

So, I guess what I'm saying is, I've done multi-threaded programming, and I'm trying to save you some grief. Don't use global variables if you can help it.

Chip H.


If you want to get the best response to a question, please check out FAQ222-2244 first
 
Erik,

I think you were right there. Because when i sat down and thought about it a little, i got the idea that the protection of data (inside a variable) is the most important, and data should be accessed only by members who is privilaged to access them. This will avoid accidental changing of data, which is the same point that you have raised.

Thanks,
Sunil

------------------------------------------
The faulty interface lies between the chair and the keyboard.
 
Erik,

I think you were also right. When i sat down and thought about it a little, i got the idea that data (even if it is inside a variable) is the most important, and only memebers which are privilaged to access them should be permitted to access/modify them. This will avoid accidental alteration of important data, which is the same point that you have raised.

Thanks,
Sunil

------------------------------------------
The faulty interface lies between the chair and the keyboard.
 
Thanks for literally &quot;chipping in,&quot; chiph! :-><

I have several other programming questions, although I'm starting to find coverage of a few of them in some places like online VB magazines. Should I start a new thread or keep going in this one?
 
Since your topic is described broadly, I think the same thread shold include anything that falls within its ambit. But global variables or no gloabls is not a queastion of style. A question of style is one in which one of two or more alternatives may be adopted without significant diffence in effct depending purely upon personal idiosyncracies. It makes a great deal of difference whether variables are global or not ( i.e. if there is a choice ). If variables are global when they need not have been the program is inferior quality because:
o it is harder to grasp as related code is spead out
o it is more error prone as some othewr module ( by &quot;module&quot; here I mean in a purely logical organization sense ) may have changed it for its purely own reasons and no one else will know
o more difficult to upgrade and maintain
 
I find it interesting that your definition of style precludes variations that are radically different.

To me it is quite within the domain of style: one programmer gets along with globals just dandy, another detests them. If they can both get the job done, it becomes a strategy discussion... which tactic is better and why. Once you exit talking about how to implement a single tactic, you've entered style, because each tactic is done essentially the same way by all programmers.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top