Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Enemy Functions?

4 views
Skip to first unread message

Mark Santaniello

unread,
Jun 3, 2006, 10:12:55 AM6/3/06
to
Scott Meyers wrote an article in CUJ a few years ago entitled "How
Non-Member Functions Improve Encapsulation". Subsequently, Herb Sutter
refactored std::string using this principle:
http://www.ddj.com/dept/cpp/184401197
http://www.gotw.ca/gotw/084.htm

The basic gist is that, given a choice, we should prefer x( foo ) to
foo.x(), because the latter is less encapsulated. Scott argues that the
benefits outweigh the asymmetric syntax.

It seems like a minor language extension could give us the best of both
worlds.

If a friend function:
* can access the private members of a class
* is not passed a this pointer
* does not reside in the class's namespace

Consider a function which:
* cannot access the private members of a class
* is passed a this pointer
* does reside in the class's namespace

With tongue-in-cheek, I call this an "enemy" function. I envision
something like:

class Foo
{
enemy: // the opposite of a friend :)
void func(); // may not access private members
}

Foo f;
f.func(); //not *really* a member function

Has anyone considered this before? It seems pretty obvious. Something
like this could fix std::string without breaking existing code.

-Mark

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

VSR

unread,
Jun 3, 2006, 8:27:30 PM6/3/06
to
Enemy? How bout "Acquaintance" ? ;-)

Mark Santaniello wrote:

>
> With tongue-in-cheek, I call this an "enemy" function. I envision
> something like:
>
> class Foo
> {
> enemy: // the opposite of a friend :)
> void func(); // may not access private members
> }
>
> Foo f;
> f.func(); //not *really* a member function

whitef...@hotmail.com

unread,
Jun 3, 2006, 8:21:29 PM6/3/06
to
With all due respect, such an "enemy" function sounds kind of useless.
I guess there is a use for "everything," but I will try to explain my
position.

> Consider a function which:
> * cannot access the private members of a class

We have those: they're called free or global functions. Oh, and member
functions of a child class, which cannot access its parent's private
stuff.

> * is passed a this pointer
> * does reside in the class's namespace

This is counter-intuitive. If you give a function a this pointer, it
should be able to dereference members of it's own object. If a function
can't work with the data in it's own object, it has no reason to be
declared in the class' namespace.

Maxim Yegorushkin

unread,
Jun 3, 2006, 8:32:24 PM6/3/06
to

Mark Santaniello wrote:
> Scott Meyers wrote an article in CUJ a few years ago entitled "How
> Non-Member Functions Improve Encapsulation". Subsequently, Herb Sutter
> refactored std::string using this principle:
> http://www.ddj.com/dept/cpp/184401197
> http://www.gotw.ca/gotw/084.htm
>
> The basic gist is that, given a choice, we should prefer x( foo ) to
> foo.x(), because the latter is less encapsulated. Scott argues that the
> benefits outweigh the asymmetric syntax.
>
> It seems like a minor language extension could give us the best of both
> worlds.
>
> If a friend function:
> * can access the private members of a class
> * is not passed a this pointer
> * does not reside in the class's namespace
>
> Consider a function which:
> * cannot access the private members of a class
> * is passed a this pointer
> * does reside in the class's namespace

Is not that function a member function in a derived class? You can put
all data members in a base class so that deirved class member functions
will not be able to access the data members in the base class.

Mark Santaniello

unread,
Jun 4, 2006, 6:34:58 PM6/4/06
to
On Sat, 03 Jun 2006 20:32:24 -0400, Maxim Yegorushkin wrote:

> Is not that function a member function in a derived class? You can put
> all data members in a base class so that deirved class member functions
> will not be able to access the data members in the base class.

This appears to do exactly what I want!

There must be some good reason that Scott Meyers failed to suggest this
alternative. Surely he's aware of it.

Andrei Polushin

unread,
Jun 5, 2006, 1:35:47 PM6/5/06
to
Mark Santaniello wrote:
> Scott Meyers wrote an article in CUJ a few years ago entitled "How
> Non-Member Functions Improve Encapsulation". Subsequently, Herb Sutter
> refactored std::string using this principle:
> http://www.ddj.com/dept/cpp/184401197
> http://www.gotw.ca/gotw/084.htm
>
> The basic gist is that, given a choice, we should prefer x( foo ) to
> foo.x(), because the latter is less encapsulated. Scott argues that the
> benefits outweigh the asymmetric syntax.

Consider two points from Scott's article:

1. From "Degrees of Encapsulation":

>> An easy way to measure how much code might be broken is to count the
>> functions that might be affected. That is, if changing one
>> implementation leads to more potentially broken functions than does
>> changing another implementation, the first implementation is less
>> encapsulated than the second.

2. From "Interfaces and Packaging":

