[comp.std.c++] Separate Decl of Private Member Fcn

stt@inmet.inmet.com (08/03/90)

It seems to be a major maintenance/recompilation headache that any
private utility function which is to be used to implement
public member functions must be declared in the class declaration,
either as a private member function, or as a friend.

It would seem much better to allow a separate place to
specify the set of private (utility/helper) member/friend functions,
or to simply allow private member functions to be declared/defined
outside of the class declaration.

This issue is discussed in E&S 9.3, bottom of page 174, but
I find the argument unconvincing:

   "Why can't functions be added to a class after the end
    of the class declaration?  If this were allowed, a user 
    could gain access to a class simply by declaring an additional
    function."

This is one of the rare "we don't trust the programmer" situations
in C or C++.  The name of the function is going to require
the class qualifier in its definition,
so it will be obvious what is going on.  It is necessarily
going to be private (I assume), so it is only callable
from other member functions.  Where is the danger?

S. Tucker Taft
Intermetrics, Inc.
Cambridge, MA  02138

mckenney@sparkyfs.istc.sri.com (Paul Mckenney) (08/08/90)

In article <259400001@inmet> stt@inmet.inmet.com writes:
>
>It seems to be a major maintenance/recompilation headache that any
>private utility function which is to be used to implement
>public member functions must be declared in the class declaration,
>either as a private member function, or as a friend.
> [ . . . ]
>This issue is discussed in E&S 9.3, bottom of page 174, but
>I find the argument unconvincing:
>   "Why can't functions be added to a class after the end
>    of the class declaration?  If this were allowed, a user 
>    could gain access to a class simply by declaring an additional
>    function."
>[ . . . ]  Where is the danger?

The danger is that a programmer could hijack a call that was intended
to invoke a base-class member function:

---- standard_library_stuff.h ----
	class A
		{
		void do_the_A_thing() { ... }
		};

	class B : public A
		{
		void do_the_B_thing() { ... do_the_A_thing(); ... }
		};

---- joe_hacker.c ----
	void B::do_the_A_thing()
		{
		... Pilot, take this member function to Iraq! ...
		}


This hijacking could be hidden deep inside a large, complex system and
be very difficult to find, especially if classes A and B had been thoroughly
debugged and were hence presumed innocent.

						Thanx, Paul

mcgrath@homer.Berkeley.EDU (Roland McGrath) (08/08/90)

In article <32527@sparkyfs.istc.sri.com> mckenney@sparkyfs.istc.sri.com (Paul Mckenney) writes:
   In article <259400001@inmet> stt@inmet.inmet.com writes:
   >
   >It seems to be a major maintenance/recompilation headache that any
   >private utility function which is to be used to implement
   >public member functions must be declared in the class declaration,
   >either as a private member function, or as a friend.
   > [ . . . ]
   >This issue is discussed in E&S 9.3, bottom of page 174, but
   >I find the argument unconvincing:
   >   "Why can't functions be added to a class after the end
   >    of the class declaration?  If this were allowed, a user 
   >    could gain access to a class simply by declaring an additional
   >    function."
   >[ . . . ]  Where is the danger?

   The danger is that a programmer could hijack a call that was intended
   to invoke a base-class member function:

	[...]

   This hijacking could be hidden deep inside a large, complex system and
   be very difficult to find, especially if classes A and B had been thoroughly
   debugged and were hence presumed innocent.

This a good argument for programmers not doing this sort of thing, since it may
make their lives more difficult down the road.  Why, however, should they be
prohibited from doing it?  Sure sounds like a victimless crime to me....
--
	Roland McGrath
	Free Software Foundation, Inc.
roland@ai.mit.edu, uunet!ai.mit.edu!roland

mckenney@sparkyfs.istc.sri.com (Paul Mckenney) (08/09/90)

