eeartym@cybaswan.UUCP (Dr R.Artym eleceng ) (08/26/88)
This is not the flame-attracting exercise that the subject line would seem to indicate --- what I'm really after are the group's opinions on the basic notion that direct access to any data element of another class is *ALWAYS* a bad idea, and should be avoided at all cost. So far I haven't come across any situation where friends are really necessary --- most cases seem to derive from a lack of object-oriented structuring, or the requirements can be met with inline functions. My own concerns lie with the impact of this loosening of the class system on the software engineering aspects of programming. In large systems, small transgressions allow in the bugs. Do YOU have a c++ program that couldn't have been written without the friend specifier? Rich -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Keywords: Parallel, Applicative, and Object-Oriented Languages and Systems --------------------------------------------------------------------------- Dr. Richard Artym, + UUCP : ..!ukc!pyr.swan.ac.uk!eeartym Electrical Engineering Dept., + JANET : eeartym@uk.ac.swan.pyr University of Wales, + Phone : [(0792) or (+44 792)] 295536 Swansea, SA2 8PP, + Fax : [(0792) or (+44 792)] 295532 U.K. + Telex : 48358 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
newsuser@LTH.Se (LTH network news server) (09/02/88)
In article <61@cybaswan.UUCP> eeartym@cybaswan.UUCP (Dr R.Artym eleceng ) writes: > My own concerns lie with the impact of this loosening of the class >system on the software engineering aspects of programming. In large systems, >small transgressions allow in the bugs. I regard "friend" functions as a way of bending the rules a little, in order to avoid bigger trouble -- rather like a white lie. While this may not be as clean as one would like, it is defendable if you stress practical software engineering. I have seen many Modula-2 and Ada programs where the writer has been "forced" to use very large modules because of too strict encapsulation. > Do YOU have a c++ program that couldn't have been written without >the friend specifier? No, but I think my programs have improved by the use of a few friend functions and classes. General comment: I think this raises the issue that class design is a demanding task, especially if other people are going to use the classes in another conext. Should we start a discussion of GOOD design of classes? Dag Bruck -- -- Department of Automatic Control Internet: dag@control.lth.se Lund Institute of Technology UUCP: ...!enea!control.lth.se!dag P. O. Box 118 S-221 00 Lund, SWEDEN Phone: +46 46-108779
baud@gt-eedsp.UUCP (Kurt Baudendistel) (09/02/88)
In article <61@cybaswan.UUCP> eeartym@cybaswan.UUCP (Dr R.Artym eleceng ) writes: > So far I haven't come across any situation where friends are really >necessary --- most cases seem to derive from a lack of object-oriented >structuring, or the requirements can be met with inline functions. the trick is that this may be true in the general case, but as with most things in c++, decisions are colored by the magic concept of *efficiency*. it is often in the programmers' best interests to use friends for this reason when creating related classes. a good example is vector and matrix classes. -- Kurt Baudendistel [GRA McClellan] Georgia Tech, School of Electrical Engineering, Atlanta, GA 30332 USENET: ...!{allegra,hplabs,ihnp4,ulysses}!gatech!gt-eedsp!$me INTERNET: $me@gteedsp.gatech.edu
ark@alice.UUCP (Andrew Koenig) (09/02/88)
In article <61@cybaswan.UUCP>, eeartym@cybaswan.UUCP (Dr R.Artym eleceng ) writes: > Do YOU have a c++ program that couldn't have been written without > the friend specifier? Obviously not -- I can write a universal Turing machine simulator without the friend specifier. But the real question is: Are there problems for which friends are the ``best'' solution? My answer is YES. Friends are useful any time you want to have several interrelated classes that can't be naturally described by inheritance. For example, a linear algebra package might have Vector and Matrix classes. Although some people might claim that a vector is just a 1xN matrix, others might prefer an Nx1 matrix, and therefore I suggest that vectors and matrices are really different beasts and neither is a species of the other. Now, how do you write a function to multiply a Matrix by a Vector? It apparently needs to know about the internal representations of both classes. Moreover, symmetry suggests that it should be a member of both or neither -- which perforce means neither. Friends are the only way to make this possible. If you are tempted to respond that this just shows that matrices and vectors should either be the same class or one should inherit from the other, my response is that my argument still holds for any two classes that need to be linked by some means other than inheritance -- you won't convince me that friends are unnecessary until you have convinced me that no such classes can ever exist. -- --Andrew Koenig ark@europa.att.com
chip@ateng.uucp (Chip Salzenberg) (09/03/88)
According to eeartym@cybaswan.UUCP (Dr R.Artym eleceng ): > Do YOU have a c++ program that couldn't have been written without >the friend specifier? Well, there is almost no language feature which is truly indispensible, but I used "friend" once where it proved very useful: an implementation of a hash table. The HashTable class contains an array of pointers to HashNode. Each HashNode contains a pointer to the next HashNode. I made HashTable a friend of HashNode. This allowed easy access to the HashNode "next" pointer but _only_ for HashTable member functions. -- Chip Salzenberg <chip@ateng.uu.net> or <uunet!ateng!chip> A T Engineering My employer may or may not agree with me. The urgent leaves no time for the important.
bs@alice.UUCP (Bjarne Stroustrup) (09/03/88)
A friend function can be used to express that coercions is acceptable for the left hand operand of an operator. Such coercions are not aceptable for the left hand operand of an operator defined as a member function: class Int { // ... public: Int(int); // e.g. Int(27) Int(string); // e.g. Int("123456789123456789") friend Int operator+(Int,Int); friend Int operator*(Int,Int); // ... Int operator=(Int); // ... }; main() { Int a = 1; a+1; // operator+(a,Int(1)) 1+a; // operator+(Int(1),a) a = 1; // a.operator+(Int(1)) 1 = a; // illegal } This could be expressed without the use of friend functions (but not in C++, since you cannot specify a member function for a built-in type so int::operator(Int) cannot be defined). It could, however, also be expressed without the use of member functions. It can also be expressed without the use of coercions, but that leads to an explosion of operator functions.
seeger@beach.cis.ufl.edu (Charles Seeger) (09/06/88)
In article <1988Sep2.094800.16353@LTH.Se> dag@Control.LTH.Se (Dag Bruck) writes: > Should we start a discussion of GOOD >design of classes? > >Dag Bruck I think that is a great idea. I could learn a lot. Chuck
fox@garfield (David Fox) (09/07/88)
In article <1988Sep2.174327.6439@ateng.uucp> chip@ateng.UUCP (Chip Salzenberg) writes: >I used "friend" once where it proved very useful: an implementation of a >hash table. > >The HashTable class contains an array of pointers to HashNode. Each >HashNode contains a pointer to the next HashNode. I made HashTable a friend >of HashNode. This allowed easy access to the HashNode "next" pointer but >_only_ for HashTable member functions. Yes, this is an example of `friend' creating *more* type security rather than less. The HashNode class need have *no* public members whatsoever, so that the class itself effectively becomes a private `member' of the HashTable class. -David Fox
lpringle@bbn.com (Lewis Pringle) (09/08/88)
In article <8160@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes: > >For example, a linear algebra package might have Vector and Matrix >classes. Although some people might claim that a vector is >just a 1xN matrix, others might prefer an Nx1 matrix, and >therefore I suggest that vectors and matrices are really >different beasts and neither is a species of the other. > >Now, how do you write a function to multiply a Matrix by a >Vector? It apparently needs to know about the internal >representations of both classes. Moreover, symmetry suggests >that it should be a member of both or neither -- which >perforce means neither. Friends are the only way to make >this possible. I am not sure that your example well illustrates your point, since accessing the size, and elements of both vectors and matrices would be public methods of those classes. I think thats really all you need to implement multiplication, even if you are efficiency hacking (in which case inline functions as accessors to the matrices would be a good choice). I am not sure if I like the use of friends, but I can share a place where I used them, and could not think of a better way. I was implementing an object-oriented portable windowing system, and I defined high level window object (class). It had pointers to implementation specific structures (e.g. the OS Window pointer) which needed to be referenced by another class (the Controls). I could have made a public accessor for this machine-dependent information, but I wanted the public interface to be entirely portable, and the use of friends was the only way I could find to acheive that. It seems to me that the trouble is that C++ provides a restrictive form of data hiding (not that I know of a better way!): its too "all or nothing". The use of friends is not exacly what you want. Perhaps if in specifying a friend,you didn't give blanket access to the given class, but instead to just an explicitly stated set of members? Lewis. "OS/2: half an operating system for half a computer." In Real Life: Lewis Gordon Pringle Jr. Electronic Mail: lpringle@labs-n.bbn.com Phone: (617) 873-4433
jss@hector.UUCP (Jerry Schwarz) (09/09/88)
My perspective on "friends" is that they are another C++ mechanism for creating a public interface to a class. As such, the considerations that go into deciding whether to declare a friend are essentially the same as those that go into deciding whether to declare a public member. There are two import distinctions between member functions and friend functions. 1. Syntactic. Friends are invoked using ordinary function notation rather member (. or ->) syntax. Or in the case of friend operator, they may be invoked with the class object as the second operand. 2. A friend may be part of the interface of more than one class. Both 1 and 2 may play a part in deciding to use a friend rather than a member. Jerry Schwarz AT&T Bell Labs, Murray Hill
shopiro@alice.UUCP (Jonathan Shopiro) (09/09/88)
In article <29433@bbn.COM>, lpringle@bbn.com (Lewis Pringle) writes: > ... > It seems to me that the trouble is that C++ provides a restrictive form of > data hiding (not that I know of a better way!): its too "all or nothing". > The use of friends is not exacly what you want. Perhaps if in > specifying a friend,you didn't give blanket access to the given class, > but instead to just an explicitly stated set of members? Good idea! In fact you can do exactly that, for example class Foo { int bar(); int baz(); }; class Foe { friend int Foo::bar(); int priv; }; Then Foo:bar() can access priv in any Foe object, but Foo::baz() cannot. I should also say that there seems no limit to the gradations of data hiding that a programmer might want, for example you might want one private member to be accessible to one external function and another private member to be accessible only to a different external function. However, the fundamental purpose of data hiding in C++ is to help the programmer avoid bugs and it has no impact whatsoever on correct programs. Therefore, more elaborate data hiding schemes have two strikes against themselves at the outset: they make things more complex by their very existence, but their justification must be that they make things simpler. -- Jonathan Shopiro AT&T Bell Laboratories, Murray Hill, NJ 07974 research!shopiro (201) 582-4179
eeartym@cybaswan.UUCP (Dr R.Artym eleceng ) (09/12/88)
In article <424@gt-eedsp.UUCP> baud@gt-eedsp.UUCP (Kurt Baudendistel) writes: > the trick is that this may be true in the general case, but as with most > things in c++, decisions are colored by the magic concept of *efficiency*. > it is often in the programmers' best interests to use friends for this > reason when creating related classes. a good example is vector and > matrix classes. Inline functions are the magic tools that allow us to dispense with the efficiency argument almost all of the time. In any case, the structure of a whole application should never be governed by efficiency, as the 20-80% rule says that we only really need to optimize our inner loops. Inlines strategically placed in those positions can give us the major gains without compromising class isolation. -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Keywords: Parallel, Applicative, and Object-Oriented Languages and Systems --------------------------------------------------------------------------- Dr. Richard Artym, + UUCP : ..!ukc!pyr.swan.ac.uk!eeartym Electrical Engineering Dept., + JANET : eeartym@uk.ac.swan.pyr University of Wales, + Phone : [(0792) or (+44 792)] 295536 Swansea, SA2 8PP, + Fax : [(0792) or (+44 792)] 295532 U.K. + Telex : 48358 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eeartym@cybaswan.UUCP (Dr R.Artym eleceng ) (09/12/88)
In article <8160@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes: > Obviously not -- I can write a universal Turing machine > simulator without the friend specifier. But the real question is: > Are there problems for which friends are the ``best'' solution? > My answer is YES. Friends are useful any time you want to have > several interrelated classes that can't be naturally described > by inheritance. In this circumstance, you should get objects to perform a service for you by calling their member functions, rather than meddling with their internals directly. > For example, a linear algebra package might have Vector and Matrix > classes. Although some people might claim that a vector is > just a 1xN matrix, others might prefer an Nx1 matrix, and > therefore I suggest that vectors and matrices are really > different beasts and neither is a species of the other. > Now, how do you write a function to multiply a Matrix by a > Vector? It apparently needs to know about the internal > representations of both classes. Moreover, symmetry suggests > that it should be a member of both or neither -- which > perforce means neither. Friends are the only way to make > this possible. Why not define the function as an operator in both classes with the other class as argument? Then the right one will get invoked depending on the order of the operands. Needless to say, the actual code need only appear in one of them (the other just swaps the arguments around and calls the former's member function). In the example that you give, the desire for symmetry is misfounded: the code should appear in that class of which a new object is being created by the operation, ie. if Vector OP Matrix produces a new Matrix, then all the actual operator code should reside in a member of Matrix and be called as a service from the corresponding member of Vector. > If you are tempted to respond that this just shows that > matrices and vectors should either be the same class > or one should inherit from the other, my response is that > my argument still holds for any two classes that need > to be linked by some means other than inheritance -- > you won't convince me that friends are unnecessary until > you have convinced me that no such classes can ever exist. I would not respond this way! Quite the opposite --- I say keep them separate, completely separate, and if there's a service that one can render the other then invoke it via the regular object access system, ie. call a member. -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Keywords: Parallel, Applicative, and Object-Oriented Languages and Systems --------------------------------------------------------------------------- Dr. Richard Artym, + UUCP : ..!ukc!pyr.swan.ac.uk!eeartym Electrical Engineering Dept., + JANET : eeartym@uk.ac.swan.pyr University of Wales, + Phone : [(0792) or (+44 792)] 295536 Swansea, SA2 8PP, + Fax : [(0792) or (+44 792)] 295532 U.K. + Telex : 48358 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eeartym@cybaswan.UUCP (Dr R.Artym eleceng ) (09/12/88)
In article <1988Sep2.094800.16353@LTH.Se> dag@Control.LTH.Se (Dag Bruck) writes: > I regard "friend" functions as a way of bending the rules a little, in > order to avoid bigger trouble -- rather like a white lie. While this > may not be as clean as one would like, it is defendable if you stress > practical software engineering. I have seen many Modula-2 and Ada > programs where the writer has been "forced" to use very large modules > because of too strict encapsulation. Maybe this stemmed from lack of inheritance. Why do you say strict encapsulation is to blame? > No, but I think my programs have improved by the use of a few friend > functions and classes. Can you supply any details? > General comment: I think this raises the issue that class design is a > demanding task, especially if other people are going to use the > classes in another conext. Should we start a discussion of GOOD > design of classes? This would be a very worthwhile endeavour. I hope you're reading comp.lang.smalltalk, as there's something along these lines in progress there. -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Keywords: Parallel, Applicative, and Object-Oriented Languages and Systems --------------------------------------------------------------------------- Dr. Richard Artym, + UUCP : ..!ukc!pyr.swan.ac.uk!eeartym Electrical Engineering Dept., + JANET : eeartym@uk.ac.swan.pyr University of Wales, + Phone : [(0792) or (+44 792)] 295536 Swansea, SA2 8PP, + Fax : [(0792) or (+44 792)] 295532 U.K. + Telex : 48358 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eeartym@cybaswan.UUCP (Dr R.Artym eleceng ) (09/12/88)
In article <1988Sep2.174327.6439@ateng.uucp> chip@ateng.uucp (Chip Salzenberg) writes: > Well, there is almost no language feature which is truly indispensible, but > I used "friend" once where it proved very useful: an implementation of a > hash table. > > The HashTable class contains an array of pointers to HashNode. Each > HashNode contains a pointer to the next HashNode. I made HashTable a friend > of HashNode. This allowed easy access to the HashNode "next" pointer but > _only_ for HashTable member functions. I see no reason why the HashTable can't request the preceding HashNode to place the new HashNode pointer in its next field. This seems tidier anyway, since the first HashNode in each chain is linked to the HashTable array and thus this operation belongs in HashTable, whereas subsequent chaining alters the contents of a HashNode and therefore belongs in a HashNode member. Inline dispenses with the efficiency argument. Why this structure anyway? --- it's the C approach, not C++! You ought to have an array of lists in HashTable, with the lists carrying HashNode objects without a next field. The nice thing about C++ is that such a more-OO split is not necessarily any less efficient. -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Keywords: Parallel, Applicative, and Object-Oriented Languages and Systems --------------------------------------------------------------------------- Dr. Richard Artym, + UUCP : ..!ukc!pyr.swan.ac.uk!eeartym Electrical Engineering Dept., + JANET : eeartym@uk.ac.swan.pyr University of Wales, + Phone : [(0792) or (+44 792)] 295536 Swansea, SA2 8PP, + Fax : [(0792) or (+44 792)] 295532 U.K. + Telex : 48358 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eeartym@cybaswan.UUCP (Dr R.Artym eleceng ) (09/12/88)
In article <8163@alice.UUCP> bs@alice.UUCP (Bjarne Stroustrup) writes: > A friend function can be used to express that coercion is acceptable > for the left hand operand of an operator. Such coercions are not aceptable > for the left hand operand of an operator defined as a member function: > ... program ... > This could be expressed without the use of friend functions (but not > in C++, since you cannot specify a member function for a built-in type > so int::operator(Int) cannot be defined). It could, however, also be > expressed without the use of member functions. > It can also be expressed without the use of coercions, but that leads > to an explosion of operator functions. I reckon this is the only convincing reason for using the friend specifier mentioned so far in this discussion. It is important to note that it stems from a limitation in C++ as it stands at the moment, and does not imply that loosening the class scope walls is a good thing. Furthermore, this kind of usage is completely benign, really just a syntactic artifice: the scope region does not flood out into another class. This pours some light on the real source of my discomfort --- my original phrase should perhaps be amended to `Friend classes considered harmful'. Such sledgehammer usage of the specifier probably indicates a non-OO design approach or that structural soundness has been sacrificed for efficiency. (Examples to the contrary still welcome!) -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Keywords: Parallel, Applicative, and Object-Oriented Languages and Systems --------------------------------------------------------------------------- Dr. Richard Artym, + UUCP : ..!ukc!pyr.swan.ac.uk!eeartym Electrical Engineering Dept., + JANET : eeartym@uk.ac.swan.pyr University of Wales, + Phone : [(0792) or (+44 792)] 295536 Swansea, SA2 8PP, + Fax : [(0792) or (+44 792)] 295532 U.K. + Telex : 48358 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eeartym@cybaswan.UUCP (Dr R.Artym eleceng ) (09/12/88)
In article <5870@columbia.edu> fox@garfield (David Fox) writes: > In article <1988Sep2.174327.6439@ateng.uucp> chip@ateng.UUCP (Chip Salzenberg) > writes: > > >I used "friend" once where it proved very useful: an implementation of a > >hash table. > > > >The HashTable class contains an array of pointers to HashNode. Each > >HashNode contains a pointer to the next HashNode. I made HashTable a friend > >of HashNode. This allowed easy access to the HashNode "next" pointer but > >_only_ for HashTable member functions. > > Yes, this is an example of `friend' creating *more* type security rather than > less. The HashNode class need have *no* public members whatsoever, so that > the class itself effectively becomes a private `member' of the HashTable > class. Friend classes without public members may look appealing at first sight, but in a non-trivial application the lack of data hiding amongst the friends would be a far greater source of concern than the possibility of non-local use of a class resulting from the presence of public members. After all, the latter misuse is easily detectable and can't impinge on the proper operation of the rest of the code (as long as static data is not used). The HashTable application doesn't warrant the use of friends at all, in my opinion, as it can be programmed easily and efficiently with full isolation between classes. -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Keywords: Parallel, Applicative, and Object-Oriented Languages and Systems --------------------------------------------------------------------------- Dr. Richard Artym, + UUCP : ..!ukc!pyr.swan.ac.uk!eeartym Electrical Engineering Dept., + JANET : eeartym@uk.ac.swan.pyr University of Wales, + Phone : [(0792) or (+44 792)] 295536 Swansea, SA2 8PP, + Fax : [(0792) or (+44 792)] 295532 U.K. + Telex : 48358 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eeartym@cybaswan.UUCP (Dr R.Artym eleceng ) (09/14/88)
Letter 1 --- I'm posting this on behalf of MARC SHAPIRO: -------------------------------------------------------- * From shapiro@fr.inria.sor Thu Sep 8 19:08:09 1988 * Organization: INRIA, BP 105, 78153 Le Chesnay Cedex, France * telephone +33(1)39-63-55-11, telex 697033 F, telecopy +33(1)39-63-53-30 * To: eeartym@uk.ac.swan.pyr * Subject: Re: Friend specifier considered harmful * Newsgroups: comp.lang.c++ In article <61@cybaswan.UUCP> you write: > Do YOU have a c++ program that couldn't have been written without >the friend specifier? Yes. I have written a generic ``plex'' or ``flexible array'' (i.e. an array which can grow or shrink at either end). The plex is a linked list of ``chunks''. An iterator object allows to traverse the plex upwards, or downwards, or any old way. You may declare any number of independent iterators for a single plex. These 3 classes are all friends of each other. Since the actual data of a plex is the contents of the chunks, the plex better have friend access to chunk. Chunk also has friend access to plex; this is not strictly necessary (actually, it's a bit unclean) but the code is more readable because the operations on the list as a whole are partof plex, whereas operations concerning a single chunk and its immediate neighbours are in chunk. Finally, iterator has access to plex for efficiency. The whole idea of an iterator is to abstract from the many individual operations needed to access elements in succession; might as well do that efficiently. Note however that iterator never changes the plex (the ``plex'' field of iterator is declared ``const''). A chunk may modify the plex, in order to thread itself onto the linked list. The plex may modify a chunk, in order to store a datum in it. All read access to fields is mediated by inline functions, as well as all the ``high-level'' write operations (like storing a datum). Only those write operations which update the specific representation set the corresponding fields directly. This is good, so that if I change the representation I know exactly where to change the code. Finally, I make a great use of assertions and invariants to make sure at all times that my assumptions are correct and the structure is not broken. Marc Shapiro INRIA, B.P. 105, 78153 Le Chesnay Cedex, France. Tel.: +33 (1) 39-63-53-25 e-mail: shapiro@inria.inria.fr or: ...!mcvax!inria!shapiro -------------------------------------------------------- -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Keywords: Parallel, Applicative, and Object-Oriented Languages and Systems --------------------------------------------------------------------------- Dr. Richard Artym, + UUCP : ..!ukc!pyr.swan.ac.uk!eeartym Electrical Engineering Dept., + JANET : eeartym@uk.ac.swan.pyr University of Wales, + Phone : [(0792) or (+44 792)] 295536 Swansea, SA2 8PP, + Fax : [(0792) or (+44 792)] 295532 U.K. + Telex : 48358 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eeartym@cybaswan.UUCP (Dr R.Artym eleceng ) (09/14/88)
I wrote this letter in reply to mail received from MARC SHAPIRO in reply to the query I posted in this newsgroup on the subject of friends. I'm posting this here to contribute to what has turned into an interesting discussion. * To: shapiro@fr.inria.sor * * Thank you very much for your reply. I do not agree with your arguments * and have tried to answer each of them. I hope I can loosen your convictions * somewhat! ------------------------- > Yes. I have written a generic ``plex'' or ``flexible array'' (i.e. an > array which can grow or shrink at either end). The plex is a linked > list of ``chunks''. An iterator object allows to traverse the plex > upwards, or downwards, or any old way. You may declare any number of > independent iterators for a single plex. An interesting spec; what was your intended application for plex objects? > These 3 classes are all friends of each other. Since the actual > data of a plex is the contents of the chunks, the plex better have > friend access to chunk. Why? To me, all this says is that a plex needs to be able to create a chunk from the data supplied. > Chunk also has friend access to plex; this is > not strictly necessary (actually, it's a bit unclean) I agree on both counts. > but the code is > more readable because the operations on the list as a whole are partof > plex, As they should be. (An object should operate at the level of abstraction it is implementing, not at one above or below.) > whereas operations concerning a single chunk and its immediate > neighbours are in chunk. As they should be in a class-based system. (In an object-based system, in contrast, a chunk wouldn't be able to operate on any other chunk --- see one of the other ongoing discussions in comp.lang.c++) However, you don't say why this good partitioning of operations is only achievable by giving chunks friend access to plex, and I don't see such a need at all. > Finally, iterator has access to plex for efficiency. The whole idea > of an iterator is to abstract from the many individual operations > needed to access elements in succession; might as well do that > efficiently. Friend access is not necessary for efficiency; inline functions appro- priately chosen can do this for you with equal effectiveness. > Note however that iterator never changes the plex (the ``plex'' field > of iterator is declared ``const''). A chunk may modify the plex, in > order to thread itself onto the linked list. I consider this to be an ill-structured and dangerous approach. By all means let a chunk know which plex it's on if your application needs this, but threading itself onto a plex directly is dangerous as it bypasses the plex's control over its contents. The chunk should be asking the plex to perform such a service on its behalf. > The plex may modify a > chunk, in order to store a datum in it. Notice that you're not really modifying a chunk, but the contents of the cell where the old chunk used to be! This is a C approach to programming, not object-oriented C++! > All read access to fields is mediated by inline functions, as well as > all the ``high-level'' write operations (like storing a datum). Only > those write operations which update the specific representation set > the corresponding fields directly. This is good, so that if I change > the representation I know exactly where to change the code. Using your own reasoning, the updates should also be done with inline members --- then you'll have only one place to look! > Finally, I make a great use of assertions and invariants to make sure > at all times that my assumptions are correct and the structure is not > broken. Great, we should all be doing this since C++ is quite ameanable to treatment this way, particularily when objects are left alone and not poked directly from outside via friend access! Can you give me details of how you do this? ----------------------- That's it! If you disagree with any of my reasoning or can support your claim with further evidence, please write further. (For now, from 8 replies, only Bjarne Stroustrup has come up with something really requiring friends in C++, and that's got to do with coercion with operators and does NOT require inter-class friend access --- see article <8163@alice.UUCP>.) Regards, Rich -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Keywords: Parallel, Applicative, and Object-Oriented Languages and Systems --------------------------------------------------------------------------- Dr. Richard Artym, + UUCP : ..!ukc!pyr.swan.ac.uk!eeartym Electrical Engineering Dept., + JANET : eeartym@uk.ac.swan.pyr University of Wales, + Phone : [(0792) or (+44 792)] 295536 Swansea, SA2 8PP, + Fax : [(0792) or (+44 792)] 295532 U.K. + Telex : 48358 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eeartym@cybaswan.UUCP (Dr R.Artym eleceng ) (09/14/88)
Letter 2 --- I'm posting this on behalf of MARC SHAPIRO: -------------------------------------------------------- * From shapiro@fr.inria.sor Mon Sep 12 10:27:32 1988 * Organization: INRIA, BP 105, 78153 Le Chesnay Cedex, France * telephone +33(1)39-63-55-11, telex 697033 F, telecopy +33(1)39-63-53-30 * To: eeartym@uk.ac.swan.pyr * Subject: plexes and friends I reply to your reply. Date: 12 Sep 88 02:14:52+0000 From: "Dr R.Artym eleceng" <eeartym@pyramid.SWANSEA.ac.uk> An interesting spec; what was your intended application for plex objects? In our application (a distributed OS) we have a things similar to Unix file descriptors, i.e. capabilities accessed by indexing a capability table in the kernel. Currently we use C++ vectors, but we are constantly running into the limitations of vectors. Dynamic allocation is a lot better. Plexes can also be used as efficient queues. > These 3 classes are all friends of each other. Since the actual > data of a plex is the contents of the chunks, the plex better have > friend access to chunk. Why? To me, all this says is that a plex needs to be able to create a chunk from the data supplied. A chunk is of fiexed (but arbitrary) size, and contains any number of elements. The plex creates a chunk with a single element, and the adds more (or removes them) until the chunk is full; then a new chunk is created. Therefore the plex needs access to its chunks (not directly to the chunk representation, though). > Chunk also has friend access to plex; this is > not strictly necessary (actually, it's a bit unclean) but the code is > more readable because the operations on the list as a whole are partof > plex, I think this is a necessary evil of linked lists. The list descriptor has a pointer to the first chunk, which has a pointer to the next chunk. Therefore list manipulation is necessarily distributed over the descriptor and the chunks. This issue is usually avoided by not making chunks a real class. > whereas operations concerning a single chunk and its immediate > neighbours are in chunk. However, you don't say why this good partitioning of operations is only achievable by giving chunks friend access to plex, and I don't see such a need at all. The issue is protection. A plex stores elements of type T into a list of chunks. Both plex and chunk are ``parameterized'' by type T (faked with macro-expansion). Because of the limitations of macro-expansion and of header files, they must be defined in the same .h file. However, I don't want clients to access the chunks. Therefore I make all the chunk operations private, and I give the plex access to them by making it a friend. When paramterized classes are added to C++, maybe will I be able to hack protection differently by a careful distribution of modules in different files, but I prefer explicit protection (by private and friend attributes) rather than implicit (by files). > Finally, iterator has access to plex for efficiency. The whole idea > of an iterator is to abstract from the many individual operations > needed to access elements in succession; might as well do that > efficiently. Friend access is not necessary for efficiency; inline functions appro- priately chosen can do this for you with equal effectiveness. Once again, the issue is protection. The iterator *does* access the plex via inline functions, but these must not be visible to the general public. They are in the private section of plex, and made visible to iterator with the friend attribute. > A chunk may modify the plex, in > order to thread itself onto the linked list. I consider this to be an ill-structured and dangerous approach. See my comment on ``necessary evil'' above. > The plex may modify a > chunk, in order to store a datum in it. Notice that you're not really modifying a chunk, but the contents of the cell where the old chunk used to be! No. See my explanation of chunks, above. > All read access to fields is mediated by inline functions, as well as > all the ``high-level'' write operations (like storing a datum). Only > those write operations which update the specific representation set > the corresponding fields directly. This is good, so that if I change > the representation I know exactly where to change the code. Using your own reasoning, the updates should also be done with inline members --- then you'll have only one place to look! Not true. If I change my representation (e.g. to use a hash table instead of a list) then all of my list updates have to be ripped out and entirely replaced with something new. Therefore it doesn't help to ``abstract'' them into procedures; in fact, that would hide the purpose of the code. > Finally, I make a great use of assertions and invariants to make sure > at all times that my assumptions are correct and the structure is not > broken. Can you give me details of how you do this? I'm writing an article on this topic, which I'll send to you. P.S. I think this material would be of interest to the newsgroup. If you agree, please post your original letter and when I see it I'll post this reply. Further installments will then make sense to our readers! Fine with me, except that I didin't keep a copy. Feel free to forward my messages to the net. Marc Shapiro INRIA, B.P. 105, 78153 Le Chesnay Cedex, France. Tel.: +33 (1) 39-63-53-25 e-mail: shapiro@inria.inria.fr or: ...!mcvax!inria!shapiro -------------------------------------------------------- -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Keywords: Parallel, Applicative, and Object-Oriented Languages and Systems --------------------------------------------------------------------------- Dr. Richard Artym, + UUCP : ..!ukc!pyr.swan.ac.uk!eeartym Electrical Engineering Dept., + JANET : eeartym@uk.ac.swan.pyr University of Wales, + Phone : [(0792) or (+44 792)] 295536 Swansea, SA2 8PP, + Fax : [(0792) or (+44 792)] 295532 U.K. + Telex : 48358 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
newsuser@LTH.Se (LTH network news server) (09/15/88)
In article <77@cybaswan.UUCP> eeartym@cybaswan.UUCP (Dr R.Artym eleceng ) writes: >In article <1988Sep2.094800.16353@LTH.Se> dag@Control.LTH.Se (Dag Bruck) >writes: > >> I have seen many Modula-2 and Ada >> programs where the writer has been "forced" to use very large modules >> because of too strict encapsulation. > >Maybe this stemmed from lack of inheritance. Why do you say strict >encapsulation is to blame? Yes, object-oriented programming may be the best way to get a well-engineered program. I still think that some ``escape'' mechanism would improve Modula-2 and Ada programs, but probably not to the degree where I will drop C++ (:-). >> No, but I think my programs have improved by the use of a few friend >> functions and classes. > >Can you supply any details? The ``real'' work I do is too complicated to discuss in detail on the net, but I will give an example in general terms: In our application, my C++ program maintains a network of objects for representing control system models. As a typical example, the routine for connecting two components of a block diagram needs access to some internal information of so called terminals. This is really just an internal check to handle the special case of connecting to an undefined enclosing environment (in the case of bottom-up development). >> Should we start a discussion of GOOD >> design of classes? >This would be a very worthwhile endeavour. I will try to come up with a few topics in class design, but I'm sure there are more competent and experienced people on the net. I hope there will be time for discussion at the USENIX C++ Conference in Denver, CO. Dag Bruck -- Department of Automatic Control Internet: dag@control.lth.se Lund Institute of Technology UUCP: ...!enea!control.lth.se!dag P. O. Box 118 S-221 00 Lund, SWEDEN Phone: +46 46-108779
turner@sdti.UUCP (Prescott K. Turner) (09/16/88)
In article <8180@alice.UUCP>, shopiro@alice.UUCP (Jonathan Shopiro) writes: >In article <29433@bbn.COM>, lpringle@bbn.com (Lewis Pringle) writes: >> The use of friends is not exacly what you want. Perhaps if in >> specifying a friend,you didn't give blanket access to the given class, >> but instead to just an explicitly stated set of members? >Good idea! In fact you can do exactly that, for example >... >Then Foo:bar() can access priv in any Foe object, but Foo::baz() cannot. No, that's not what he was asking for. It was for the ability to specify that a function could get at some of a class's private members, but not all. 'Protected' already gives us the ability to specify that some members are more private than others. What's lacking is a way to designate a function as something in between a run-of-the-mill function and a close friend. How about an 'acquaintance' keyword which works like 'friend' but which provides access only to protected and not private members? Not that I'm an advocate of this idea -- just attempting to home in on what was desired. -- Prescott K. Turner, Jr. Software Development Technologies, Inc. 375 Dutton Rd., Sudbury, MA 01776 USA (508) 443-5779 UUCP:...genrad!mrst!sdti!turner
chip@ateng.uucp (Chip Salzenberg) (09/16/88)
According to eeartym@cybaswan.UUCP (Dr R. Artym): >chip@ateng.uucp (Chip Salzenberg) writes: >> The HashTable class contains an array of pointers to HashNode. Each >> HashNode contains a pointer to the next HashNode. I made HashTable a friend >> of HashNode. This allowed easy access to the HashNode "next" pointer but >> _only_ for HashTable member functions. > >I see no reason why the HashTable can't request the preceding HashNode to >place the new HashNode pointer in its next field. Of course, I could have done it that way. But I chose the "friendly" approach so that other classes besides HashTable couldn't even _see_ the next pointer, much less modify it. >Why this structure anyway? --- it's the C approach, not C++! You ought to >have an array of lists in HashTable, with the lists carrying HashNode objects >without a next field. But you're wrong! Linked lists are a useful data structure no matter what the language. ...Although I find it interesting that the interface I used would permit changing to arrays instead of linked lists without modifying the HashTable interface at all. This is a good sign. -- Chip Salzenberg <chip@ateng.uu.net> or <uunet!ateng!chip> A T Engineering My employer may or may not agree with me. The urgent leaves no time for the important.
dl@rocky.oswego.edu (Doug Lea) (09/16/88)
Even in object oriented programming methodologies, there is plenty of room for `pure' constructive functions (i.e., functions that construct a new object based on some unaltered, possibly `private', properties of their arguments), as opposed to non-constructive, potentially `mutative' member functions that change and/or report state. Such functions must often be declared as friends. Consider, for example, a numerical class `Num', with instance `n', and two supplied routines: n.abs(); A member `mutator' routine that replaces n with its absolute value (probably returning *this by reference) and Num y = abs(n) A friend `constructive' function that returns a new object that is the absolute value of n, without changing n. Similarly, with a class `List' and instance `l': l.reverse() Reverses l in-place and reverse(l); Returns a new List that is the reverse of l. From an object-oriented view, pure constructive functions like abs(n) and reverse(l) are on the same par as class constructors themselves: Both `List(l);' (via the X(X&) constructor) and `reverse(l)' construct new objects using essentially the same logic, and requiring the same internal data access privilages. One could dispense with the above functional forms, and force clients to create reversed objects via `List y(l); y.reverse()', but why should clients be required to use a construct that is both less natural and less efficient (clearly, `reverse(l)' could be implemented significantly more efficiently that first constructing then reversing.) Of course, it is not always necessary to support both constructive and mutative versions of everything, but there are cases where support of both versions makes sense, and yet more cases where only the constructive version is desirable. I tend to use friend functions in these situations. Any class I write that ought to support a number of constructive functions, but otherwise must promise to maintain internal integrity of its private data (as with numeric and list types) has many friend functions. The alternative of supporting public low-level data access facilities (that would enable these to be declared without `friend' status) opens up the class to far more potential abuse than does the implementation of a fairly complete set of constructive friend functions, without any other access provisions. Another alternative of implementing two member functions, functional l.build_reverse() and mutative l.self_reverse() does not seem attractive either. Pure functions ought to possess functional notation, and member functions should almost always be just those that mutate and/or report state. This sort of function-versus-member distinction is already blurred a bit in C++, since both constructive and mutative versions of operators (e.g., `+' vs. `+=') may be declared either as friends or as members. (I declare the mutative operators as members and the others as friends -- As a further aside, I find that I am almost always dissatisfied with any C++ routine that *both* mutates and constructs.) Part of the dispute about the use of `friend' functions is based upon the fact that `friend' does not exactly mean `intrinsic' (i.e., logically a part of a class), as perhaps it should in these cases. Several ways to modify C++ to behave in this fashion come to mind. One possibility would be to allow `reverse(l)' to somehow be given the same status as a constructor (i.e., to allow constructors to have different names than the class name). Any solution should allow such functions to be virtual, inherited, etc. In the mean time, using `friends' seems the most practical way to handle this. -Doug Lea Doug Lea, Computer Science Dept., SUNY Oswego, Oswego, NY, 13126 (315)341-2367 email: dl@rocky.oswego.edu or dl%rocky.oswego.edu@nisc.nyser.net UUCP :...cornell!devvax!oswego!dl or ...rutgers!sunybcs!oswego!dl
dml@esl.UUCP (Denis Lynch) (09/17/88)
In article <1988Sep15.151514.20657@ateng.uucp> chip@ateng.UUCP (Chip Salzenberg) writes: >>Why this structure anyway? --- it's the C approach, not C++! You ought to >>have an array of lists in HashTable, with the lists carrying HashNode objects >>without a next field. > >But you're wrong! Linked lists are a useful data structure no matter what >the language. But you missed the point. Of course linked lists are useful. They are a separate concept of their own, and shouldn't be imbedded in you hash table constructs. You want a hash table to be a sequence (list, array, file, shouldn't matter to even the top level of HashTable), where each element in the sequence can in turn be a set or sequence (I don't know which, maybe order matters to you.) The HashNode isn't the thing that really has the "next" property, that is the responsibility of the set or sequence. The big difference here is what happens when you try to use your hash node: suppose for some reason I need to keep a list of the most recently used hash nodes. Sounds like I want to use the next field, right? If it is designed as suggested above, you can see that a HashNode can appear on multiple lists without getting confused. In short, look at something like OOPS. Use a set of basic "computer science" classes to handle lists, arrays, sets, etc., independent of the *contents* of the collections. >...Although I find it interesting that the interface I used would permit >changing to arrays instead of linked lists without modifying the HashTable >interface at all. This is a good sign. And if you follow these recommendations, the only thing you would need to change is the initialization method, since the insertion/access methods would be the same for arrays, lists, and other things. -- Denis Lynch ESL Incorporated decwrl!borealis!\ ARPA: dml%esl.ESL.COM@ames.arc.nasa.gov ucbcad!ucbvax!- ames!- esl!dml SMAIL: dml@esl.ESL.COM lll-lcc!/