>> Herb Sutter has explained that the "interface" to a class (roughly
>> speaking, the functionality provided by the class) includes the
>> non-member functions related to the class, and he's shown that the
>> name lookup rules of C++ support this meaning of "interface" [7,8].
>> This is wonderful news for my "non-friend non-members are better
>> than members" argument, because it means that the decision to make
>> a class-related function a non-friend non-member instead of a member
>> need not even change the interface to that class!

The news are not so "wonderful", as they could be: user still depends
on a whole class interface, and the interface does not change with this
trick. It's just "encapsulating" one part of class implemenation from
another part, not a real encapsulation.


> Has anyone considered this before? It seems pretty obvious. Something
> like this could fix std::string without breaking existing code.

But we can extract minimal interface into the base class to achieve the
same effect:

template<class charT> class __minimal_string {
public:
// minimal interface
private:
// private variables
};


template<class charT> class basic_string : __minimal_string<charT> {
public:
// fat interface
};

It helps us (1) to author these two classes and (2) to determine which
uses are either risky or rather stable. In practice, the author does
not care about the 1st benefit, or he will write such a split
implementation if he cares. From the user point of view, the member
stability is the least thing to care about: he will use the most
convenient member anyway.

There are some benefits, but there is nobody who benefits.

--
Andrei Polushin

Gene Bushuyev

unread,
Jun 5, 2006, 1:35:22 PM6/5/06
to
"Mark Santaniello" <mar...@santaniello.net> wrote in message
news:pan.2006.06.04....@santaniello.net...

> On Sat, 03 Jun 2006 20:32:24 -0400, Maxim Yegorushkin wrote:
>
>> Is not that function a member function in a derived class? You can put
>> all data members in a base class so that deirved class member functions
>> will not be able to access the data members in the base class.
>
> This appears to do exactly what I want!
>
> There must be some good reason that Scott Meyers failed to suggest this
> alternative. Surely he's aware of it.


This would violate another important OO principle, i.e. derive in order to be
reused, not to reuse. Also derivation comes with its own member fees, so instead
of one problem in base class we create another problem in derived.

Regarding the "enemy" proposal, I don't see how such function would be better
than non-member non-friend. Even syntactically, non-member functions are more
advantageous than members, just think about templates and non-class types.

--
Gene Bushuyev (www.gbresearch.com)
----------------------------------------------------------------
To see what is in front of one's nose needs a constant struggle ~ George Orwell

I V

unread,
Jun 5, 2006, 1:47:46 PM6/5/06
to
On Sat, 03 Jun 2006 20:32:24 -0400, Maxim Yegorushkin wrote:
> Is not that function a member function in a derived class? You can put
> all data members in a base class so that deirved class member functions
> will not be able to access the data members in the base class.

That seems like an excellent idea. In particular, it avoids the problem
that the practice suggested by Meyers makes the format used to call a
function dependent on the implementation detail of whether or not that
function has to access class internals. I've tried in the past to avoid
that problem by not using member functions at all, instead using free
functions which are made friends where necessary, but that's so unexpected
as to be actively hostile to anyone reading the code. Your approach
pretty much avoids that problem. It's a bit clunky to have to declare two
classes, though, particularly as you would have to write constructors in
the derived class to forward to each constructor of the base class.

Valentin Samko

unread,
Jun 5, 2006, 1:46:18 PM6/5/06
to
Mark Santaniello wrote:
> On Sat, 03 Jun 2006 20:32:24 -0400, Maxim Yegorushkin wrote:
>
>> Is not that function a member function in a derived class? You can put
>> all data members in a base class so that deirved class member functions
>> will not be able to access the data members in the base class.
>
> This appears to do exactly what I want!
>
> There must be some good reason that Scott Meyers failed to suggest this
> alternative. Surely he's aware of it.

The main problem with a member function in a derived class is that you can not call it on
an object of another derived class! You do not have this problem with non member functions.

--

Valentin Samko - http://www.valentinsamko.com

patrik...@sofe.nu

unread,
Jun 5, 2006, 1:51:17 PM6/5/06
to
> There must be some good reason that Scott Meyers failed to suggest this
> alternative. Surely he's aware of it.

In an inteview found on http://www.artima.com/intv/goldilocks4.html
Bjarne Stroustrup explains why such a solution is not a good idea:

<QUOTE>
"
...I've been preaching this song for the better part of 20 years. But
people got very keen on putting everything in classes and hierarchies.
I've seen the Date problem solved by having a base class Date with some
operations on it and the data protected, with utility functions
provided by deriving a new class and adding the utility functions. You
get really messy systems like that, and there's no reason for having
the utility functions in derived classes. You want the utility
functions to the side so you can combine them freely. How else do I get
your utility functions and my utility functions also? The utility
functions you wrote are independent from the ones I wrote, and so they
should be independent in the code. If I derive from class Date, and you
derive from class Date, a third person won't be able to easily use both
of our utility functions, because we have built dependencies in that
didn't need to be there. So you can overdo this class hierarchy stuff.
"
</QUOTE>

