Re: Restricting access should be illegal?
Daniel James wrote:
In article news:<44c77720$1@news.kapsch.co.at>, Gerhard Menzl wrote:
Er ... it's just one extra line.
Who said anything about lines?
You did. You wrote:
I don't care what the compiled code looks like, I am talking about
source code cruft, ...
This is getting a bit silly, I'm afraid, but I don't see lines mentioned
anywhere in there.
You advocate replacing:
class Callback
{
public:
virtual void CallMeBack (CallbackData d, Parameter p) = 0;
};
with:
class Callback
{
public:
void CallMeBack (CallbackData data, ExtraParameter expar)
{
this->DoCallMeBack (data, expar);
}
private:
virtual void DoCallMeBack (CallbackData d, Parameter p) = 0;
};
So you get two function declarations and one function invocation for one
function declaration. Every parameter appears three times instead of
once. That's a factor of 3 : 1. I don't care what the respective numbers
of lines are, it's three times as much code that has to be maintained.
I concede that if you take derived classes into account, the overall
ratio will be lower, but the interface class is blown up by a factor of
three.
Now you're talking about source again ...
I am talking about source code all the time.
I would say that the code in which Member has different access types
in the two classes is less 'obvious': we all know what it means and
what it does, but it has to be thought about more, and so more
requires more effort from the maintenance programmer, and so is more
likely to engender bugs.
It is in the nature of idioms that they have to be thought about *less*,
and that they are *less* likely to engender bugs, providing they are
applied correctly, of course.
What is being made explicit is the separation between interface and
implementation. I'd call that "documentation". Couple that with the
advantages of having a single point of entry for debugging, logging,
etc., that I mentioned in my last posting and to which you have made
no reference I would say that making it explicit is advantageous.
The separation of interface and implementation is made explicit and
documented sufficiently by the existence of an abstract base class and a
concrete class derived from it. Any programmer vaguely familiar with
object-oriented design and design patterns will recognize this instantly.
I don't see what "a single point of entry for debugging" buys you. I
have never found the need for regularly breaking execution at every
virtual function call. If you need to log in the base class, then, of
course, it ceases to be a mere abstract interface, and hiding the
virtual function behind a public non-virtual function is justified, just
as it is with precondition checks. But logging is part of the design,
not something that you add willy-nilly.
Perhaps I'm being stupid (it's the heat) but I don't see what you're
getting at -- how does it help to change the access type of a virtual
function within the class hierarchy in these cases? What does it
enable you to achieve that couldn't be achieved if this (to my eyes)
rather dirty trick were not allowed?
I've written callbacks and observers without using it, and not been
aware of any particular difficulty, so please enlighten me!
// code that does nothing to illustrate the point, such as
// constructors, destructors, error handling, etc. omitted
class NetworkObserver
{
public:
virtual void HandleNetworkEvent(NetworkEvent* event) = 0;
};
class NetworkDispatcher
{
public:
static NetworkDispatcher& Instance();
void Register(NetworkObserver* observer, int portno);
void Unregister(NetworkObserver* observer);
// private implementation details here
};
Now suppose you want to design a class that needs to react to network
events on a particular port to fulfill its role. Let's call it
Communicator for want of a better example. Communicator derives from
NetworkObserver, overrides HandleNetworkEvent(), and registers with the
NetworkDispatcher. If you keep the original access level,
HandleNetworkEvent() becomes part of the public interface of
Communicator. Every client may call it. But of course that is not what
you want. If HandleNetworkEvent() is called, you want to be sure it's
called by the NetworkDispatcher (or one of its subcontractors), and
nobody else. By making it public you would convey a false message to
clients. They are not meant to call you back, so they should not be
allowed to. In C++, you can solve this elegantly:
class Communicator : private NetworkObserver
{
public:
void Start()
{
NetworkDispatcher::Instance().Register (this, ourPort);
}
void Stop()
{
NetworkDispatcher::Instance().Unregister (this);
}
// rest of public interface
private:
virtual void HandleNetworkEvent(NetworkEvent* event)
{
// do whatever you need to do in response
}
static int const ourPort = 10000;
};
Inflating NetworkObserver in your preferred way buys you nothing. No
need for logging, no precondition checking - it's a simple callback
interface. Note that languages like Java and C#, which do not allow you
to change access levels in derived classes, also make it impossible to
wrap the virtual function call in the base class in a non-virtual
function, unless you restrict yourself to single inheritance (i.e. never
terminate more than one protocol). So much for protecting developers
from themselves.
It looks to me more like giving the programmer feet he didn't know he
had, and putting them directly in front of his gun <smile>
C++ has a number of quirks that meet your metaphoric description, such as
Class object();
being parsed as a function declaration, or
template <typename Container = std::vector<int>>
as a bit shift to the right. I don't think the orthogonality of access
level and overriding belongs in this category. Access level governs who
is allowed to call a function or access a variable. It does not affect
overriding. While it may surprise people, it's conceptually clean and
consistent. That many C++ programmers are not familiar enough with the
language to be aware of this does not qualify it as a dirty quirk.
--
Gerhard Menzl
#dogma int main ()
Humans may reply by replacing the thermal post part of my e-mail address
with "kapsch" and the top level domain part with "net".
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]