In article <MCGRATH.90Aug7190831@homer.Berkeley.EDU> mcgrath@homer.Berkeley.EDU (Roland McGrath) writes:
>In article <32527@sparkyfs.istc.sri.com> mckenney@sparkyfs.istc.sri.com (Paul Mckenney) writes:
>   In article <259400001@inmet> stt@inmet.inmet.com writes:
>   >
>   >It seems to be a major maintenance/recompilation headache that any
>   >private utility function which is to be used to implement
>   >public member functions must be declared in the class declaration,
>   >either as a private member function, or as a friend.
>   > [ . . . ]
>   >This issue is discussed in E&S 9.3, bottom of page 174, but
>   >I find the argument unconvincing:
>   >   "Why can't functions be added to a class after the end
>   >    of the class declaration?  If this were allowed, a user 
>   >    could gain access to a class simply by declaring an additional
>   >    function."
>   >[ . . . ]  Where is the danger?
>
>   The danger is that a programmer could hijack a call that was intended
>   to invoke a base-class member function:
>
>	[...]
>
>   This hijacking could be hidden deep inside a large, complex system and
>   be very difficult to find, especially if classes A and B had been thoroughly
>   debugged and were hence presumed innocent.
>
>This a good argument for programmers not doing this sort of thing, since it may
>make their lives more difficult down the road.  Why, however, should they be
>prohibited from doing it?  Sure sounds like a victimless crime to me....

In a single-programmer project, one can certainly argue that the only
possible victim is the perpetrator.  In larger projects with many
participants, retribution does not necessarily so surely and swiftly
descend upon the perpetrator.

In addition, note that the use of derivation and virtual functions
provides a safer and more ``socially acceptable'' way of getting this
effect in many cases:

	#include <stream.h>

	class A
		{
	public:
		virtual void do_the_A_thing()
			{
			cout << "Doing the A thing.\n";
			}
		};

	class B : public A
		{
	public:
		void do_the_B_thing()
			{
			cout << "Doing the B thing.\n";
			do_the_A_thing();
			}
		};

	class take_me_to_Baghdad : public B
		{
	public:
		void do_the_A_thing()
			{
			cout << "Hijacked to Baghdad!\n";
			}
		};

	main()

		{
		A	a;
		B	b;
		take_me_to_Baghdad c;

		cout << "a.do_the_A_thing();\n";
		a.do_the_A_thing();

		cout << "b.do_the_B_thing();\n";
		b.do_the_B_thing();

		cout << "c.do_the_B_thing();\n";
		c.do_the_B_thing();
		}

The output of this program is:

	a.do_the_A_thing();
	Doing the A thing.
	b.do_the_B_thing();
	Doing the B thing.
	Doing the A thing.
	c.do_the_B_thing();
	Doing the B thing.
	Hijacked to Baghdad!

The approach shown in this program is safer for two reasons: (1) the
``virtual'' keyword on ``do_the_A_thing()'' has explicitly warned the
programmer that this member function can be overridden in a derived
class, and (2) variables declared to be of class A or class B are
not affected by the addition of class take_me_to_Baghdad.

Please note that there are other problems with the proposed modification.
Suppose that one compilation unit adds one member function to a class,
and another compilation unit adds a different function.  Suppose that
one or both of these are virtual functions.  Suppose that a reference
to an object belonging to the abused class is passed from one of the
compilation units to the other.

In summary, I feel that the proposed modification encourages dangerous
practices and provides few, if any, additional capabilities beyond those
of the existing language.
				Thanx, Paul

jimad@microsoft.UUCP (Jim ADCOCK) (08/09/90)

In article <MCGRATH.90Aug7190831@homer.Berkeley.EDU> mcgrath@homer.Berkeley.EDU (Roland McGrath) writes:
>
>   This hijacking could be hidden deep inside a large, complex system and
>   be very difficult to find, especially if classes A and B had been thoroughly
>   debugged and were hence presumed innocent.
>
>This a good argument for programmers not doing this sort of thing, since it may
>make their lives more difficult down the road.  Why, however, should they be
>prohibited from doing it?  Sure sounds like a victimless crime to me....
>--
>	Roland McGrath

I disagree about this feature being victimless.  Optimizing compilers can
rely on visibility in making optimizing choices.  Hijacking and other hacks
break these optimizations.  Compiler writers are again forced to make a choice:
Do we write compilers that do a good job of optimizing the code of people who
write within the language, or do we pessimize our optimizers, because we have
to assume some people are going to write hack code?  So I claim people who
write hack code victimize people who write within the language, because 
compilers have to be pessimistic about the kinds of programming hacks people
are going to make when programming in C++.  My claim is that much better
optimizing compilers can be developed if people are willing to program within
the language, rather than playing C-like coding hacks.

stt@inmet.inmet.com (08/13/90)

Re: Separate Decl of Private Member Fcn

The obvious solution to the concerns about allowing
member functions to be added after the end of the class
declaration is to require that they be private and static.
This makes them visible only to the other member functions,
and prevents "hijacking."

