[comp.lang.c] Personal dialects and C++ overloading

Bob.Stout@p6.f506.n106.z1.fidonet.org (Bob Stout) (02/01/90)

In an article of <31 Jan 90 07:52:10 GMT>, (Steve Watt) writes:

 >  The exact potential problem with overloading is that each operator does
 >not NECESSARILY give some clue as to what it does.  For example:  I'd love
 >to do something cruel and inhuman to the person who came up with 
 >       "This is a test" >> cout
 >
 >  Because to me, >> means shift right!  Clearly, someone else (I think his
 >initials are actually B.S.*! :) thinks of >> as 'put something to there'.
 >  But why not use -> for output and <- for input?  a * b (where a and b are
 >some appropriate class) could mean add a and b, a repetitions of b, or some
 >other thoroughly bizarre operation (such as removing string b from string 
 >a).

  When this topic came up in the FidoNet C++ conference, *this* particular  
B.S. noted that it will take an extraordinary amount of discipline on the part  
of programmers using operator overloading to keep C++ from becoming "the Forth  
of the '90's" 

ruede@boulder.Colorado.EDU (Ulrich Ruede) (02/02/90)

In article <4156@helios.TAMU.EDU> john@stat.tamu.edu (John S. Price) writes:
>
>#define FORNEXT(x,y,x)  for (x=y;x<z;++x)
>
>...
>FORNEXT(x,0,10);
>
>That is not standard C, and I feel that using this type of macro is
>bad coding style.  
>

Well, knowing that the following program has to do with a graph (with
attributed nodes and edges) would you prefer to see an initialization
like

	FOR_ALL_NODES_OF(a_mesh, this_node)
		FOR_ALL_EDGES_OF(this_node, this_edge)
			this_edge->component= 0;
		END_ALL_EDGES
	END_ALL_NODES

or rather

	{	
		Node_Ptr this_node;
		for(this_node= (a_mesh)->grd; (this_node) != NULL; (this_node)= (this_node)->next){
			{ register Edge_Ptr	this_edge= (this_node)->c,
			_end= (this_edge)+((this_node)->num_edges);
			for(; (this_edge)<_end; (this_edge)++){
				this_edge->component= 0;

			}
		}
	}
	}

This has been produced by cb on the expanded result.

The macros enable me to define the data structures and the access to
these structures together, as one module. If I don't use macros, the
information how to access all nodes of the graph is dispersed throughout
the program. Changing the basic structure then means to rewrite
everything. If I have the macros, I need to change just the macro definition.

I am using m4, so everyone who prefers the expanded result, can easily
get it with all symbolic constants still in symbolic form.

I also think that changing the syntax of a language is not a bad thing by
itself. If you write a function, you have also changed the syntax
of the language by introducing a new terminal symbol at the expression
level. In some sense all programming consists in changing the syntax
of a given base language, until you have a *specialized language* in which
you solve your problem with a single statement like
	work();
This view of programming may be more obvious users of languages like
Prolog or Forth.

I agree, that only because you dislike C syntax you shouldn't
do things like
	#define BEGIN {
