From: ray@grumpy.ksc.nasa.gov (Ray Ho)
Newsgroups: fj.mail-lists.x-window
Subject: Too Many Globals; Summary
Date: 14 Apr 92 02:42:05 GMT
Distribution: fj
Organization: MDSSC-KSC

Date: 13 Apr 92 11:44:52 EST
Message-Id: <1992Apr13.114453.2512@titan.ksc.nasa.gov>
Newsgroups: comp.windows.x,comp.windows.x.motif

Thanks to all those who responsed to my posting. I've learned a lot from
those mails. I believe the compiled info can help the whole X industry to
do a much better job.

I guess there will be more late coming responses to me, and some open 
discussions on USENET. I will post another summary if that is the case.

Following are the responses to my question "How to reduce globals" :

----------------------------------------

Form Stephen N. Spencer <sspencer@magnus.acs.ohio-state.edu

I tend to use local variables (for example, for "important" widgets)
and functions which will return the widget:

	Widget toplevel;

	Widget getTopLevel() { return(toplevel); }

I make the function declaration available to all modules, but not the
definition of 'toplevel'. Any function can call "getTopLevel()" and
receive the toplevel widget.

----------------------------------------

From Eric A. Anderson <ea08+@andrew.cmu.edu>

I've found in C++ combined with my library I've been able to get away
with pretty much no global variables, I've used a few to keep argc,
argv in and for a helpfunction, but other than that, you can keep
those things which would have been globals inside of the objects.

----------------------------------------

From David Quarrie <quarrie@cebaf.gov>

I realize that this isn't going to help you at all, but it's possible that a 
C++ encapsulation of motif widgets might allow you to do something similar...

I'm using Eiffel, an OOP, and have written an encapsulation of Motif widgets
using that language. One of the problems I had when using C was exactly the
same as yours - a large number of globals - mainly because of the very 
limited possibilities for passing data to callback routines (which is where
most of the actual work of the application gets done) using the client
data handle. In doing the encapsulation I used the Eiffel object handle as
the client data to the callback routine. The C callback routine then just
passed control onto the corresponding Eiffel callback routine for the 
appropriate object.  Inheritance and redefining the Eiffel callback routine
then allowed the application-specific work to take place.

The particularly nice part about that is that the callback routine becomes 
just another routine within the context of the object and therefore has
access to any other objects and routines within that context.

If none of the above makes any sense at all let me know and I'll try to
explain further!!!

----------------------------------------

From Craig MacFarlane <craigm@chateau-rouge.ICS.UCI.EDU>

Take a look at the book by Booch called "Object Oriented Design", or
something close to that.

----------------------------------------

From Eric D. Taylor <eric@pencom.com>

I have found this to be true. I determined that it is a fundamental
problem of a procedure based language like C. Object oriented
language tend to make this problem easier to address.

I have found however, that no matter how messy it gets, it pays
to eliminate ALL global variables and start thinking in terms
of "contexts".

----------------------------------------

From Anita Hsiung <ahsiung@BBN.COM>

Nope. X/Motif is notorious for forcing the programmer to use lots
and lots of global variables. Part of this problem is because the
nature of X and window systems in general is that they are
EVENT-DRIVEN packages, not sequentially or logically driven packages.
You have a window that waits for input, it gets input, it does
processing. If you have a checkbox says "are you sure about
nnnnn?", you need globals to check what input the user gave. So, the
problem is inherent.

One thing we have found is if you make AS MANY FUNCTIONS AS YOU CAN
inside any one file to be static, then you can cut down on the number
of global variables. For example, if each file has an OK button, you
can make a function called OKButtonCallback static and thus not have
functions hanging all over the place being called by everyone. Does
this make sense? It's been a long day. Anyway, you're not the only
one with the problem, but the number of globals can be cut down. I
think we only had less than 20 global variables in the last project I
worked on (instead of the 50 or so on my first project with X).

----------------------------------------

From Marc Andreessen <marca@ncsa.uiuc.edu>

