Topic: Generalizing pure functions


Author: robertk@rkrajewski.lotus.com (Robert Krajewski)
Date: 18 Sep 91 18:16:10 GMT
Raw View
Pure virtual functions were added to C++ as a way of enforcing the
specification of a pure virtual class, that is, one that could not be
constructed except through subclasses with public constructors. By
specifying a virtual function but explicitly leaving it to be
implemented by a subclass, one could define a class member function
protocol without having to stub out useless dummy functions -- the
compiler (and, perhaps, the linker or run-time system) would enforce
the implementation of pure virtual function in any subclass.

I would like to generalize pure virtual functions to pure functions:
that is, a pure-specifier ( = 0 ) can be used with the same semantics
for either virtual *or* non-virtual functions. The difference is that,
for a non-virtual function, a compiler will not allow the invocation
of a member function unless the class of the object has declared or
inherited a real (non-pure) version of the member function. In other
words, given:

class AbstractSet {
   public:
   int IsMember(int elt) const = 0; // to be implemented by subclasses
   ...
};

class SetAsTree : public AbstractSet {
   public:
   int IsMember(int elt) const; // declaration promises implementation
   ...
};

the compiler will issue an error in the following code fragment:

void foo(const AbstractSet & aset)
{
    ...
    if (aset.IsMember(something)) { // no implementation for AbstractSet !
    ...
    }
}

Of course, if IsMember is pure *virtual*, the call is perfectly legal,
as before. If a pure function is non-virtual, it simply places a
constraint upon its use (by requiring more information at compile time
that than of a pure virtual function).

Here are the motivations for this change:

1. It allows a class designer to specify a set of required member
functions (from subclasses) without incurring the overhead of virtual
functions. One should be able to define an abstract class that defines
the interfaces of a set of potentially mutually replaceable sibling
classes. I can define what a set class is supposed to look like, with
the confidence that I can replace one subclass with another at time
compile time, and then interfaces will still be compatible.

2. It removes an option that can be used only with another option,
thus making the syntax of member function declaration a little less
idiosyncratic and regular. Currently, ARM states that the
pure-virtual-specifier can only be used if the virtual keyword is
used, without reflecting this in the (informal) BNF grammer for member
function declaration.

3. It extends the checking features of pure virtual functions further
into the compile-time domain, freeing it from the cost of the virtual
function mechanism. Such an extension is in the C++ spirit of
compile-time checking for run-time efficiency.

The only way that the generalized pure declaration complicates
declaration handling is the action to be taken when a subclass
redeclares a member function, declared by its superclass as pure, as
pure. Is this an error ? Or does it merely warrant a warning ?




Author: exmalo@eras70.ericsson.se (Mats Lovkvist Exjobb R/UP)
Date: 19 Sep 91 08:04:57 GMT
Raw View
In article <ROBERTK.91Sep18181610@rkrajewski.lotus.com> robertk@rkrajewski.lotus.com (Robert Krajewski) writes:

   1. It allows a class designer to specify a set of required member
   functions (from subclasses) without incurring the overhead of virtual
   functions. One should be able to define an abstract class that defines
   the interfaces of a set of potentially mutually replaceable sibling
   classes. I can define what a set class is supposed to look like, with
   the confidence that I can replace one subclass with another at time
   compile time, and then interfaces will still be compatible.

This is a good example where you would NOT want 'pure functions' because
they would force the programmer to change the declaration for each and every
variable of the replaced class.  If replaceable sibling classes is the goal,
this is a bad thing imho.
I.e. if you want an abstract base class it is fundamental to be able
to declare variables being of the base class type everywhere in the code
where it is not necessary to know about which implementation is used.
This makes 'pure functions' more or less unusable (except maybe in private
abstract base classes).
      _
Mats Lofkvist
d87-mal@nada.kth.se




Author: robertk@rkrajewski.lotus.com (Robert Krajewski)
Date: 20 Sep 91 07:12:56 GMT
Raw View
In article <EXMALO.91Sep19090457@eras70.ericsson.se> exmalo@eras70.ericsson.se (Mats Lovkvist Exjobb R/UP) writes:

   In article <ROBERTK.91Sep18181610@rkrajewski.lotus.com> robertk@rkrajewski.lotus.com (Robert Krajewski) writes:

      1. It allows a class designer to specify a set of required member
      functions (from subclasses) without incurring the overhead of virtual
      functions. One should be able to define an abstract class that defines
      the interfaces of a set of potentially mutually replaceable sibling
      classes. I can define what a set class is supposed to look like, with
      the confidence that I can replace one subclass with another at time
      compile time, and then interfaces will still be compatible.

   This is a good example where you would NOT want 'pure functions' because
   they would force the programmer to change the declaration for each and every
   variable of the replaced class.  If replaceable sibling classes is the goal,
   this is a bad thing imho....