(if you can't see the braces, get a better terminal (:-) or
	#define OR ||
just as you wouldn't introduce new names for printf and other standard
library functions. Writing your own subroutines, however, makes perfect
sense, just as I believe it makes sense to use macros as mine above.
It would still be better if these capabiltities were built into some language
(without sacrificing performance).

Ulrich Ruede

ruede@boulder.colorado.edu (until Friday)
ruede@infovax.informatik.tu-muenchen.dbp.de (next week)

jwl@ernie.Berkeley.EDU (James Wilbur Lewis) (02/04/90)

Ulrich Ruede writes:

-Well, knowing that the following program has to do with a graph (with
-attributed nodes and edges) would you prefer to see an initialization
-like
-
-	FOR_ALL_NODES_OF(a_mesh, this_node)
-		FOR_ALL_EDGES_OF(this_node, this_edge)
-			this_edge->component= 0;
-		END_ALL_EDGES
-	END_ALL_NODES
-
-or rather
-
-	{	
-		Node_Ptr this_node;
-		for(this_node= (a_mesh)->grd; (this_node) != NULL; (this_node)= (this_node)->next){
-			{ register Edge_Ptr	this_edge= (this_node)->c,
-			_end= (this_edge)+((this_node)->num_edges);
-			for(; (this_edge)<_end; (this_edge)++){
-				this_edge->component= 0;
-
-			}
-		}
-	}
-	}
-
-This has been produced by cb on the expanded result.

You've stacked the deck in favor of your macros by showing a fragment
of gross, badly-formatted machine-generated code.  I'd much prefer to see
something like this:

zero_components(a_mesh)
A_MESH_TYPE a_mesh;
{
  Node_Ptr this_node;
  register Edge_Ptr this_edge, end;

  for( this_node = a_mesh->grd;  this_node != NULL; this_node = this_node->next)
  {
     end = this_edge + this_node->num_edges;
     for(this_edge = this_node->c; this_edge < end; this_edge++)
     {
         this_edge->component = 0;
     }
  }
}

I notice that to "macroize" your operation, you had to introduce an extra
parameter into each macro (this_node and this_edge, respectively) to get the 
temp variables defined correctly.  And you rely on using an underscore to hide 
another temp variable from the macro user.  There is also the problem of 
"this_node" being evaluated multiply within the inner macro -- I pity 
the fool who writes FOR_ALL_EDGES_OF(this_node++,this_edge) !

-The macros enable me to define the data structures and the access to
-these structures together, as one module. If I don't use macros, the
-information how to access all nodes of the graph is dispersed throughout
-the program. 

There is almost always a better way to do this without resorting to the
preprocessor.  The operation above, "do something" to each edge of a
graph, probably *is* a common operation for you.  It shouldn't be too hard
to write that as a function instead of a set of macros -- perhaps you could
pass a function pointer to your routine which would specify the operation
to perform on each edge or node.  Or if the function call overhead in the inner loop bothers you, you could pass some sort of function code to the
routine and have a switch statement inside the loop to select from a
(hopefully small) set of operations.  

Once you get the operation in the form of a function call,  feel
free to define any macros you like to invoke it.  I won't care because
syntactically, it still looks like a function call, and my tools that
know C's syntax won't get confused by it.  And the specialized control
structure is still localized in one place, so you can modify it easily
without rewriting the rest of your code.

-I am using m4, so everyone who prefers the expanded result, can easily
-get it with all symbolic constants still in symbolic form.

Everyone who has access to m4, that is....tough darts if you're porting
it to VMESS or Mess-DOS! :-)

There are two related attributes of "well-writtten" programs that I'm trying 
to get across in this thread.  Programs should be written in such a way that
future maintainers can easily use whatever tools they might have at their
disposal.  (Specifically, since many interesting tools need to parse
your code, you should avoid macros that would choke a C parser.)  Your
coding style shouldn't force future maintainers to use any particular tool to 
make sense of it.  Don't assume that every programmer who will encounter your 
code has access to m4, or even the preprocessor!  All the world isn't a UNIX
box! 

-I also think that changing the syntax of a language is not a bad thing by
-itself. If you write a function, you have also changed the syntax
-of the language by introducing a new terminal symbol at the expression
-level. 

Not in any C grammar I've ever seen. 

Now if you want to talk about typedefs, that's another story!  

-It would still be better if these capabiltities [abstracting control 
-structures, etc. -jwl] were built into some language
-(without sacrificing performance).

I think inline functions would be good for that.  Too bad they're not
standard C!

-- Jim Lewis
   U.C. Berkeley

anw@maths.nott.ac.uk (Dr A. N. Walker) (02/07/90)

In article <4156@helios.TAMU.EDU> john@stat.tamu.edu (John S. Price) writes:
> [...]		    It was pointed out to me the other day that
>Bourne used alot of macros to make C look like Algol (I can post
>some of the code if you like.)  I would HATE to have to
>maintain that code.

	[I don't want to pick on John, he's only one of several posters
who have commented adversely on Bournegol.]

	Steve Bourne can defend himself;  however, I think some of you
are missing the point.  In the late 70s, Algol (specifically, Algol 68)
was the language of choice for many of us old lags, certainly for me,
and I strongly suspect for SDB.  C was definitely second best.

	What to do, faced with a programming task?  Why, you write it
in Algol, of course.  Later you try to port that program to a computer
that has no [adequate] Algol compiler, but has C.  So, you copy the
source across, write a few macros, try to compile and lint the result,
and iterate into a working Bournegol program.  It's not perverted C,
it's perverted Algol.  I've done the same with a lot of "dusty decks".

	Those who are complaining loudest about Bournegol might like
to think about what they will do if they ever find themselves with lots
of C source on a computer with no C compiler, but with, say, Fortran
available.

	Once you are used to Bournegol, it becomes a perfectly viable
language in which it is possible to write useful programs, like "sh"
[:-)].

	Over a decade later, most of *my* programs are now written