I have used Ada for ten years and the ability to add private functions
with visibility to private data, without disturbing the
declaration of a type is essential to the enhancement
and sane maintenance of a large system.

After all, that is why "static" functions were added to the
original C.  It would be a shame if private static member functions in C++
couldn't have the same limited per-file visibility they have in C.

S. Tucker Taft
Intermetrics, Inc.
Cambridge, MA  02138

rfg@NCD.COM (Ron Guilmette) (08/15/90)

In article <259400002@inmet< stt@inmet.inmet.com writes:
<
<Re: Separate Decl of Private Member Fcn
<
<The obvious solution to the concerns about allowing
<member functions to be added after the end of the class
<declaration is to require that they be private and static.
<This makes them visible only to the other member functions,
<and prevents "hijacking."
<
<I have used Ada for ten years and the ability to add private functions
<with visibility to private data, without disturbing the
<declaration of a type is essential to the enhancement
<and sane maintenance of a large system.
<
<After all, that is why "static" functions were added to the
<original C.  It would be a shame if private static member functions in C++
<couldn't have the same limited per-file visibility they have in C.

I generally agree with Tucker's comments, except that I believe that what
he probably meant to say was that it should be allowable to add private
*non-virtual* functions after the end of a class.

I don't see how the static-ness of a member function should be an issue when
it comes to adding member functions after the end of the class.

-- 

// Ron Guilmette  -  C++ Entomologist
// Internet: rfg@ncd.com      uucp: ...uunet!lupine!rfg
// Motto:  If it sticks, force it.  If it breaks, it needed replacing anyway.

stt@inmet.inmet.com (08/20/90)

Re: Separate Decl of Private Member Fcn

I wrote:

>>The obvious solution to the concerns about allowing
>>member functions to be added after the end of the class
>>declaration is to require that they be private and static.
>>This makes them visible only to the other member functions,
>>and prevents "hijacking."

Ron Guilmette wrote:

>I generally agree with Tucker's comments, except that I believe that what
>he probably meant to say was that it should be allowable to add private
>*non-virtual* functions after the end of a class.
>
>I don't see how the static-ness of a member function should be an issue when
>it comes to adding member functions after the end of the class.

I agree than non-virtual would probably be adequate, but not as safe as static.
Static implies non-virtual, but it also prevents
"hijacking" of inherited (though non-virtual) member functions.

Also, making something "private" in C++ does not actually
hide it from derivatives, but rather it makes it "inaccessible"
which is subtly diffferent (see E&S page 240 for discussion of
"access control" vs. "visibility control").

Making it static, on the other hand, makes it only usable
within the class, or with an explicit class qualifier.
It never clashes with names in other classes (see E&S page 181).

S. Tucker Taft
Intermetrics, Inc.
Cambridge, MA  02138

mckenney@sparkyfs.istc.sri.com (Paul Mckenney) (08/22/90)

In article <259400003@inmet> stt@inmet.inmet.com writes:
|
|Re: Separate Decl of Private Member Fcn
|
|I wrote:
|
|>>The obvious solution to the concerns about allowing
|>>member functions to be added after the end of the class
|>>declaration is to require that they be private and static.
|>>This makes them visible only to the other member functions,
|>>and prevents "hijacking."
|
|Ron Guilmette wrote:
|
|>I generally agree with Tucker's comments, except that I believe that what
|>he probably meant to say was that it should be allowable to add private
|>*non-virtual* functions after the end of a class.
|>
|>I don't see how the static-ness of a member function should be an issue when
|>it comes to adding member functions after the end of the class.
|
|I agree than non-virtual would probably be adequate, but not as safe as static.
|Static implies non-virtual, but it also prevents
|"hijacking" of inherited (though non-virtual) member functions.

An earlier article in this thread mentioned that adding member functions
after the end of a class was very useful in ADA programs.  However, ADA's
``derived'' types can only restrict the values that may be assigned to
members that already exist in the parent type.  There is no notion of
adding new members in the derived type.

So, what does the ability to add member functions after the end of an existing
class buy you that simply deriving another class from the existing class
could not?  Especially given the confusion that could result from different
member functions being independently added to the same class in different
translation units of the same program...

stt@inmet.inmet.com (08/28/90)

Re: Separate Decl of Private Member Fcn

McKenney asks:

