C'mon, Kaz, you know better. Usually, an instance inhabits a
`static' somewhere (so its name is not available outside the scope
of its keeper), or it inhabits dynamically allocated memory (which
has no name at all).
True story: A former employer's flagship product did things with
documents, and the earliest versions could handle only one document
at a time. THE document was, fairly naturally, described by a host
of global variables: What are THE page margins, what is THE set of
paragraph styles, what is THE associated file name, and so on. At
some point (before I joined), the product was extended to handle
multiple documents simultaneously -- but by then, all those globals
had infiltrated themselves into too many places to extricate; there
just Was Not Going To Be an attempt to track down every reference to
every global and route it through a pointer instead, nor to inflate
all the functions and function calls to pass the pointer around.
Where's the problem? Write a tool that notices when a function uses one
of the globals (you surely had at least a list of them, hadn't you?).
Then rewrite the function prototype in the header, if any, and the
function declaration in the code file so that it takes a pointer to a
struct as first argument and references the globals through that
pointer. Then remove any _definition_ of the globals and bundle them in
said struct. For starters, define one static instance of the struct
somewhere, but remove it later after inclusion of multiple-instance code.
By now, every old usage of the functions should result in a compiler
error or warning, and every usage of the globals should result in a
linker error. Correct those and you are nearly done. Now you only need
to find leftover locally defined static-duration variables that only
work for one document and eradicate those. Done!
Solution? The overarching framework noticed when the user moved
from Document A to Document B, and swapped things around to make it
work. It saved all the globals for A into one big struct, and then
restored all their values from B's struct (build-time tools created
the struct definition and the save/restore code, with a little help
from source-code markers to identify the globals). When the user then
moved to Document C, B's globals were squirrelled away and C's values
overwrote them.
So you had to create the struct anyway already!
By the time I left that employer, the size of the automatically-
generated struct and of the save/restore functions had grown to the
point where some platforms' compilers could no longer handle them,
How is that possible? You would need to break some hard limit in the
compiler, like 64KB on a 16-bit machine. You had 64KB worth of globals?
and the tools had been modified to break the struct into three, with
three sets of functions. Also, just moving your mouse from Window A
to Window B could drive your paging disk berserk as it tried to handle
all these references to globals scattered hither and yon all over the
address space; you could force your workstation to its knees just by
flicking your mouse back and forth on the screen.
At that point the "solution" has become unviable and another solution
must be sought. But, well, that's gonna cost money, right?
One solution would have been to leave the program in its state with all
the globals and just put a wrapper there to handle multiple documents
with multiple processes. Then the OS can worry about the swap drive,
with copy-on-write pages and such.
That's the power of globals.
I take two lessons from this experience: First, sufficient ingenuity
and a willingness to hack can come up with a short-term solution to most
any problem. Second, short-term solutions have little staying power.
Good design helps, too: If the company had worked object-orientedly from
the start (which would have resulted in a program that used those
structs from the start), much money could have been saved. But of
course, there would have been more money needed to be spent before the
first release.
What do we learn? Global variables are usually a hack that can be
removed by good design and a good compiler. Seriously, a compiler with
link time optimization should not yield worse results with a structured
global than with individual globals.
Ciao,
Markus