Well, in my scenario, I'm willing to change the types of the classes
in the program text of my clients (from, say, SetAsTree to
SetAsVector) in exchange for not having the overhead of virtual
functions. I'm willing to use pure functions as a specification
mechanism and nothing more.

By divorcing pure from virtual, I was hoping to orthogonalize part of
the language -- the resulting expanded semantics are still useful. In
a way, the new uses are a step back towards C (away from runtime
dispatch), but that's certainly not a characteristic alien to C++ in
relation to other object-oriented programming languages. (Before I was
at Lotus, I was implementing Lisps at Gold Hill and LMI, so I do have
a pretty good idea of what ``real'' object programming is supposed to
look like.)




Author: pena@brainware.fi (Olli-Matti Penttinen)
Date: 20 Sep 91 17:06:45 GMT
Raw View
In article <ROBERTK.91Sep20091256@rkrajewski.lotus.com> robertk@rkrajewski.lotus.com (Robert Krajewski) writes:

   By divorcing pure from virtual, I was hoping to orthogonalize part of
   the language -- the resulting expanded semantics are still useful. In
   a way, the new uses are a step back towards C (away from runtime
   dispatch), but that's certainly not a characteristic alien to C++ in
   relation to other object-oriented programming languages.

Orthogonalize? Quite the opposite, I think. Surely, on a purely
syntactic level your proposal is quite clean. As you pointed out
yourself, separating pure form virtual is something the informal ARM
grammar actually has done.

However, although I feel very strongly about notation, semantics are
the most important aspect of any formal language. Unfortunately your
proposal would lead to yet another way of restricting the
instantiation of a class. So far, I have been able to convince myself
that protected constructors and pure virtuals both are a *good thing*.
Together they form a very powerful, fairly straightforward and
understandable set of tools to construct truly abstract base classes
in a type safe manner.