in C from the start.  (Actually, that's a half truth:  most by line
count.  Most by number are in [Bourne] shell.)  Even so, when faced
with a tricky problem involving linked lists, or digraphs, or similar,
I still write the algorithm first in Algol, where it's *much* easier
to write, to prove, to maintain, to understand, and translate into C.
(At least Algol to C is fairly easy;  Algol to Pascal is an absolute
pig for these activities.)

>If someone says that they have a program in C that they want
>me to look at, I EXPECT it to be C, not some weird, perverted
>Algol-looking beast.

	But C *is* a weird, perverted Algol-like beast! [:-)]

-- 
Andy Walker, Maths Dept., Nott'm Univ., UK.
anw@maths.nott.ac.uk

peter@ficc.uu.net (peter da silva) (02/08/90)

In article <1990Feb6.174139.21140@maths.nott.ac.uk>, anw@maths.nott.ac.uk (Dr A. N. Walker) writes:
> 	Those who are complaining loudest about Bournegol might like
> to think about what they will do if they ever find themselves with lots
> of C source on a computer with no C compiler, but with, say, Fortran
> available.

Buy a copy of "Software Tools", and try to get a few programs running
under Ratfor. After a certain amount of blasphemy, settle down to
rewrite the whole kit and kaboodle from scratch. As you later point out,
C and Algool are pretty close. Certainly a lot closer than Fortran
and C:

> 	But C *is* a weird, perverted Algol-like beast! [:-)]
-- 
 _--_|\  Peter da Silva. +1 713 274 5180. <peter@ficc.uu.net>.