> So, what does the ability to add member functions after the end of an existing
> class buy you that simply deriving another class from the existing class
> could not?  Especially given the confusion that could result from different
> member functions being independently added to the same class in different
> translation units of the same program...

The point is not really to add new "member functions."  The point
is to allow the implementation of the existing member functions
to use local/static/private "utility" functions which are not 
necessarily visible at all in the class declaration.

C++ nomenclature necessarily considers such
local functions "member functions" (or friends), if they are going to have
any access to the private members of the class, but they are really intended
to serve the same purpose as static functions in C, which
have "file" scope rather than global scope.  By giving such
functions "file" scope, there is no ambiguity if multiple same-named
functions are introduced in distinct files.

If you prefer, these local utility functions could be considered "friends",
but the critical point is that you can add new ones without editing
the class declaration.  The disadvantage of making them friends is that
they would themselves then be accessible to non-members, whereas if they
are private, static members, then only other functions with private access
can call them.

The reference to Ada had nothing to do with derived types.
It was referring to the ability to add local subprograms
to a package body as desired to help in the implementation
of visible subprograms.

S. Tucker Taft
Intermetrics, Inc.
Cambridge, MA  02138

rfg@NCD.COM (Ron Guilmette) (08/31/90)

In article <259400003@inmet> stt@inmet.inmet.com writes:
+
+Re: Separate Decl of Private Member Fcn
+
+I wrote:
+
+>>The obvious solution to the concerns about allowing
+>>member functions to be added after the end of the class
+>>declaration is to require that they be private and static.
+>>This makes them visible only to the other member functions,
+>>and prevents "hijacking."
+
+Ron Guilmette wrote:
+
+>I generally agree with Tucker's comments, except that I believe that what
+>he probably meant to say was that it should be allowable to add private
+>*non-virtual* functions after the end of a class.
+>
+>I don't see how the static-ness of a member function should be an issue when
+>it comes to adding member functions after the end of the class.
+
+I agree than non-virtual would probably be adequate, but not as safe as static.
+Static implies non-virtual, but it also prevents
+"hijacking" of inherited (though non-virtual) member functions.
+
+Making it static, on the other hand, makes it only usable
+within the class, or with an explicit class qualifier.
+It never clashes with names in other classes (see E&S page 181).

Just to clarify, I believe that Tucker is misreading E&S to say that the
names of static member functions are not inherited.  As I understand it,
they are.  So I think that my observation is still valid, i.e. that an
extension to C++ to allow the declaration of class member functions *after*
the class type specification is a potentially useful extension, but it
*must* be subject to the restriction that the (added) member functions
must be non-virtual.

-- 

// Ron Guilmette  -  C++ Entomologist
// Internet: rfg@ncd.com      uucp: ...uunet!lupine!rfg
// Motto:  If it sticks, force it.  If it breaks, it needed replacing anyway.

mckenney@sparkyfs.istc.sri.com (Paul Mckenney) (09/08/90)

In article <259400005@inmet> stt@inmet.inmet.com writes:
>Re: Separate Decl of Private Member Fcn
>> So, what does the ability to add member functions after the end of an existing
>> class buy you that simply deriving another class from the existing class
>> could not?  Especially given the confusion that could result from different
>> member functions being independently added to the same class in different
>> translation units of the same program...

>The point is not really to add new "member functions."  The point
>is to allow the implementation of the existing member functions
>to use local/static/private "utility" functions which are not 
>necessarily visible at all in the class declaration.

One way to do this within the current definition of C++ to to use
friend classes rather than friend functions.  For example, in a global
header file (foo.h) to be included everywhere (thus not to be lightly
modified):

	class public_interface
		{
		friend class private_implementation;
	public:
		// public interface
	private:
		// private stuff
		};

In a second file (foo.cc), place the definitions of all member functions.
These member functions may refer to members of class ``private_implementation''.
This file may also have the declaration and definition for your utility
functions, as (probably static) member functions of class
``private_implementation''.  These member functions would have access
to class ``public_interface''s private members via the friend clause.
Note that foo.cc may be freely modified to add, delete, or restructure
the utility member functions in class ``private_implementation'' without
requiring all translation units that include foo.h to be recompiled.

>The reference to Ada had nothing to do with derived types.
>It was referring to the ability to add local subprograms
>to a package body as desired to help in the implementation
>of visible subprograms.

Please accept my apologies for the misunderstanding.

				Thanx, Paul