Pure non-virtual member functions would not add to that set. Instead,
to me such a declaration says: I am a base class. You must derive from
me (because of `pure'). Probably in multiple ways (otherwise the whole
consept wouldn't make sense). You need to be able to perform this
operation on yourself (the pure non-virtual declaration). Sorry, I
cannot help you either in defining or carrying out of the operation
(because it is statically bound).

Sounds like `besser-wisserism' to me.

Conclusion: if you want to write mutually interchangable classes,
fine. But littering the language definition with provisions for such
is neither necessary nor beneficial.
--
Olli-Matti Penttinen <pena@brainware.fi> | "When in doubt, use brute force."
Brainware Oy                             |    --Ken Thompson
P.O.Box 330                              +----------------------------------
02151  ESPOO, Finland       Tel. +358 0 4354 2565       Fax. +358 0 461 617




Author: john@bnrmtl.bnr.ca (John Hickin)
Date: 20 Sep 91 15:04:33 GMT
Raw View
In article <ROBERTK.91Sep18181610@rkrajewski.lotus.com>, robertk@rkrajewski.lotus.com (Robert Krajewski) writes:
|> Pure virtual functions were added to C++ as a way of enforcing the
|> specification of a pure virtual class, that is, one that could not be
|> constructed except through subclasses with public constructors. By
|> specifying a virtual function but explicitly leaving it to be
|> implemented by a subclass, one could define a class member function
|> protocol without having to stub out useless dummy functions -- the
|> compiler (and, perhaps, the linker or run-time system) would enforce
|> the implementation of pure virtual function in any subclass.
|>
|> I would like to generalize pure virtual functions to pure functions:
|> that is, a pure-specifier ( = 0 ) can be used with the same semantics
|> for either virtual *or* non-virtual functions. The difference is that,
|> for a non-virtual function, a compiler will not allow the invocation
|> of a member function unless the class of the object has declared or
|> inherited a real (non-pure) version of the member function. In other
|> words, given:

// (etc., deleted)

I think that the idea of pure non-virtual member functions has merit.  When
reading that posting, however, I got the feeling that the proposal sought
to outlaw the ABC desigbner from implementing a pure member function.  If
such was his intent I disagree as nothing prevents one from implementing
pure virtuals;  one is only prohibited to invoke them through the virtual
call mechanism as there is no entry in the virtual function table.

Here is an example where you really want to have an implementation for a
pure function:

 class Abstract{ public: Abstract() {}  virtual ~Abstract() = 0; };
 inline Abstract::~Abstract() {}

I am sure that there are situations where an implementation of a pure
non-virtual would be useful.  This comes to mind:

 class AbstractNode { public: ...
  virtual void foo() = 0;
  void display() = 0;
 };

 void AbstractNode::display() { // basic code to drive xwindow stuff }

The fact that display() is pure warns the client that derived classes must
implement it.  They can inherit `explicitly' as in

 void MyNode :: display()
 {
  //pre-base display stuff here
  AbstractNode::display();
  //post-base display stuff here
 }

Right now the only way to achieve this is to make display virtual which
causes the client to incur the overhead of the virtual function call
mechanism.

I see only 1 problem with the idea and that is with pointers to member
functions.  My (Sun C++) compiler has no trouble with the following:

 typedef void (AbstractNode::*PVmfV) ();
 PVmfV pFunc = AbstractNode::foo;
 AbstractNode* pN = new MyNode();
 (pN ->* pFunc) ();
 //
 // Calls MyNode::foo, virtual call.

But what would happen if we were to assign AbstractNode::display to pFunc
instead?  We would be OBLIDGED to call the base class version which might
not be implemented!  We couldn't call a derived class version;  just what
derived class did the programmer mean?  MyOtherNode : public AbstractNode?

So either ban pointers to pure non-virtual member functions or require an
implementation (or just leave C++ as it is now).

--
John Hickin      Bell-Northern Research, Montreal, Quebec
(514) 765-8888   bnrmtl!john@larry.mcrcim.mcgill.edu




Author: dfoster@jarthur.claremont.edu (Derek R. Foster)
Date: 24 Sep 91 19:25:09 GMT
Raw View
In article <ROBERTK.91Sep18181610@rkrajewski.lotus.com> robertk@rkrajewski.lotus.com (Robert Krajewski) writes:
>I would like to generalize pure virtual functions to pure functions:
>that is, a pure-specifier ( = 0 ) can be used with the same semantics
>for either virtual *or* non-virtual functions. The difference is that,
>for a non-virtual function, a compiler will not allow the invocation
>of a member function unless the class of the object has declared or
>inherited a real (non-pure) version of the member function.

I was thinking exactly the same thing a couple of days ago -- I would love
to see a formal proposal of this written up. Another reason (or possibly the
same?) reason would be to explicitly disable calls to a function that only
makes sense when applied to a base class, instead of simply declaring the
function as a private function that does nothing, which seems to be the
current solution (or am I wrong about this?).

i.e.
class Base
{
  A_Base_function() { whatever };
}

class Derived1
{
  A_Base_function() = 0;
}

void main()
{
  Base    A;
  Derived B;
  A.A_Base_function(); // OK
  B.A_Base_function(); // Compiler error: A_Base_function declared pure!
}



Derek Riippa Foster




Author: kelley@mpd.tandem.com (Michael Kelley)
Date: 27 Sep 91 15:21:03 GMT
Raw View
In article <1991Sep24.192509.2559@muddcs.claremont.edu>,
dfoster@jarthur.claremont.edu (Derek R. Foster) writes:
> In article <ROBERTK.91Sep18181610@rkrajewski.lotus.com>
robertk@rkrajewski.lotus.com (Robert Krajewski) writes:
> >I would like to generalize pure virtual functions to pure functions:
> >that is, a pure-specifier ( = 0 ) can be used with the same semantics
> >for either virtual *or* non-virtual functions. The difference is that,
> >for a non-virtual function, a compiler will not allow the invocation
> >of a member function unless the class of the object has declared or
> >inherited a real (non-pure) version of the member function.
>
> I was thinking exactly the same thing a couple of days ago -- I would love
> to see a formal proposal of this written up. Another reason (or possibly the
> same?) reason would be to explicitly disable calls to a function that only
> makes sense when applied to a base class, instead of simply declaring the
> function as a private function that does nothing, which seems to be the
> current solution (or am I wrong about this?).
>
> i.e.
> class Base
> {
>   A_Base_function() { whatever };
> }
>
> class Derived1
> {
>   A_Base_function() = 0;
> }
>
> void main()
> {
>   Base    A;
>   Derived B;
>   A.A_Base_function(); // OK
>   B.A_Base_function(); // Compiler error: A_Base_function declared pure!
> }
>
>
>
> Derek Riippa Foster

That's fine when the compiler knows it's dealing with an object of class
B, which is
not the case most of the time -- it's usually dealing with a pointer or
reference to
an object of a base class.  If the function redefined to be 'pure' is
virtual, you're
in as much (if not more) trouble than you were without redefining it as
pure (you'd
have to hope that the implementation of "pure_virtual_called" does the
right thing
for your application). Since we have exception handling, we just throw
an exception
in the function for the derived class:

B::A_Base_function()
{
 throw PureVirtual(__FILE__, __LINE__);
}

main()
{
 Base A;
 Derived B;
 Base *AP;

 try {
  AP = &A;
  AP->A_Base_function();
  AP = &B; // compiler doesn't know this is "bad"
  AP->A_Base_function();
 }
 catch (PureVirtual &pv) {
  ...
  exit(1);
 }

 return 0;
}

Until exception handling becomes public, I think making the function private
in the derived class will have to suffice.

Mike Kelley
Tandem Computers, Austin, TX
kelley@mpd.tandem.com
(512) 244-8830 / Fax (512) 244-8247