/      \
\_.--._/ Xenix Support -- it's not just a job, it's an adventure!
      v  "Have you hugged your wolf today?" `-_-'

brnstnd@stealth.acf.nyu.edu (02/08/90)

In article <4152@helios.TAMU.EDU> john@stat.tamu.edu (John S. Price) writes:
> No, no, no.  Operator overloading in C++ is used to abstract what
> the '+' operator does to a user-defined class.

This Ada (and, by imitation, Fortran 90 and C++) misfeature makes
program maintenance a nightmare. The problem isn't just that it's a pain
to locate the routine that adds a GLORP to a FOOBIEBLETCH. The problem
is that you won't even notice the nonstandard usage until it's too late.

Overloading saves typing time. It has no other advantages. (Is it an
advantage to be able to code without thinking and make undetectable
syntax errors?)

---Dan

henry@utzoo.uucp (Henry Spencer) (02/10/90)

In article <926.18:17:58@stealth.acf.nyu.edu> brnstnd@stealth.acf.nyu.edu (Dan Bernstein) writes:
>Overloading saves typing time. It has no other advantages...

Sorry, I can't go along with that.  Saving typing time by itself is not
of massive importance, but shorter programs are.  Other things being
equal, a shorter program is easier to understand, easier to work with,
easier to modify.  The gotcha is in that phrase "other things being equal".
In the case of operator overloading, other things are equal only if the
author has been careful to follow the Law of Least Astonishment when
writing the program.

Operator overloading is a power tool.  One does not give power tools to
children and expect safe use, but for professionals they make work
quicker and easier and thus make harder jobs manageable.  The problem
is that there are too many incompetent amateurs -- calling themselves
programmers -- who will slice off their own fingers, or their successors'
fingers, if given access to power tools.

Unfortunately, it's also true that some power tools are safer than others.
There is room for debate about whether C++'s sharp edges are dangerously
exposed.  Operator overloading is desirable and useful, but placing some
more constraints on it might be desirable.
-- 
SVR4:  every feature you ever |     Henry Spencer at U of Toronto Zoology
wanted, and plenty you didn't.| uunet!attcan!utzoo!henry henry@zoo.toronto.edu

brnstnd@stealth.acf.nyu.edu (02/14/90)

In article <1990Feb9.181111.24546@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:
> In article <926.18:17:58@stealth.acf.nyu.edu> brnstnd@stealth.acf.nyu.edu (Dan Bernstein) writes:
> >Overloading saves typing time. It has no other advantages...
> Sorry, I can't go along with that.  Saving typing time by itself is not
> of massive importance, but shorter programs are.

True. But do you really find that typing foo instead of library.foo (or
whatever your overloading language allows) makes your programs shorter?
Narrower, maybe, but that doesn't aid reading or comprehension.

> Operator overloading is desirable and useful, but placing some
> more constraints on it might be desirable.

Yeah. I probably wouldn't mind overloading if it had to have a special
syntax and if I could coax my editor into un-overloading (underloading?)
all overloaded operations. If Ada abbreviated lib1.lib2.foo as .foo or
lib2.foo rather than foo or lib2.foo, it might even be readable---the
initial period would serve as an ``overload warning'' and wouldn't be
too distracting. C++ doesn't have such simple syntax but the same idea
would work.

---Dan

schaut@cat9.cs.wisc.edu (Rick Schaut) (02/15/90)

Regarding overloading of operators:

In article <926.18:17:58@stealth.acf.nyu.edu> brnstnd@stealth.acf.nyu.edu (Dan Bernstein) writes:
| This Ada (and, by imitation, Fortran 90 and C++) misfeature makes
| program maintenance a nightmare. The problem isn't just that it's a pain
| to locate the routine that adds a GLORP to a FOOBIEBLETCH. The problem
| is that you won't even notice the nonstandard usage until it's too late.

Any compiler that would allow you to apply the "+" operator to a "GLORP"
and a "FOOBIEBLETCH" doesn't conform to the notion of overloading.
Overloading does _not_ come at the expense of strong type checking.  It
_does_, however, allow the programmer to apply the abstract notion of
"addition" to objects for which such an operation makes sense (e.g. sets).

Of course overloading the "+" operator to mean set difference when the two
operands are sets doesn't make much sense in the semantics of any language.
Does passing an array of characters in a parameter that is supposed to be
a pointer to a FILE object make much sense?

| Overloading saves typing time. It has no other advantages. (Is it an
| advantage to be able to code without thinking and make undetectable
| syntax errors?)

Overloading saves having to think about the mechanics of calling the
correct procedure whilst allowing the programmer to think more about the
semantics of the program.  It's not a case of being able to code without
thinking.  Rather, it's a case of being able to code while thinking about
that which is important.

--
Rick (schaut@garfield.cs.wisc.edu)

Peace and Prejudice Don't Mix! (unknown add copy)

brnstnd@stealth.acf.nyu.edu (02/16/90)

In article <4301@daffy.cs.wisc.edu> schaut@cat9.cs.wisc.edu (Rick Schaut) writes:
> In article <926.18:17:58@stealth.acf.nyu.edu> brnstnd@stealth.acf.nyu.edu (Dan Bernstein) writes:
> | This Ada (and, by imitation, Fortran 90 and C++) misfeature makes
> | program maintenance a nightmare. The problem isn't just that it's a pain
> | to locate the routine that adds a GLORP to a FOOBIEBLETCH. The problem
> | is that you won't even notice the nonstandard usage until it's too late.
> 
> Any compiler that would allow you to apply the "+" operator to a "GLORP"
> and a "FOOBIEBLETCH" doesn't conform to the notion of overloading.

Say what? Why not? What if GLORPs and FOOBIEBLETCHen are natural
candidates for addition? And since when does the compiler decide this
for you?

Read my comments in another article in this thread about how to make
overloading readable.

> | Overloading saves typing time. It has no other advantages. (Is it an
> | advantage to be able to code without thinking and make undetectable
> | syntax errors?)
> Overloading saves having to think about the mechanics of calling the
> correct procedure whilst allowing the programmer to think more about the
> semantics of the program.

The same would be true if overloading were distinctively marked, by an
initial character, for example. This would save gratuitous syntax errors
without being too distracting.

Even so, are you sure that vector + vector means addition and not
concatenation? Or are they character vectors, being used as strings?
As many other programmers have pointed out, you should never force
someone to learn a new language just to read a program.

---Dan

gsarff@meph.UUCP (Gary Sarff) (02/17/90)

In article <4301@daffy.cs.wisc.edu>, schaut@cat9.cs.wisc.edu (Rick Schaut) writes:
>Regarding overloading of operators:
>
>In article <926.18:17:58@stealth.acf.nyu.edu> brnstnd@stealth.acf.nyu.edu (Dan Bernstein) writes:
>| This Ada (and, by imitation, Fortran 90 and C++) misfeature makes
>| program maintenance a nightmare. The problem isn't just that it's a pain
>| to locate the routine that adds a GLORP to a FOOBIEBLETCH. The problem
>| is that you won't even notice the nonstandard usage until it's too late.
>
>Any compiler that would allow you to apply the "+" operator to a "GLORP"
>and a "FOOBIEBLETCH" doesn't conform to the notion of overloading.

Why not, if that is what I want, and that is the way I declared the operator
to work?  I want + to add 1 operand of type GLORP to one of type
FOOBIEBLETCH.  They don't necessarily have to be the same type of objects to
overload.

>Overloading does _not_ come at the expense of strong type checking.  It
>_does_, however, allow the programmer to apply the abstract notion of
>"addition" to objects for which such an operation makes sense (e.g. sets).

Makes sense to whom?  You?  Me?  A foreign programmer who doesn't speak your
native english?

>
>Of course overloading the "+" operator to mean set difference when the two
>operands are sets doesn't make much sense in the semantics of any language.

Again, make sense to whom?

>Does passing an array of characters in a parameter that is supposed to be
>a pointer to a FILE object make much sense?
>
>| Overloading saves typing time. It has no other advantages. (Is it an
>| advantage to be able to code without thinking and make undetectable
>| syntax errors?)
>
>Overloading saves having to think about the mechanics of calling the
>correct procedure whilst allowing the programmer to think more about the
>semantics of the program.  It's not a case of being able to code without
>thinking.  Rather, it's a case of being able to code while thinking about
>that which is important.
>

I think the original poster's point, and the reason I have always distrusted
overloading, (as in Ada when the whole point is supposed to be reliability
and ease of maintainability) is that when you have overloading, you must have
all source code, headers, module def's, whatever available to you (the way
the compiler does) or the meaning of any statement you see that has operators
in it cannot be deduced by you.  This is ease of maintainability??
Even if you can determine the types of a,b, and c, and you see

  a = b + c;

do you _REALLY_ know the semantics of this, what if I overloaded + to do
something really strange, like b + c = 1 if b and c are mutually prime?
Doesn't make sense to you, (or me either), but that isn't the point.  The
compiler won't stop me from doing this, the language definition allows it.
The only restraint we have that something like this won't happen is the
restraint of the originating programmer.  Features like that in any language
are at least a bit scary to contemplate.

schaut@cat19.cs.wisc.edu (Rick Schaut) (02/19/90)

There is a bit of a misunderatanding here which is probably due to my having
jumped in on the middle of a discussion.  If so, I apologize and will attempt
to be a bit more lucid.

In article <16334:04:32:49@stealth.acf.nyu.edu> brnstnd@stealth.acf.nyu.edu (Dan Bernstein) writes:
| In article <4301@daffy.cs.wisc.edu> schaut@cat9.cs.wisc.edu (Rick Schaut) writes:
| > In article <926.18:17:58@stealth.acf.nyu.edu> brnstnd@stealth.acf.nyu.edu (Dan Bernstein) writes:
| > Any compiler that would allow you to apply the "+" operator to a "GLORP"
| > and a "FOOBIEBLETCH" doesn't conform to the notion of overloading.
| 
| Say what? Why not? What if GLORPs and FOOBIEBLETCHen are natural
| candidates for addition? And since when does the compiler decide this
| for you?

If GLORPS and FOOBIEBLETCHen are type compatible (as the programmer has
defined them), then addition should be fine.  You seemed to be complaining
that overloading allows you to apply some operator to objects having
incompatible types which, frankly, isn't true.

| Read my comments in another article in this thread about how to make
| overloading readable.

If that's all you were saying I wouldn't have posted a follow-up even though
I don't necessarily agree.  Why should we take extra steps to make overloading
more readable (whatever 'readable' means) when we don't for other aspects
of a language?  Doing anything to notify the reader that overloading is
being applied in a particular expression seems to defeat the whole purpose
of overloading.  If you know the objects to which an overloaded operator is
being applied, why do you need some special flag to tell you there is
overloading?  If a compiler can understand overloading, a human ought to
be able to understand it as well.

In any event, you _did_ make the following statement:

| > | Overloading saves typing time. It has no other advantages. (Is it an
				     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
| > | advantage to be able to code without thinking and make undetectable
| > | syntax errors?)
| > Overloading saves having to think about the mechanics of calling the
| > correct procedure whilst allowing the programmer to think more about the
| > semantics of the program.
| 
| The same would be true if overloading were distinctively marked, by an
| initial character, for example. This would save gratuitous syntax errors
| without being too distracting.

Which you now seem to concede isn't correct.  Overloading allows at least
enough advantage that you want to allow it yet flag it in some manner.  I
was, primarily, taking issue with the sentence I underlined above.

| Even so, are you sure that vector + vector means addition and not
| concatenation? Or are they character vectors, being used as strings?

I'll grant that ambiguities are a problem, and ambiguities are _usually_
solved through context.  However, no language that is in prevalent use
can't be described using a context free grammar, so if the compiler is
smart enough to resolve those ambiguites without a context, a human shouldn't
have any problem whatsoever.

| As many other programmers have pointed out, you should never force
| someone to learn a new language just to read a program.

That's funny.  We seem to have very few qualms about the fact that one can't
read a C program without understanding C.  Why should new languages be any
different?  C++ is _not_ C and isn't intended to be C.

--
Rick (schaut@garfield.cs.wisc.edu)

Peace and Prejudice Don't Mix! (unknown add copy)