kanze

unread,
Jun 6, 2006, 9:42:45 AM6/6/06
to
I V wrote:
> On Sat, 03 Jun 2006 20:32:24 -0400, Maxim Yegorushkin wrote:

> > Is not that function a member function in a derived class?
> > You can put all data members in a base class so that deirved
> > class member functions will not be able to access the data
> > members in the base class.

> That seems like an excellent idea. In particular, it avoids
> the problem that the practice suggested by Meyers makes the
> format used to call a function dependent on the implementation
> detail of whether or not that function has to access class
> internals. I've tried in the past to avoid that problem by not
> using member functions at all, instead using free functions
> which are made friends where necessary, but that's so
> unexpected as to be actively hostile to anyone reading the
> code. Your approach pretty much avoids that problem. It's a
> bit clunky to have to declare two classes, though,
> particularly as you would have to write constructors in the
> derived class to forward to each constructor of the base
> class.

I think the real problem is to define the basic abstraction of
the class, and make it clear what that abstraction is. For
historical reasons, this is almost impossible for a standard
string class -- there are too many conflicting requirements.
But the string class is very much an exception in this. (My
original, pre-standard String class has exactly two member
functions: extract and replace. My idea was that everything
else could be expressed in terms of these two functions. Due to
user preasure, however, it grew to have one of the largest
interfaces in my library.)

Once you've defined this minimum abstraction, it becomes natural
to think in terms of global functions which operate on it.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

kanze

unread,
Jun 6, 2006, 9:41:33 AM6/6/06
to
Mark Santaniello wrote:
> On Sat, 03 Jun 2006 20:32:24 -0400, Maxim Yegorushkin wrote:

> > Is not that function a member function in a derived class?
> > You can put all data members in a base class so that deirved
> > class member functions will not be able to access the data
> > members in the base class.

> This appears to do exactly what I want!

> There must be some good reason that Scott Meyers failed to
> suggest this alternative.

Maybe because it doesn't work.

Consider the case of std::string, which Herb wrote about.
Suppose that I want to add functions for trimming and padding.
I derive:

class StringWithTrimAndPad : public std::string
{
// ...
} ;

Now what happens:

1. I have to duplicate all of the constructors of std::string
in my derived class.

2. Functions which are passed an std::string cannot use my new
functions; the entire application (including any third party
libraries!) must adapt the new string class.

3. If by any chance, someone does use std::string, and delete
through a pointer to it, I've undefined behavior.
Similarly, if I pass someone a StringWithTrimAndPad[], and
he is declared to take a std::string[].

4. And what happens if someone else in the project wants to do
the same thing, to add some special functions of his?

There is a more or less fundamental issue, in that we have two
syntaxes for calling a function, or rather for passing the first
parameter of the function. In my experience, however, except
for some very few basic classes like string, it's more a feature
than a problem -- when virtual functions are involved, I would
consider it definitly a feature, highlighting the object on
which the function call resolution depends.

Note that if the difference in syntax between s.append( t ) and
trim( s ) bothers you, there's nothing which stops you from
defining your own append( s, t ). And I agree that in a few
cases (probably not enough to justify a change in the language),
it would be nice to be able to go the other way; I, for one,
find s.append( t ).insert( u, 3 ).trim() more intuitive than
trim( insert( append( s, t ), u ), 3 ). (Of course, the current
versions of string::append and string::insert don't really
support this the way I'd like either -- they should be const
functions returning a new instance.)

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

kanze

unread,
Jun 6, 2006, 9:42:20 AM6/6/06
to
I V wrote:
> On Sat, 03 Jun 2006 20:32:24 -0400, Maxim Yegorushkin wrote:

> > Is not that function a member function in a derived class?
> > You can put all data members in a base class so that deirved
> > class member functions will not be able to access the data
> > members in the base class.

> That seems like an excellent idea. In particular, it avoids
> the problem that the practice suggested by Meyers makes the
> format used to call a function dependent on the implementation
> detail of whether or not that function has to access class
> internals. I've tried in the past to avoid that problem by not
> using member functions at all, instead using free functions
> which are made friends where necessary, but that's so
> unexpected as to be actively hostile to anyone reading the
> code. Your approach pretty much avoids that problem. It's a
> bit clunky to have to declare two classes, though,
> particularly as you would have to write constructors in the
> derived class to forward to each constructor of the base
> class.

I think the real problem is to define the basic abstraction of


the class, and make it clear what that abstraction is. For
historical reasons, this is almost impossible for a standard
string class -- there are too many conflicting requirements.
But the string class is very much an exception in this. (My
original, pre-standard String class has exactly two member
functions: extract and replace. My idea was that everything
else could be expressed in terms of these two functions. Due to
user preasure, however, it grew to have one of the largest
interfaces in my library.)

Once you've defined this minimum abstraction, it becomes natural
to think in terms of global functions which operate on it.

--


James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

0 new messages