[comp.object] Dumb question

abbott@aerospace.aero.org (Russell J. Abbott) (11/22/89)

As a non-C++ programmer but as someone who has programmed in Smalltalk I
beg your indulgence for a dumb question.  

To what extent can one dynamically bind code in C++?  For example, in
Smalltalk one can send a message to an object which has been passed as a
parameter.  Since Smalltalk does not have static typing, there is no way
of knowing ahead of time which piece of code will be executed, or even
which of several pieces of code as would be the case, I gather, with
virtual functions.  One does not even know ahead of time whether the
object to which the message is sent will have a method defined that
matches the call, in which case an exception is raised.

More concretely, can one write a sort routine that will sort a list of
any type (perhaps not yet even defined) that responds to "<"?   I would
assume that one could, but it wasn't clear to me from the C++ material
that I have how to do it.  

Thanks.

-- 
-- Russ abbott@itro3.aero.org

sakkinen@tukki.jyu.fi (Markku Sakkinen) (11/24/89)

In article <61737@aerospace.AERO.ORG> abbott@itro3.org (Russell J. Abbott) writes:
>As a non-C++ programmer but as someone who has programmed in Smalltalk I
>beg your indulgence for a dumb question.  

The question (that follows) is by no means dumb!
It concerns the basic difference between weakly and strongly typed
object languages. Although variants of this question have been discussed
rather often in these newsgroups, many readers could be interested
in a recapitulation.

>To what extent can one dynamically bind code in C++? [...]
>
>More concretely, can one write a sort routine that will sort a list of
>any type (perhaps not yet even defined) that responds to "<"?   I would
>assume that one could, but it wasn't clear to me from the C++ material
>that I have how to do it.  

The answer is No and Yes.

No, you cannot write it so that it would apply to _any type_ that responds
to "<". Note that, in contrast to more purely OO languages, C++ has many
types that are not classes.

Yes, with Release 2.0 of C++ now having multiple inheritance,
you can replicate the example done in Eiffel in Bertrand Meyer's
"Object-oriented Software Construction" (19.4.1, p. 411).
You define an abstract class COMPARABLE that declares at least one
suitable relational operator as a member function, then use COMPARABLE
as one base class of all those classes to which you want your sort routine
to apply. But you will have to write the code implementing the member
function at least for each immediate subclass ("derived class") of
COMPARABLE. This can mean writing "wrapper classes" around existing
classes and types, as is done for INTEGER in Meyer (19.4.2, p. 412).

Note 1: I have probably read
about some (at least proposed) strongly typed OO language(s)
in which this can be done more easily, i.e. the sort routine can be
defined to apply to all existing classes that have a "<" operation
(yielding a Boolean result). Someone name an example?

Note 2: An old favourite idea of mine is that classes should offer,
as a primitive operation, rather one comparison operator (values:
equal, less, greater, incomparable, equivalent; possibly others
in special cases) than several relational operators. The latter
can be derived from the former, and class interfaces would be simpler.