Personally, I use lots of variables that are static to one file (but
global within that file), and than provide functions to set/retrieve
those variables in that file that are available externally. (This is
with C.)

To avoid global variables altogether (even within files), what you
can do is have one big struct/class (call it a ProgramStruct) that holds
everything you consider global, create it(probably via a constructor)
in your main(), pass it to all the function main calls, have those
functions pass it to the functions_they_call, and so on. This would
work with both OOP and non-OOP, the big difference probably being whether
functions operate directly on that struct or go through methods for the
class.

Just some random thoughts after a busy day.

----------------------------------------

From Victor Kan  <kan@dg-rtp.dg.com> 

We used to use a huge global object that held pointers to the main
parts of our static GUI and we'd almost always pass a pointer to that
global object through the client_data into callbacks.  We've pretty
much eliminated most of the global structure without too much
difficulty (wrt keeping things from getting messy).

Looking at example programs that come with motif, e.g. Motifburger,
you'd think that global variables and lots of constants are God's gift
to Motif programmers (more likely a bad side effect of being a Uil
application).  But it really isn't necessary, especially if you use
OOP (C++ in particular).

Basically, we use generic callbacks, passing in C++ object pointers as
client data and have those callbacks do the right thing by invoking
methods depending on the class of the C++ object.  Essentially, it's
implementing a virtual function table in C since Motif doesn't work
very well with C++ (at least not the versions of C++ and Motif
libraries that we're using) in a native style of classes, etc.

We let the C++ objects (which wrap around regular C Motif objects)
store what they need to do and what data they need.

I mentioned that our GUI used to be pretty static.  Now, we've gone
dynamic and reference objects by name as well as pointer.  Mapping
between the two is pretty cheap, just a dictionary lookup for
name->pointer and one method invokation on pointer->name.

----------------------------------------

From Brett Kettering <KETTERING@ADDVAX.llnl.gov>

I have never seen the need to have a global variable.  True there are times
where you would want access to a variable and in this case I export a function
that allows the user to get a copy of the variable.  In the event that the
variable needs to have a value changed I would export a function that allows
the user to change its value, but how and to what values are under my control,
not the users.  I don't think I would allow the user to directly get his hands
on it so that its address can be passed around.

Generally, when I write code I have the following files:

	file.c			The C source

	file.h			The header containing all variable and function
				declarations.  Any function that is not
				exported is declared static and all variables
				are static.

	file_constants.h	All the constant (read #define) declarations
				that are used and should be known both
				externally and internally.

	file_externals.h	All the function declarations that the user
				of file.c can call.

	file_types.h		Any type definitions that must be known both
				externally and internally.

In case you can't tell I try to write my files in an ADT (Abstract Data Type)
sort of way, an OO type idea.  This mechanism is directly supported and
enforced in Ada, the language I prefer, in case you couldn't tell.  In this
manner I can in some fashion deliver the same type of interface, but not
enforced by the language.

If you take a look at the include files for X you'll see stuff like
Intrinsic.h and IntrinsicP.h.  The "P" stands for private, but they really
have no way, other than asking the user "please" to disallow the use of
the types, variables, and functions in the "P" file.

Just my $0.02 worth.

----------------------------------------

From: Jim Wight <J.K.Wight@newcastle.ac.uk>

How have you tried to eliminate them? I have started tackling the
problem in Athena Widget programs by deriving custom widgets and
turning the global variables into class or instance variables.  I
don't consider the outcome to be messier. Furthermore, deriving new
widgets for Intrinsics based toolkits has turned out to be not nearly
as difficult as I had somehow feared it would be.

----------------------------------------

From: Stephen L Favor <xcpslf@pdssrv.oryx.com>

because of the tree structure of X11/Motif, the only globals you must have are 
any toplevel widgets.  because all other Widgets are children/popup children of 
the toplevel widget, you may access them by XtNameToWidget() if you have any of
their ancestors.

of course, the toplevel shell is an ancestor of every widget in the program.

note: name specification for XtNameToWidget() is often tricky. this is 
especially true in a large application where there may be duplicated names.  
this problem may be avoided as long as you know a specific, unique dotted name 
for a widget.

there are a host of other Xt...() functions that may be used to obtain Motif or
X resources once you have a widget.

if you ever get the knack of this, it will greatly reduce debugging efforts 
because most of your routines will access other widgets by names instead of 
pointers (which can be bogus).  you might also look into using action procedures
instead of event handlers and (sometimes) callbacks to further modularize 
programs.  the main reason for all of this is that it is easier to spot a 
misspelled action name or widget name than it is to figuire out that you wrote 
over the widget's address space because you didn't /shouldn't have/did 
dereference that pointer...

the only major drawback to this is overhead, but as long as you're not on a 386
or referencing a widget by name 1000 times a second, i've not seen a significant
degradation in performance.

in general, i try to avoid globals or use static globals unless there is no 
other way around it.

----------------------------------------
From Kee Hinckley <nazgul@alfalfa.com>

I guess I need some examples.  What are you using global variables for? 
I don't think that we have more than four or five in our code.

----------------------------------------

From David E. Smyth < David.Smyth@sniap.mchp.sni.de >

You probably do not need globals at all.  Perhaps you can make
the display or top level shell global, but I do not.  Sometimes
I use statics, but never globals.

The way to get to your application objects from a callback is to
use the XContextManager fucntions XSaveContext, XUNiqueContext,
XFindContext.  

Keep this concept celarly in mind: the user interface directly
represents the objects within the application.  A tool for playing
with a file will present the attributes of the file to the user
via the user interface, and the user interface will allow methods
ON A SPECIFIC "file" object to be invoked.  A button, then, will
invoke the methods on the object represented by the user interface
of which the button is a part.

Lets say you have an interface which shows a bunch of files.  You
select one, then you get a pop-up for manipulated that one file.
The popupshell maps 1:1 with the "file" object you will manipulate.
The shell may have a button for each method available: copy, delete,
set permission flags, edit.  When you press the button, you can 
go up to the shell widget, and use the shell widget with XFindContext
to determine which "file" the button is manipulating, then 
invoke the method of that object:

object = CreateObject(...);
shell = CreateUserInterfaceForObejct(...);
XSaveContext( dpy, XtWindow(shell), (XContext)0, (caddr_t)object );

void OpenCallback( Widget w, XtPointer client, XtPointer call )
{
    Object* object;
    while ( !(XtIsSubclass(w,shellWidgetClass) )
	w = XtParent(w);
    XFindContext(dpy, XtWindow(w), (XContext)0, &object );
    OpenFile(object);
}


If you are not developing an object oriented application, I do not
know how to do it.  Well, I guess you just hack away.  I don't
know of any basic theory or methodology for doing it easily.
Certainly structured programming is hopeless.

Go to Xhibition, attend my tutorial.  Maybe I can send you
some examples sometime.  Not right now, too busy.

--------------------------------

From Niels P. Mayer <mayer@hplnpm1.hpl.hp.com>


We use WINTERP (OSF/Motif Widget INTERPreter) to implement our Motif
applications in an object oriented fashion -- WINTERP/XLISP's
object-oriented extensions allow you to create properly encapsulated
programs that can be entirely free of global variables.

I don't have alot of time to talk about it, but below, I'm posting

(1) Blurb on WINTERP, availability (free), etc. If you pick up the WINTERP
documentation/examples/sources by anonymous ftp, you can read about some of
the object oriented capabilities of WINTERP.

Papers on WINTERP (discussing it's OO capabilities too) are available in
the ftp distribution of WINTERP. The latest published paper on WINTERP is
in the proceedings of the Motif '91 conference held in Washington DC,
December '91.

(2) My WINTERP solution to Tom LaStrange's recent "toolkit challenge"
posted to comp.windows.x et al -- this solution uses no global variables
and indicates the kind of design that is possible with WINTERP. You can use
the WINTERP user interface language to subclass existing widgets into
application-specific classes; these new widget classes can contain (local)
instance variables for dealing with any references to subentities in the
subclass; message passing takes care of performing computations on the
subclasses.

IMHO, it is impossible to do an equivalently- clean, -extensible, and
-well-encapsulated version of the same problem in C+Motif, UIL+C, WCL+C,
etc.

				--------------------


(1)
==============================================================================

WINTERP: An object-oriented rapid prototyping, development and delivery
environment for building user-customizable applications with the OSF/Motif
UI Toolkit.

------------------------------------------------------------------------------

WINTERP is a Widget INTERPreter, an application development environment
enabling rapid prototyping of graphical user-interfaces (GUI) through the
interactive programmatic manipulation of user interface objects and their
attached actions. The interpreter, based on David Betz's XLISP, provides an
interface to the X11 toolkit Intrinsics (Xtk), the OSF/Motif widget set,
primitives for collecting data from UN*X processes, and facilities for
interacting with other UN*X processes. WINTERP thus supports rapid
prototyping of GUI-based applications by allowing the user to interactively
change both the UI appearance and application functionality. These features
make WINTERP a good tool for learning and experimenting with the
capabilities of the OSF/Motif UI toolkit, allowing UI designers to more
easily play "what if" games with different interface styles.

WINTERP is also an excellent platform for delivering extensible or
customizable applications. By embedding a small, efficient language
interpreter with UI primitives within the delivered application, users and
system integrators can tailor the static and dynamic layout of the UI,
UI-to-application dialogue, and application functionality. WINTERP's use of
a real programming language for customization allows WINTERP-based
applications to be much more flexible than applications using customization
schemes provided by the X resource database or OSF/Motif's UIL (user
interface language).

An environment similar to WINTERP's already exists in the Gnu-Emacs text
editor -- WINTERP was strongly influenced by Gnu-Emacs' successful design.
In Gnu-Emacs, a mini-Lisp interpreter is used to extend the editor to
provide text-browser style interfaces to a number of UN*X applications
(e.g. e-mail user agents, directory browsers, debuggers, etc). Whereas
Emacs-Lisp enables the creation of new applications by tying together
C-implemented primitives operating on text-buffer UI objects, WINTERP-Lisp
ties together operations on graphical UI objects implemented by the Motif
widgets. Both achieve a high degree of customizability that is common for
systems implemented in Lisp, while still attaining the speed of execution
and (relatively) small size associated with C-implemented applications.

Other features:
	* WINTERP is free software -- available via anonymous ftp from
	  export.lcs.mit.edu.
	* Portable -- runs without porting on many Unix systems.	
	* Interface to gnuemacs' lisp-mode allows code to be developed
	  and tested without leaving the editor;
	* Built-in RPC mechanism for inter-application communications;
	* XLISP provides a simple Smalltalk-like object system.
	* OSF/Motif widgets are real XLISP objects -- widgets can be
	  specialized via subclassing, methods added or altered, etc.
	* Automatic storage management of Motif/Xt/X data.
	* Contains facilities for "direct manipulation" of UI components;

You may obtain the latest released version of the WINTERP source,
documentation, and examples via anonymous ftp from internet host
export.lcs.mit.edu (18.30.0.238): in directory contrib/winterp you will
find the compress(1)'d tar(1) file winterp-???.tar.Z. (??? represents the
version number). Slides, papers and further documentation can be found in
directory contrib/winterp/papers.

If you do not have Internet access you may request the source code to be
mailed to you by sending a message to winterp-source%hplnpm@hplabs.hp.com
or hplabs!hplnpm!winterp-source.

There is also a mailing list for WINTERP-related announcements and
discussions. To get added to the list, send mail to
winterp-request%hplnpm@hplabs.hp.com or hplabs!hplnpm!winterp-request.

For discussions about XLISP, see the USENET newsgroup comp.lang.lisp.x.

----------------------------------------