hsu@stratus (Yung-Kao Hsu) (09/07/88)
Hi, I am a new subscriber to this group and a semi-new user of GNU C++ (about 2 months). I am interested in learning whether it is possible to make the private part of a class definition invisible ? In Ada, a user-defined data structures can be made invisible by decalaring their types as "private". While in Modula-2, you can do the same thing by exporting the name of the type but not its definition. The data structures are then declared in the body of the packages or implementation modules. Besides the type information, these two languages also allow you to hide the variables associated with the packages and definition modules. This features make package/module interfaces cleaner (you don't see any implementation information at all), and there are more advantages. But, I couldn't find anything similiar in C++ !! To make my question clearer, consider the following C++ class definition: class X { int x[10]; \\ private decalarations public: W,X, ... }; My question: is there anyway I can write this definition as: class X { public: W,X, ... }; while declaring "int x[10]" elsewhere (in separate file that is not accessable by users of this class). Can anyone answer me ? Yung. ------
clive@drutx.ATT.COM (Clive Steward) (09/28/88)
From article <1358@stratus>, by hsu@stratus (Yung-Kao Hsu): > > Hi, I am a new subscriber to this group and a semi-new user of GNU C++ > (about 2 months). I am interested in learning whether it is possible to make > the private part of a class definition invisible ? > It doesn't seem you can make anything truly invisible to the user of a class without a customized preprocessor, because they're always going to be able to read the header someplace if the compiler system can. But one could probably simplify the 'user interface' by doing something like: in privatestuff.h: #define FoosPrivates private:\ int foo1; \ char *foo2; /* etc */ /* be sure not to use any // C++ comments anywhere in this macro, as C preprocessor won't understand them... */ then in your main header file for the class: #include "privatestuff.h" class foo { FoosPrivates; // yes, the ; is unnecessary, but looks better? public: // etc. };
henry@utzoo.uucp (Henry Spencer) (09/29/88)
In article <1358@stratus> hsu@stratus (Yung-Kao Hsu) writes: >This features make package/module interfaces cleaner (you don't see any >implementation information at all), and there are more advantages. >But, I couldn't find anything similiar in C++ !! The reason is that these features hurt code efficiency. The user does not, in any case, get much benefit from seeing those implementation details, because he can't make much use of them... but the compiler can and does. Consider your example: > class X { int x[10]; \\ private decalarations > public: > W,X, ... > }; The user can't access x, so seeing it won't do him much good. However, the compiler now knows exactly how big a variable of class X is, so it can generate code that knows this. If those private declarations were hidden somewhere else, either the compiler has to get much more complex or else class variables can't be as efficient as ordinary variables. For example, as I recall (it's been a while...), since Modula 2 doesn't tell the compiler how big the variable is, all the compiler can do for space allocation is to allocate a pointer to the variable, and do all accesses via that pointer. This can be pretty slow. The advantages of the C++ method are even more prominent when you have something like: class foo { int x; public: int twice() { return 2*x; } } myvariable; In C++, the code that results from using "twice" is essentially "2 * myvariable.x"; note that there is no function call needed! In Modula 2, you can't do this without the full function-call overhead. Remember, C++ is aimed primarily at the C community, which has a long tradition of caring A LOT about efficiency. There are many, many people who are willing to adopt C++ only because they can be fairly sure that they aren't losing significant efficiency by doing so. C++, like C, specializes in being fast rather than pretty. -- The meek can have the Earth; | Henry Spencer at U of Toronto Zoology the rest of us have other plans.|uunet!attcan!utzoo!henry henry@zoo.toronto.edu
shap@polya.Stanford.EDU (Jonathan S. Shapiro) (10/01/88)
In article <1988Sep29.044111.16104@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes: >In article <1358@stratus> hsu@stratus (Yung-Kao Hsu) writes: >>This features make package/module interfaces cleaner (you don't see any >>implementation information at all), and there are more advantages. >>But, I couldn't find anything similiar in C++ !! > >The user can't access x, so seeing it won't do him much good. However, >the compiler now knows exactly how big a variable of class X is, so it >can generate code that knows this. If those private declarations were >hidden somewhere else, either the compiler has to get much more complex >or else class variables can't be as efficient as ordinary variables. >For example, as I recall (it's been a while...), since Modula 2 doesn't >tell the compiler how big the variable is, all the compiler can do for >space allocation is to allocate a pointer to the variable, and do all >accesses via that pointer. All of what you say is true and good, and I think the tradeoff is worth it, but it is worth pointing out on the other side that a consequence of unifying the private and public parts of the code are an increase in the header dependencies that can get explosive. Consider having a private member of type froboz. Even if there is no public member of type froboz, all of the includers of the class definition have to know what a froboz is - EVEN though they can't make any use of the relevant member. As someone who has developed large C++ systems, I have had occasion to sadly regret this fact. On the other hand, I am more concerned with the fact that there is NO way out of the run time cost in modula. One technique that I have seen used to avoid this is to make all of the private members part of a structure and declare the private part as a structure reference. This is cumbersome to the coder, but in a big system can be worth it. Then when you have completed development you make it an immediate object and insert a #include in the class def header file. The flip side of this is that what you say about both languages is wrong given a good librarian. The librarian could maintain the component sizes, and at least for the client functions that would eliminate unneeded dependency in both languages, subject to the constraint that the private part be compiled first. Jon
henry@utzoo.uucp (Henry Spencer) (10/02/88)
In article <4193@polya.Stanford.EDU> shap@polya.Stanford.EDU (Jonathan S. Shapiro) writes: >The flip side of this is that what you say about both languages is >wrong given a good librarian. The librarian could maintain the >component sizes, and at least for the client functions that would >eliminate unneeded dependency in both languages, subject to the >constraint that the private part be compiled first. Um, no, it doesn't eliminate the dependencies, it just hides them. If you change the size of something, everything that knows about it still has to be recompiled. It's essentially the equivalent of synthesizing the *real* header file from a fake one (with no private information) and the private part. Remember, too, that sizes are the easy part -- things like inline functions are both more significant and harder to do. -- The meek can have the Earth; | Henry Spencer at U of Toronto Zoology the rest of us have other plans.|uunet!attcan!utzoo!henry henry@zoo.toronto.edu
akwright@watdragon.waterloo.edu (Andrew K. Wright) (10/02/88)
In article <4193@polya.Stanford.EDU> shap@polya.Stanford.EDU (Jonathan S. Shapiro) writes: >The flip side of this is that what you say about both languages is >wrong given a good librarian. The librarian could maintain the >component sizes, and at least for the client functions that would >eliminate unneeded dependency in both languages, subject to the >constraint that the private part be compiled first. A problem I see here is a stubborn adherence by many people to the conventional C-like or Ada-like view of separate compilation. Since the public part of a class can be generated automatically from the private part, why force the user to enter it at all? The compilation system (souped-up librarian) can determine it when compiling a program, and a tool can be provided to produce it for a reader. Eiffel is one existing OO language which operates this way. The research languages I have been working on for several years all operate this way. -- Andrew K. Wright watmath!akwright CS Dept., University of Waterloo.
hsu@cumulus (Yung-Kao Hsu) (10/03/88)
--------- In article <1988Sep29.044111.16104@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes: >In article <1358@stratus> hsu@stratus (Yung-Kao Hsu) writes: >>This features make package/module interfaces cleaner (you don't see any >>implementation information at all), and there are more advantages. >>But, I couldn't find anything similiar in C++ !! > >The user can't access x, so seeing it won't do him much good. However, This is true if all public functions won't return any pointer to x. But, sometimes for efficiency reason, we will allow such bad pratice. For example, if x is a table whose element is defined as an union of 5 different data types and there are 5 different classes that defined to handle these data types. Since the main function for the object containing x is record keeping, pointers seems to be the best choices for both efficiency and program readability in designing the interfaces between these classes. But, since we can see the definition and with pointers, nothing is safe. Anyway, I agree that what you said in general is right. In article <1988Sep29.044111.16104@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) continues: >the compiler now knows exactly how big a variable of class X is, so it >can generate code that knows this. If those private declarations were >hidden somewhere else, either the compiler has to get much more complex >or else class variables can't be as efficient as ordinary variables. >For example, as I recall (it's been a while...), since Modula 2 doesn't >tell the compiler how big the variable is, all the compiler can do for >space allocation is to allocate a pointer to the variable, and do all >accesses via that pointer. In article <4193@polya.Stanford.EDU> shap@polya.Stanford.EDU (Jonathan S. Shapiro)replied: : All of what you say is true and good, and I think the tradeoff is : worth it, but it is worth pointing out on the other side that a : consequence of unifying the private and public parts of the code are : an increase in the header dependencies that can get explosive. : Consider having a private member of type froboz. Even if there is no : public member of type froboz, all of the includers of the class : definition have to know what a froboz is - EVEN though they can't make : any use of the relevant member. The problem Jon mentioned above was in fact the main reason for my initial question. I have devloped a very simple compiler in G++ as a framework for project assignment to be used in compiler course my advisor taught. But, I simply couldn't write a proper makefile to make seperate compilation possible. The header dependency simply forces re-compilation of all program units if some private declarations were changed (which shouldn't cause any problem to any other classes). Even though Jon had a semi-solution to this problem, but I think it is too human-intensive (for undergraduate students at least). Anyway, thanks to both Jon and Henry for your information. -- YUNG-KAO HSU School Of Infomation & Computer Science, Georgia Tech Atlanta GA30332 Internet: hsu@cumulus.gatech.edu CSNet: hsu%cumulus@gatech UUCP: ...!{akgua,allegra,amd,hplabs,seismo,ihnp4}!gatech!cumulus!hsu
ekrell@hector.UUCP (Eduardo Krell) (10/03/88)
In article <1368@cumulus> hsu@cumulus.UUCP (Yung-Kao Hsu) writes: >The header dependency simply forces re-compilation of all program >units if some private declarations were changed (which shouldn't cause any >problem to any other classes). You can't really say that. What if something like sizeof() was used? Adding or removing private members from a class may change its size. Eduardo Krell AT&T Bell Laboratories, Murray Hill, NJ UUCP: {att,decvax,ucbvax}!ulysses!ekrell Internet: ekrell@ulysses.att.com
shap@polya.Stanford.EDU (Jonathan S. Shapiro) (10/03/88)
In article <1368@cumulus> hsu@cumulus.UUCP (Yung-Kao Hsu) writes: >--------- > >In article <1988Sep29.044111.16104@utzoo.uucp> henry@utzoo.uucp >(Henry Spencer) writes: > >>The user can't access x, so seeing it won't do him much good. However, > >This is true if all public functions won't return any pointer to x. >But, sometimes for efficiency reason, we will allow such bad pratice. The fact that a programmer can willfully break the type system is not a good argument here. I can also use integer pointers as character pointers. If the function returning pointer wants to be type-safe, it should be declared as returning a pointer to a class member. In that case, what Henry says continues to be true. Jon
hsu@cumulus (Yung-Kao Hsu) (10/03/88)
In article <10681@ulysses.homer.nj.att.com> ekrell@hector.UUCP (Eduardo Krell) writes: >In article <1368@cumulus> hsu@cumulus.UUCP (Yung-Kao Hsu) writes: > >>The header dependency simply forces re-compilation of all program >>units if some private declarations were changed (which shouldn't cause any >>problem to any other classes). > >You can't really say that. What if something like sizeof() was used? >Adding or removing private members from a class may change its size. > >Eduardo Krell AT&T Bell Laboratories, Murray Hill, NJ > >UUCP: {att,decvax,ucbvax}!ulysses!ekrell Internet: ekrell@ulysses.att.com The problem you mentioned above really only hit half of the target. If the sizeof() was used, the data type involved really is not private as far as I can see. If you consider the example I mentioned in the posting, i.e, an object that handles a table of values. The changes of table size should not affect those object who use it, since they are only interested in storing and retrieve data elements. But the changes of the table element size should, since the data element type is part of interface specification. But, C++ simply does not support any mechansim like ada and modula-2 that help in distincting these situations. Thus, in most cases, you have to take care of what should be recompiled and what needs not. I feel this is a very serious problem if we want to use C++ in a large project or consider it as an object-oriented language. --------- -- YUNG-KAO HSU School Of Infomation & Computer Science, Georgia Tech Atlanta GA30332 Internet: hsu@cumulus.gatech.edu CSNet: hsu%cumulus@gatech UUCP: ...!{akgua,allegra,amd,hplabs,seismo,ihnp4}!gatech!cumulus!hsu
hsu@cumulus (Yung-Kao Hsu) (10/03/88)
In article <4223@polya.Stanford.EDU* shap@polya.Stanford.EDU (Jonathan S. Shapiro) writes: *In article <1368@cumulus> hsu@cumulus.UUCP (Yung-Kao Hsu) writes: ** **In article <1988Sep29.044111.16104@utzoo.uucp* henry@utzoo.uucp **(Henry Spencer) writes: ** ***The user can't access x, so seeing it won't do him much good. However, ** **This is true if all public functions won't return any pointer to x. **But, sometimes for efficiency reason, we will allow such bad pratice. * *The fact that a programmer can willfully break the type system is not *a good argument here. I can also use integer pointers as character *pointers. If the function returning pointer wants to be type-safe, *it should be declared as returning a pointer to a class member. In *that case, what Henry says continues to be true. * *Jon What I really should say in the previous posting is: the language should provide some mechanisms (i.e, hiding the data type definition) that prevent the programmer from mis-use even they want to. Also, your example is not quite convincing, since character and integer are not considered as "private" types but pre-defined public data types and we all know how they are implemented. ----- -- YUNG-KAO HSU School Of Infomation & Computer Science, Georgia Tech Atlanta GA30332 Internet: hsu@cumulus.gatech.edu CSNet: hsu%cumulus@gatech UUCP: ...!{akgua,allegra,amd,hplabs,seismo,ihnp4}!gatech!cumulus!hsu
david@june.cs.washington.edu (David Callahan) (10/04/88)
In article <1988Oct2.045604.17456@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes: >In article <4193@polya.Stanford.EDU> shap@polya.Stanford.EDU (Jonathan S. Shapiro) writes: >>The flip side of this is that what you say about both languages is >>wrong given a good librarian. ... > >Um, no, it doesn't eliminate the dependencies, it just hides them. If >you change the size of something, everything that knows about it still >has to be recompiled. ... Changing the size or position of private elements changes only the offsets of the public elements. These could be treated as external variables and bound at link time rather than compile time. There might small performance penalty if a particular architecture handles small offsets better than big ones and the compiler must assume a big offset. This goes for sizeof() too. >Remember, too, that sizes are the easy part -- things like inline functions >are both more significant and harder to do. >-- >The meek can have the Earth; | Henry Spencer at U of Toronto Zoology >the rest of us have other plans.|uunet!attcan!utzoo!henry henry@zoo.toronto.edu Inline functions are real compilation dependences and can not be hidden by late binding, but the orginal poster's intent was to hide implementation details, not avoid recompilation when public information changes, and that is certainly posible. David Callahan
ekrell@hector.UUCP (Eduardo Krell) (10/04/88)
In article <1369@cumulus> hsu@cumulus.UUCP (Yung-Kao Hsu) writes: >The problem you mentioned above really only hit half of the target. If the >sizeof() was used, the data type involved really is not private as far as >I can see. I didn't explain myself. I meant to say that anytime the size of the class is needed (e.g., when you generate an instance of the class), that module will have to be recompiled. You don't have to use sizeof() directly, but the compiler will need it to generate the right argument for malloc() when you create an object of that class. And there's more. You can move members around and add/remove private members. That could change the relative offsets of members in the class. A program that was using object->member now needs to be recompiled because the offset of "member" might have changed... Eduardo Krell AT&T Bell Laboratories, Murray Hill, NJ UUCP: {att,decvax,ucbvax}!ulysses!ekrell Internet: ekrell@ulysses.att.com
ekrell@hector.UUCP (Eduardo Krell) (10/04/88)
In article <5922@june.cs.washington.edu> david@uw-june.UUCP (David Callahan) writes: >Changing the size or position of private elements changes only the >offsets of the public elements. These could be treated as external >variables and bound at link time rather than compile time. This is a bad idea as it would break all the C++ <-> C compatibility. Imagine a C++ program calling stat() and then trying to access the elements of the stat structure returned by the kernel ... Eduardo Krell AT&T Bell Laboratories, Murray Hill, NJ UUCP: {att,decvax,ucbvax}!ulysses!ekrell Internet: ekrell@ulysses.att.com
robert@pvab.UUCP (Robert Claeson) (10/04/88)
In article <4193@polya.Stanford.EDU>, shap@polya.Stanford.EDU (Jonathan S. Shapiro) writes: [discussion about how to hide the private part of a class declaration deleted] > ... Then when you have completed development > you make it an immediate object and insert a #include in the class def > header file. Sometime during this last winter, someone from Glockenspiel, Ireland, wrote something on this subject in this newsgroup (John Carolan, please step forward). The article wasn't very long, but was well worth reading. I'm afraid I have lost it. And yes, his method included the use of a pointer. -- Robert Claeson, ERBE DATA AB, P.O. Box 77, S-175 22 Jarfalla, Sweden Tel: +46 758-202 50 Fax: +46 758-197 20 Email: robert@pvab.se (soon rclaeson@erbe.se)
ech@poseidon.UUCP (Edward C Horvath) (10/05/88)
Henry Spencer observes: > Remember, C++ is aimed primarily at the C community, which has a long > tradition of caring A LOT about efficiency. There are many, many people > who are willing to adopt C++ only because they can be fairly sure that > they aren't losing significant efficiency by doing so. C++, like C, > specializes in being fast rather than pretty. Deja vu. In the mid-70s, Berk Tague (the first manager of the Bell Labs Unix Support Group) said that he didn't really believe that the choice of language was important. However, C was the only language he had found that would woo the diehards away from assembler. I suppose I'm a diehard, too. I suspect that stems from 20 years of hearing the same argument that "the next generation of hardware will let {LISP, SNOBOL, shell scripts, smalltalk, your language here} run fast enough to obviate the efficiency issues." And 20 years of seeing the demands placed on the programs increase as fast as the hardware power. And 20 years of seeing the efficiency freaks eat up the market share. So I still code the innermost loops in assembler. The reality is that each new level of abstraction in the programming process has to prove itself ECONOMICALLY. I hear the arguments about how much more efficient object-oriented programming is, in terms of reuse of code and better maintainability. But if there's an efficiency penalty, it's going to cost me market share EARLY, and maintainability is moot to a company that's folded. When a 5- or 10 year old software product team reports the continuing evolutionary quality of their product, and the reduced maintenance costs, without an efficiency penalty, everybody will jump on the bandwagon (or already be beyond it). But end-users just can't see the internal cleanliness, although they've become better about demanding timely updates and fast response to bug reports. Bjarne Stroustroup clearly recognizes these issues. The C++ book apologizes, and properly so, for the "unclean" aspects of the language. His apologia, however, almost invariably involve allowing the compiler to do a better job of generating code. The result is a compromise, enlightened by the SmallTalk experience, but tempered by a recognition of the reality "real" programmers face. I'm switching to C++ just as soon as I have a compiler for my Mac (maybe even when there's a preprocessor, we'll see), because it looks like it will help me more than it will cost me. I'm not switching to SmallTalk, it just isn't fast enough for production-quality applications. Ultimately, I do believe that we'll program in an evolutionary fashion that exploits the strengths of what-has-gone-before. I have not been persuaded that we know enough to construct that ultimate programming environment. But I've started reading about Eiffel... =Ned Horvath=
chris@mimsy.UUCP (Chris Torek) (10/05/88)
-In article <4223@polya.Stanford.EDU* shap@polya.Stanford.EDU
-(Jonathan S. Shapiro) suggests that
->The fact that a programmer can willfully break the type system is not
->a good argument here. I can also use integer pointers as character
->pointers. If the function returning pointer wants to be type-safe,
->it should be declared as returning a pointer to a class member. In
->that case, what Henry says continues to be true.
In article <1375@cumulus> hsu@cumulus (Yung-Kao Hsu) answers:
-What I really should say in the previous posting is: the language should
-provide some mechanisms (i.e, hiding the data type definition) that prevent
-the programmer from mis-use even they want to.
It cannot be done. If the programmer is curious enough, or desperate
enough, he will physically disassemble the machine in order to make it
do what he wants. I know, for I have done it. Far better than making
it hard to do it wrong is making it easy to do it right.
-Also, your example is not quite convincing, since character and integer
-are not considered as "private" types but pre-defined public data types
-and we all know how they are implemented.
Except that many people who `know' this are in fact wrong. The analogy
seems reasonably apt to me.
--
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
henry@utzoo.uucp (Henry Spencer) (10/06/88)
In article <5922@june.cs.washington.edu> david@uw-june.UUCP (David Callahan) writes: >Changing the size or position of private elements changes only the >offsets of the public elements. These could be treated as external >variables and bound at link time rather than compile time. There might >small performance penalty... There will be a large performance penalty on many modern machines, unless the linker is smart enough to practically redo the code generation to compensate. Things like limited-range offset addressing modes, which are very common, really do require knowing the offset at code-generation time to avoid having to generate much slower worst-case code. >Inline functions are real compilation dependences and can not be hidden by late >binding, but the orginal poster's intent was to hide implementation details, >not avoid recompilation when public information changes, Inline functions are an optimization; their contents are no more "public information" than the types and sizes of private members. -- The meek can have the Earth; | Henry Spencer at U of Toronto Zoology the rest of us have other plans.|uunet!attcan!utzoo!henry henry@zoo.toronto.edu
jellinghaus-robert@CS.YALE.EDU (Rob Jellinghaus) (10/07/88)
In article <528@poseidon.UUCP> ech@poseidon.UUCP (XMRJ50000-Edward C Horvath;LZ 3F-315;3005) writes: >Ultimately, I do believe that we'll program in an evolutionary fashion >that exploits the strengths of what-has-gone-before. I have not been >persuaded that we know enough to construct that ultimate programming >environment. > >But I've started reading about Eiffel... I've heard it mentioned 3 times already, and this is the first time I've ever read this newsgroup. What is Eiffel? Could someone supply some references? >=Ned Horvath= Rob Jellinghaus | "SINGAPORE?!? You're supposed to be a jellinghaus-robert@CS.Yale.EDU | COWBOY! What kind of cowboy song is ROBERTJ@{yalecs,yalevm}.BITNET | THAT??!! Singapore, I oughta--" {everyone}!decvax!yale!robertj | -- Eddie Foy, _The Cowboy Wally Story_
halldors@paul.rutgers.edu (Magnus M Halldorsson) (10/07/88)
Eiffel is a OO prog lang, written by Bertrand Meyer, published by Interactive Software Eng. Inc. It has multiple inheritance etc. Look at the new Journal of Object-Oriented Computing, issue#2 has a review of it. Meyer also has a recent book out (1987) on OOP using Eiffel. I can look it up if you can't find it. Magnus
cmc@inf.rl.ac.uk (Chris Crampton) (11/21/88)
In article <322@pvab.UUCP> robert@pvab.UUCP (Robert Claeson) writes: >In article <4193@polya.Stanford.EDU>, shap@polya.Stanford.EDU (Jonathan S. Shapiro) writes: > >[discussion about how to hide the private part of a class declaration deleted] > >> ... And yes, his method included the use of a pointer. One solution I have used which really is quite straight forward and bound to have occured to others is as follows: ------------foo.h------------------ class foo { struct foo_privates* pfoo; // private data void private_method(); ... public: ... }; ------------foo.c------------------ #include "foo.h" struct foo_privates { ... // implementation dependant data and methods }; ... // implementation code ----------------------------------- The file foo.h is included by all users of the foo class and foo.c is the implementation module for class foo. It is only in the latter that the private data of foo is described, thus hiding any details. Only foo.h need be distributed with the library providing the foo class. If, of course, the parts of the implementation that need access to the private data are spread across more than one C++ file then the private data would have to be described in additional header file (which needn't be distributed). There are two advantages to this scheme: - the details of the implementation are kept private - if the implementation is changed but the interface remains unchanged then the amount of recompilation necessary is greatly reduced There are disadvantages: - private methods become messy in that they aren't part of the actual class or, if they are, then they are exposed in the header file (as in private_method() above) - when debugging classes using debuggers intended for old C then the private struct probably has to be moved into the header file so that complete debugging can be performed - inline funcions which use private data are not possible You pays yer money... Chris. ======================================================================= Chris M Crampton JANET: cmc@uk.ac.rl.inf Informatics Department ARPA: cmc%inf.rl.ac.uk@nss.cs.ucl.ac.uk Rutherford Appleton Labs, UUCP: ..!mcvax!ukc!rlvd!cmc Didcot, OXON, OX11 0QX, U.K. cmc@rlvd.uucp Tel. +44 235 44 6756 FAX. +44 235 44 5831