Markku Sakkinen
Department of Computer Science
University of Jyvaskyla (a's with umlauts)
Seminaarinkatu 15
SF-40100 Jyvaskyla (umlauts again)
Finland

dlw@odi.com (Dan Weinreb) (11/24/89)

In article <61737@aerospace.AERO.ORG> abbott@aerospace.aero.org (Russell J. Abbott) writes:

   More concretely, can one write a sort routine that will sort a list of
   any type (perhaps not yet even defined) that responds to "<"?   I would
   assume that one could, but it wasn't clear to me from the C++ material
   that I have how to do it.  

What you'd have to do in C++ is:
(1) Define a base class that has a virtual less_than operation; call it xxx.
(2) Declare the sort routine to accept a (pointer to an) object of type xxx.
(3) For all the types you ever want to sort, make those types in inherit from xxx.

The compile-time type checking in C++ assures that the argument will
be an object for which less_than is declared.  This extra checking can
be helpful, catching errors at compile-time, which compensates to some
degree for the loss of flexibility compared to Smalltalk-80.

(The question of the relative values of the two approaches is extremely
complicated, depends on many other tradeoffs besides this one, and
tends to be a bit of a religious war, so won't try to elaborate on it
here.  I have now had extensive experience in both the Lisp/CLOS world
and in the C++ world, and one thing I know for sure is that each
approach has some advantages compared to the other.)

If you like the Smalltalk style that you described, but you'd like to
use C++, a good bet is to use the NIH class library (formerly known as
OOPS, available from your usual friendly distributor of public domain
software, e.g. uunet).  By using NIH classes, and deriving your own
classes from the NIH hierarchy, you can get much of the behavior
you're talking about.  This is still not as flexible as Smalltalk-80
(since you can't add new virtual function members to class Object
(without modifying the library, which can be done but has its own
drawbacks)), but it's an interesting point in tradeoff space that's
probably just right for some programmers and applications.

Dan Weinreb		Object Design, Inc.		dlw@odi.com

ewan@cs.washington.edu (Ewan Tempero) (11/27/89)

In article <61737@aerospace.AERO.ORG> abbott@itro3.org (Russell J. Abbott) writes:
>More concretely, can one write a sort routine that will sort a list of
>any type (perhaps not yet even defined) that responds to "<"?   I would
>assume that one could, but it wasn't clear to me from the C++ material
>that I have how to do it.  
You don't. If you want to sort a list, you don't apply a "sort" operation
to the list, you ask the list to sort itself. Since the list is an object
(we are talking about object-oriented systems where everything is an
object, right?) it should know its own representation (including the
type of the objects in the list) and so its "sort" operation will know
what to do. If it doesn't have a "sort" operation then how can you possibly
sort it? If you want to pass it to a "sorting" routine then you are 
not following the object-oriented paradigm.

In general, many so-called "polymorphic" functions don't exist in OO systems.
Either the object concerned knows how to perform that function (and so
knows its own representation) or it doesn't. If it doesn't, then the problem 
is in the design of the application.
[note: my comments have been stated as fact however they are, of course,
my own opinions]
--ewan

sakkinen@tukki.jyu.fi (Markku Sakkinen) (11/27/89)

In article <9938@june.cs.washington.edu> ewan@june.cs.washington.edu (Ewan Tempero) writes:
>In article <61737@aerospace.AERO.ORG> abbott@itro3.org (Russell J. Abbott) writes:
>>More concretely, can one write a sort routine that will sort a list of
>>any type (perhaps not yet even defined) that responds to "<"?   I would
>>assume that one could, but it wasn't clear to me from the C++ material
>>that I have how to do it.  
>You don't. If you want to sort a list, you don't apply a "sort" operation
>to the list, you ask the list to sort itself. Since the list is an object
>(we are talking about object-oriented systems where everything is an
>object, right?) it should know its own representation (including the
>type of the objects in the list) and so its "sort" operation will know
>what to do. If it doesn't have a "sort" operation then how can you possibly
>sort it? If you want to pass it to a "sorting" routine then you are 
>not following the object-oriented paradigm.
>[...]

I think you didn't notice that the question pertained specifically to C++,
where not by far everything is an object! The slogan comes from the
Smalltalk community, but if you take a sharp look at Smalltalk-80,
you will note that at least variables are _not_ objects! It is only a kind
of very puristic OO dogma that no "free" functions or procedures should
be allowed, but they should always be methods of some class (or object).

Apparently you also misread the question as meaning 'the _list_ responds
to "<"', while the purpose was 'the [element] type responds to "<"'.
So the question can be understood as: "How do I write a sort operation
(member function) for a general list class in C++?"
Thus, it does not necessarily imply a deviation from the pure OO paradigm.

See article <2122@tukki.jyu.fi> for one answer to the original question.

Markku Sakkinen
Department of Computer Science
University of Jyvaskyla (a's with umlauts)
Seminaarinkatu 15
SF-40100 Jyvaskyla (umlauts again)
Finland

jimad@microsoft.UUCP (Jim Adcock) (11/29/89)

// in any case -- here's a quick and dirty example of what I think 
// people have been talking about....

#include <stdio.h>
#include <string.h>

void Fatal() 
{ fprintf(stderr, "Smalltalk-like runtime error\n"); exit(1); }

char* base__classname = "base";
class base
{
public:
	virtual char* classname() {return base__classname; }
	virtual int compare(base& b)
	{if (classname() != b.classname()) Fatal(); else return 0;}
	virtual base& print() {printf("base\n"); return *this;}
	friend int operator==(base& a, base& b){return a.compare(b)==0;}
	friend int operator!=(base& a, base& b){return a.compare(b)!=0;}
	friend int operator>=(base& a, base& b){return a.compare(b)>=0;}
	friend int operator<=(base& a, base& b){return a.compare(b)<=0;}
	friend int operator>(base& a, base& b){return a.compare(b)>0;}
	friend int operator<(base& a, base& b){return a.compare(b)<0;}
};

char* Int__classname = "Int";
class Int : public base
{
	int i;
public:
	Int(int ii) { i = ii; }
	base& print(){ printf("%d\n",i); return *this;}
	char* classname() { return Int__classname; }
	int compare(base& b) 
	{if (b.classname() != classname()) Fatal(); else return i-((Int*)&b)->i;
	}
};

char* Double__classname = "Double";
class Double : public base
{
	double i;
public:
	Double(double ii) { i = ii; }
	base& print(){ printf("%g\n",i); return *this;}
	char* classname() { return Double__classname; }
	int compare(base& b) //this is a slightly bogus def for "compare":
	{ if (b.classname() != classname()) Fatal(); 
	  else return (int)(1.0e10*(i-((Double*)&b)->i));
	}
};

char* String__classname = "String";
class String : public base
{
	char* i;
public:
	String(char* ii) { i = ii; }
	base& print(){ printf("%s\n",i); return *this;}
	char* classname() { return String__classname; }
	int compare(base& b)
	{ if (b.classname() != classname()) Fatal(); 
	  else return strcmp(this->i, ((String*)&b)->i); 
	}
};

class List4	//cheap imitation of a "real" list class
{
  	base* p[4];
public:
	List4(base* p0, base* p1, base *p2, base *p3) 
	{p[0]=p0; p[1]=p1; p[2]=p2; p[3]=p3;}
	List4& print();
	List4& sort();
};

List4& List4::print()
{
	printf("(\n"); 
	for (int i=0; i<4; ++i) p[i]->print(); 
	printf(")\n"); 
	return *this;
}

List4& List4::sort()	//bogus
{
	for (int j=0; j<3; ++j) for (int i=0; i<3; ++i)
		if (*p[i] > *p[i+1])
		{
			base* tmp = p[i];
			p[i] = p[i+1];
			p[i+1] = tmp;
		}

	return *this;
}

main()
{
	Int i6(6);
	Int i5(5);
	Int i_100(-100);
	Int i200(200);
	Int i5a(5);
	Double d5(5);
	Double d6(5);
	String himom("hi mom");
	String hidad("hi dad");
	String hibro("hi bro");
	String hisis("hi sis");
	String hidad2("hi dad");
	printf("%d\n", i5 == i5a);
	printf("%d\n", i5 > i6);
	printf("%d\n", i5 == i5);
	printf("%d\n", d5 < d6);
	printf("%d\n", d5 == d5);
	printf("%d\n", himom == hidad);
	printf("%d\n", hidad == hidad2);
	printf("%d\n", hidad > himom);
	List4 L1(&himom, &hidad, &hibro, &hisis);
	L1.print().sort().print();
	List4 L2(&i6, &i5, &i_100, &i200);
	L2.print().sort().print();
	printf("%d\n", i5 > himom);	//ack! compare of unlike objects!
}

gjditchfield@watmsg.waterloo.edu (Glen Ditchfield) (11/30/89)

In article <9191@microsoft.UUCP> jimad@microsoft.UUCP (Jim Adcock) writes:
>// in any case -- here's a quick and dirty example of what I think 
>// people have been talking about....
[Code that sketches a sortable list omitted.]

Note that the list's sort() function used the `<' function defined by the
list element class, which in turn used a virtual compare() function.  I
think some people were talking about this, but other people wrote about a
scheme where the compare() function is a virtual function of the list
class.
   I like putting compare() in the list class better.  You can have
different lists that sort the same element type in different ways; invoices
sorted by customer in one list, and by date in another.  The sort routine
isn't bound to one element class function name, like `<'.  You can sort by
things like string length, which wouldn't normally be done with `<'.
   In practice, in C++, wouldn't it be simpler to pass a comparison
function as an argument to the sort() function?  If the list's elements are
always sorted in the same order, the list insertion operation would insert
elements in that order in the first place.

    Glen Ditchfield  gjditchfield@violet.uwaterloo.ca  Office: DC 2517
Dept. of Computer Science, U of Waterloo, Waterloo, Ontario, Canada, N2L 3G1
			 Gorbachev is a CIA mole.

weiner@novavax.UUCP (Bob Weiner) (11/30/89)

> If you like the Smalltalk style that you described, but you'd like to
> use C++, a good bet is to use the NIH class library (formerly known as
> OOPS, available from your usual friendly distributor of public domain
> software, e.g. uunet).  By using NIH classes, and deriving your own
> classes from the NIH hierarchy, you can get much of the behavior
> you're talking about.

Could someone please provide more detail on the NIH class library?  Does
it try to replicate the Smalltalk class hierarchies in C++?  Does NIH
have to with inventing or diseases?

Thanks for any elucidation.
-- 
Bob Weiner, Motorola, Inc.,   USENET:  ...!gatech!uflorida!novavax!weiner
(407) 364-2087

psrc@pegasus.ATT.COM (Paul S. R. Chisholm) (12/07/89)

In article <1656@novavax.UUCP>, weiner@novavax.UUCP (Bob Weiner) writes:
> Could someone please provide more detail on the NIH class library?  Does
> it try to replicate the Smalltalk class hierarchies in C++?  Does NIH
> have to with inventing or diseases?

The primary author of the NIH library just posted a good article on the
subject in comp.lang.c++.  Yes, the NIH classes look a lot like the
Smalltalk-80 classes.  He works for the National Institutes of Health,
but so far as I know, that's the only medical relation; and the pun on
"Not Invented Here" is unintentional, though appreciated.)

> Bob Weiner, Motorola, Inc.,   USENET:  ...!gatech!uflorida!novavax!weiner

Paul S. R. Chisholm, AT&T Bell Laboratories
att!pegasus!psrc, psrc@pegasus.att.com, AT&T Mail !psrchisholm
I'm not speaking for the company, I'm just speaking my mind.
Smalltalk-80 is a trademark of Xerox, or Parc Place, or somebody.

michael@xanadu.com (Michael McClary) (01/02/90)

In article <2122@tukki.jyu.fi> sakkinen@jytko.jyu.fi (Markku Sakkinen)
SAKKINEN@FINJYU.bitnet (alternative) writes:
>
>Note 2: An old favourite idea of mine is that classes should offer,
>as a primitive operation, rather one comparison operator (values:
>equal, less, greater, incomparable, equivalent; possibly others
>in special cases) than several relational operators. The latter
>can be derived from the former, and class interfaces would be simpler.

I recommend against this approach.  Such an operator must fully
characterize the relationship between two objects to answer any
question about that relationship.  Then the bulk of this work is
discarded.

This may seem a trivial efficiency quibble, but consider:  There
are data types where some relations can be determined in microseconds
from information in RAM, while others require hours of disk activity
(or a tape mount in your Sri Lanka branch office, which is closed due
to civil unrest...)

When you finally encounter such a data type, will you
 - accept the ten-orders-of-magnitude performance penalty,
 - redesign all your code, or
 - install a special-purpose kludge, thus dropping all your
   existing tools on the floor?

sakkinen@tukki.jyu.fi (Markku Sakkinen) (01/04/90)

In article <1990Jan2.112028.19312@xanadu.com> michael@xanadu.UUCP (Michael McClary) writes:
>In article <2122@tukki.jyu.fi> sakkinen@jytko.jyu.fi (Markku Sakkinen)
>SAKKINEN@FINJYU.bitnet (alternative) writes:
>>
>>Note 2: An old favourite idea of mine is that classes should offer,
>>as a primitive operation, rather one comparison operator (values:
>>equal, less, greater, incomparable, equivalent; possibly others
>>in special cases) than several relational operators. The latter
>>can be derived from the former, and class interfaces would be simpler.
>
>I recommend against this approach.  Such an operator must fully
>characterize the relationship between two objects to answer any
>question about that relationship.  Then the bulk of this work is
>discarded.
> ...

No, I did not mean that the one operator should be able to fully
characterise relatioships between objects in complicated cases.
Rather, it should express some "canonical" order (or quasi-order)
within the class. If there are several interesting order relations
in a class, each of them should be represented by its own
comparison-valued function.

Of course, there are cases in which it is easier e.g. to test whether
two objects are equal than to find out their exact relationship.
I did not want to _prohibit_ the definition of conventional relational
operators when deemed useful.

For a more thorough treatment, see my article in ACM SIGPLAN Notices
22:8 (August 1987). The term 'quasi-order' is, however, used there
incorrectly: I should have written 'weak order'; quasi-order is
more general.

Markku Sakkinen
Department of Computer Science
University of Jyvaskyla (a's with umlauts)
Seminaarinkatu 15
SF-40100 Jyvaskyla (umlauts again)
Finland