[comp.lang.c++] Friend specifier considered harmful

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!/