[comp.std.c] struct comparison

maart@cs.vu.nl (Maarten Litmaath) (07/14/89)

According to the pANS structs can be

	- assigned
	- returned
	- used as function arguments

but they cannot be COMPARED, right? Very annoying.
The reason appears to be internal padding, whose contents aren't specified.
Thus the following won't work:

	struct	foo {
			char	bar;
			int	baz;
			double	zork;
		} mork, mindy;

	...
	puts(bcmp((char *) &mork, (char *) &mindy, sizeof mork) == 0 ?
		"equal" : "unequal");

To compare the 2 structs, one now has to check each field oneself:

	puts(mork.bar == mindy.bar && mork.baz == mindy.baz
		&& mork.zork == mindy.zork ? "equal" : "unequal");

Why does the PROGRAMMER have to go through all that trouble?
I just want to say:

	puts(mork == mindy ? "equal" : "unequal");

Is the ANSI committee trying to tell us the compiler cannot transform the
equality test into the correct member-by-member comparison code?
-- 
   "... a lap-top Cray-2 with builtin    |Maarten Litmaath @ VU Amsterdam:
cold fusion power supply"  (Colin Dente) |maart@cs.vu.nl, mcvax!botter!maart

henry@utzoo.uucp (Henry Spencer) (07/14/89)

In article <2874@solo3.cs.vu.nl> maart@cs.vu.nl (Maarten Litmaath) writes:
>Why does the PROGRAMMER have to go through all that trouble?
>I just want to say:
>
>	puts(mork == mindy ? "equal" : "unequal");
>
>Is the ANSI committee trying to tell us the compiler cannot transform the
>equality test into the correct member-by-member comparison code?

Yes.  Think about unions.  Or pointers (do you compare the pointers or
what they point at?).  The compiler just doesn't have enough information.
-- 
$10 million equals 18 PM       |     Henry Spencer at U of Toronto Zoology
(Pentagon-Minutes). -Tom Neff  | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

gwyn@smoke.BRL.MIL (Doug Gwyn) (07/15/89)

In article <2874@solo3.cs.vu.nl> maart@cs.vu.nl (Maarten Litmaath) writes:
>Is the ANSI committee trying to tell us the compiler cannot transform the
>equality test into the correct member-by-member comparison code?

No, but apart from the padding issue it is not clear what would
constitute "equality" in practice; memberwise comparison would
seldom be what one really wanted.  Consider char* members, for
example; probably struct equality in such a case should use the
equivalent of strcmp() on those members.  Floating-point equality
tests are usually meaningless (except sometimes when one of the
operands is precisely zero).  Union members pose yet another
problem.  And so on.  Given such pratical problems, and that a
programmer can perform this test another way, and that it would
be an "invention", X3J11 chose not to require support for
aggregate equality testing.

There were an immense number of suggestions for such linguistic
enhancements received by X3J11, as listed in the introduction to
each official public review response document.  If there is much
interest, I could post the list in order to give you a better
appreciation of why such suggestions were routinely rejected.

njk@freja.diku.dk (Niels J|rgen Kruse) (07/15/89)

henry@utzoo.uucp (Henry Spencer) writes:

>In article <2874@solo3.cs.vu.nl> maart@cs.vu.nl (Maarten Litmaath) writes:
>>Why does the PROGRAMMER have to go through all that trouble?
>>I just want to say:
>>      puts(mork == mindy ? "equal" : "unequal");
>>Is the ANSI committee trying to tell us the compiler cannot transform the
>>equality test into the correct member-by-member comparison code?

>Yes.  Think about unions.  Or pointers (do you compare the pointers or
>what they point at?).  The compiler just doesn't have enough information.

Just like the compiler doesn't have enough information to
initialize a union, right? !

A simple rule like comparing first members of unions and the
pointers themselves would work nicely in a lot of cases and
match the way initialization works.

Or comparison of structs with inconvenient members could just
be outlawed, which wouldn't reduce the usefullness much.

Or perhaps better, inconvenient members could just be ignored,
leaving them for the programmer to handle separately.
-- 
         Niels J|rgen Kruse
Email    njk@diku.dk
Mail     Tustrupvej 7, 2 tv, 2720 Vanlose, Denmark

henry@utzoo.uucp (Henry Spencer) (07/18/89)

In article <167@ssp1.idca.tds.philips.nl> roelof@idca.tds.PHILIPS.nl (R. Vuurboom) writes:
>>It's also not a particularly good idea:  suppose I change to a polar 
>>form, where the representation of a given
>>complex number is not unique?  
>
>Henry, this is cheating :-). If somebody comes along and changes the 
>representation of an int while I'm not looking nobody expects the
>two values (old representation vs new representation) to compare equal.

Not quite what I was getting at.  The point of polar representation is
that member-by-member comparison does not dependably get the right answer!
Equality comparison on polar representation requires range reduction on
the angle first.  This leads again to the need for C++, where you can
define the comparison operation to be arbitrarily complex.
-- 
$10 million equals 18 PM       |     Henry Spencer at U of Toronto Zoology
(Pentagon-Minutes). -Tom Neff  | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

bbadger@x102c.harris-atd.com (Badger BA 64810) (07/18/89)

In article <1989Jul18.020424.2392@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:
>In article <167@ssp1.idca.tds.philips.nl> roelof@idca.tds.PHILIPS.nl (R. Vuurboom) writes:
>>>It's also not a particularly good idea:  suppose I change to a polar 
>>>form, where the representation of a given
>>>complex number is not unique?  
>>
>>Henry, this is cheating :-). If somebody comes along and changes the 
>>representation of an int while I'm not looking nobody expects the
>>two values (old representation vs new representation) to compare equal.
>
>Not quite what I was getting at.  The point of polar representation is
>that member-by-member comparison does not dependably get the right answer!
>Equality comparison on polar representation requires range reduction on
>the angle first.  This leads again to the need for C++, where you can
>define the comparison operation to be arbitrarily complex.
>-- 
>$10 million equals 18 PM       |     Henry Spencer at U of Toronto Zoology
>(Pentagon-Minutes). -Tom Neff  | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

Sure, you _may_ run into special cases, but is this any reason to leave out 
a useful operation?  Other languages, such as Ada (Oh no! The A-word!), 
provide for comparison of composite objects (records in Ada correspond to 
structs in C).  Of course there should be caveats about padding data being 
compared.  Also unions should only be compared to another union or object
which is *actually using* the same representation.  (That is, given:
	union lf_t {long l; float f} lf_a, lf_b;
	lf_a.l = 10; 
	lf_b.f = 10.0;
we should probably not be testing (lf_a == lf_b), but rather 
(lf_a.l == (long) lf_b.f).)

However, the fact remains that either simple ``compare all bytes'' or 
an expansion to compare all struct members recursively, would be a simple
extension which could be quite useful.

Bernard A. Badger Jr.	407/984-6385          |``Use the Source, Luke!''
Secure Computer Products                      |``Get a LIFE!''  -- J.H. Conway
Harris GISD, Melbourne, FL  32902             |Buddy, can you paradigm?
Internet: bbadger%x102c@trantor.harris-atd.com|'s/./&&/g' Tom sed expansively.

roelof@idca.tds.PHILIPS.nl (R. Vuurboom) (07/18/89)

In article <1989Jul18.020424.2392@utzoo.uucp| henry@utzoo.uucp (Henry Spencer) writes:
|
|Not quite what I was getting at.  The point of polar representation is
|that member-by-member comparison does not dependably get the right answer!
|Equality comparison on polar representation requires range reduction on
|the angle first.  This leads again to the need for C++, where you can
|define the comparison operation to be arbitrarily complex.

No need to be arbitrary...polar or rectangular will do nicely :-)
-- 
Its a thin red line (and spelling error) between boring and borish.

Roelof Vuurboom  SSP/V3   Philips TDS Apeldoorn, The Netherlands   +31 55 432226
domain: roelof@idca.tds.philips.nl             uucp:  ...!mcvax!philapd!roelof

guy@auspex.auspex.com (Guy Harris) (07/20/89)

>Sure, you _may_ run into special cases, but is this any reason to leave out 
>a useful operation?

Well, it depends on how useful it *really* is; given enough "special
cases", it may be that the only cases where it *is* useful are special
cases, in which case you can whip up a macro to do the comparison.

>Other languages, such as Ada (Oh no! The A-word!), provide for comparison
>of composite objects (records in Ada correspond to structs in C).

So how does Ada define comparison of records?

>Of course there should be caveats about padding data being compared.

I.e., "padding data gets compared"?  That doesn't sound like all that
useful an operation to me; in order to be useful, I'd have to zero out
structures on e.g. the stack.

>Also unions should only be compared to another union or object
>which is *actually using* the same representation.  (That is, given:
>	union lf_t {long l; float f} lf_a, lf_b;
>	lf_a.l = 10; 
>	lf_b.f = 10.0;
>we should probably not be testing (lf_a == lf_b), but rather 
>(lf_a.l == (long) lf_b.f).)

Fine, so how is the compiler to know to do that?  C unions are not
discriminated unions; there's nothing in the union itself to indicate
which member is being used.

>However, the fact remains that either simple ``compare all bytes'' or 
>an expansion to compare all struct members recursively, would be a simple
>extension which could be quite useful.

Simple "compare all bytes" is certainly simple; how useful it is is
another question, given that comparing padding bytes is simply wrong,
unless you *guarantee* that they're always going to have some "standard"
value that gets in the way.  You still haven't indicated how unions are
to be compared, so an expansion to compare all struct members
recursively would fail if any such members were unions.  And, once
again, I'll point out that pointer comparison may or may not be what you
want, either.

In other words, comparison might be "simple" in a restricted sense, but
nobody's proven that in that "simple" sense (i.e., bitwise comparison
either of all the bits in the structure, or only the "meaningful" bits)
this would be anywhere near "quite useful".

If you want abstract data types, you know where to find them....

diamond@csl.sony.JUNET (Norman Diamond) (07/20/89)

In article <1989Jul18.020424.2392@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:

>Equality comparison on polar representation requires range reduction on
>the angle first.  This leads again to the need for C++, where you can
>define the comparison operation to be arbitrarily complex.

Well, with ordinary integers, overflow might occur.  Does this mean that
"+" should not be provided for integers?

--
-- 
Norman Diamond, Sony Computer Science Lab (diamond%csl.sony.jp@relay.cs.net)
  The above opinions are inherited by your machine's init process (pid 1),
  after being disowned and orphaned.  However, if you see this at Waterloo or
  Anterior, then their administrators must have approved of these opinions.

bbadger@x102c.harris-atd.com (Badger BA 64810) (07/20/89)

In article <2261@auspex.auspex.com> you write:
>>Sure, you _may_ run into special cases, but is this any reason to leave out 
>>a useful operation?
>
>Well, it depends on how useful it *really* is; given enough "special
>cases", it may be that the only cases where it *is* useful are special
>cases, in which case you can whip up a macro to do the comparison.
>
>>Other languages, such as Ada (Oh no! The A-word!), provide for comparison
>>of composite objects (records in Ada correspond to structs in C).
>
>So how does Ada define comparison of records?
From ANSI/MIL-STD-1815A 22 JAN 1983, which is sometimes called the Ada 
Language Reference Manual (ALRM), section 4.5.2:
\begin{quote}
[par 1] The equality and inequality operators are predefined for any type 
that is not limited. 
...
[par 5] For two array values or two record values of the same type,
the left operand is equal to the right operand if and only if 
for each component of the left operand there is a {\em matching component}
of the right operand, and vice versa; and the values of the matching 
components are equal, as given in the predefined equality operator for 
the component type. In particular, two null arrays of the same type are 
always equal; two null records of the same type are always equal.
[par 6] For comparing two records of the same type, {\em matching components}
are those which have the same component identifier.
\end{quote}

>
>>Of course there should be caveats about padding data being compared.
>
>I.e., "padding data gets compared"?  That doesn't sound like all that
>useful an operation to me; in order to be useful, I'd have to zero out
>structures on e.g. the stack.
>
Right, the component-by-component comparison is much safer, and is the 
natural approach for Ada.  Given C's low-level approach, though, I did 
not want to rule out from discussion a simplistic compare-all-bytes 
implementation.  That's also why I pointed out some gotcha's.

>>Also unions should only be compared to another union or object
>>which is *actually using* the same representation.  (That is, given:
>>	union lf_t {long l; float f} lf_a, lf_b;
>>	lf_a.l = 10; 
>>	lf_b.f = 10.0;
>>we should probably not be testing (lf_a == lf_b), but rather 
>>(lf_a.l == (long) lf_b.f).)
>
>Fine, so how is the compiler to know to do that?  C unions are not
>discriminated unions; there's nothing in the union itself to indicate
>which member is being used.
>
>>However, the fact remains that either simple ``compare all bytes'' or 
>>an expansion to compare all struct members recursively, would be a simple
>>extension which could be quite useful.
>
>Simple "compare all bytes" is certainly simple; how useful it is is
>another question, given that comparing padding bytes is simply wrong,
>unless you *guarantee* that they're always going to have some "standard"
>value that gets in the way.  You still haven't indicated how unions are
>to be compared, so an expansion to compare all struct members
>recursively would fail if any such members were unions.  And, once
>again, I'll point out that pointer comparison may or may not be what you
>want, either.
>
Yes, comparison of unions is not well-defined, so comparison of 
structs containing union members would also be not well-defined.

>In other words, comparison might be "simple" in a restricted sense, but
>nobody's proven that in that "simple" sense (i.e., bitwise comparison
>either of all the bits in the structure, or only the "meaningful" bits)
>this would be anywhere near "quite useful".
>
This goes a bit too far, because you throw out the useful comparisons with 
the ill-defined ones.  It is perfectly well-defined and quite handy 
to define a simple member-by-member comparison on structs and arrays.
The only thing you give up are the unions.  Comparison of pointers is 
defined to be comparison of the pointers themselves, just as always.
char *p, *q;
char a[] = "aa"; char b[] = "bb";
main(){
p = &a[0];q = &b[0];
strcpy(p,"hi"); strcpy(q,"hi"); 
if (p == p) {printf("Not in C you don't!\n");}
else printf("I thought that's what you meant!\n");
}

Now, I think that given:
	typedef struct _pt_t { int x,y,x; } pt_t, * pt_pt;
	typedef struct _pt_t polyline_t[3];
	pt_t p = { 11, 12, 13};
	pt_t q = {21, 22, 23};
	polyline_t pl = {{ 11, 12, 13}, { 21, 22, 23}, { 31, 32, 33}};
It would be nice to check things like:
	if ( pl[0] == p ) {}
rather than 
	if ( pl[0].x == p. &&  pl[0].y == p.y &&  pl[0].z == p.z ) {}


>If you want abstract data types, you know where to find them....
Well, this doesn't really have anything to do with abstraction, we're not 
hiding any implementation.  This is just structured data.  

I wouldn't cry either if a tagged-union record were added to C.  That would 
give the compiler a chance to do the union comparisons too, based on the 
tags.  

Bernard A. Badger Jr.	407/984-6385          |``Use the Source, Luke!''
Secure Computer Products                      |``Get a LIFE!''  -- J.H. Conway
Harris GISD, Melbourne, FL  32902             |Buddy, can you paradigm?
Internet: bbadger%x102c@trantor.harris-atd.com|'s/./&&/g' Tom sed expansively.

roelof@idca.tds.PHILIPS.nl (R. Vuurboom) (07/21/89)

In article <2261@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes:
>
>Well, it depends on how useful it *really* is; given enough "special
>cases", it may be that the only cases where it *is* useful are special
>cases, in which case you can whip up a macro to do the comparison.

Whipping, as is well known, is unpleasant at best (but may depend on 
which side of the whip you're on :-)


>>However, the fact remains that either simple ``compare all bytes'' or 
>>an expansion to compare all struct members recursively, would be a simple
			      ^^^^^^
>>extension which could be quite useful.
>
>Simple "compare all bytes" is certainly simple; how useful it is is
>another question, given that comparing padding bytes is simply wrong,
>unless you *guarantee* that they're always going to have some "standard"
>value that gets in the way.  

Agree.

>You still haven't indicated how unions are
				 ^^^^^^
>to be compared, so an expansion to compare all struct members
>recursively would fail if any such members were unions.  

Don't we all love to point out the obvious: a struct is not a union and
a union is not a struct and never the twain shall meet.

To repeat a struct comparison definition I posted a few days back:

Now _if_ I were to define struct comparison it would _only_ be for
structs for which each member can be directly compared or
is a struct to which this requirement is recursively applied.

This (and the above) definition is strictly Thatcherite: _no_ unions.

With this definition we've got the corollary that two structs compare
equal if and only if their members compare equal. 

So I disallow struct comparisons with structs which have unions, 
arrays and bitfields as members and possibly some other cases which
I can't think of but others undoubtedly can. But my point is that 
I've still got an awful lot of structs *which occur in the real world* 
for which I can (and several times wished I could) do a sensible comparison. 

Looking at the arguments up to now that have been advanced against
struct comparison:

1. unions, bit fields (and the related case of data padding)

Agreed. But my argument above is that if I exclude these cases I've
still got a very relevant operation.


2. member arrays, strings.

Why demand direct language support for comparison of arrays/strings in
a struct if you don't do it directly? Again exclude this case: I've
still got a useful, relevant comparison operator.

3. polar complex example

It took a while before I began to get the sneaky feeling that maybe 
Henry Spencer had put one over me here.

His argument was that because a certain _mathematical_ concept (complex
comparison) couldn't be supported this was (one of the) grounds for 
omitting a _language_ concept (struct comparison). 

This argument holds in my opinion as much water as defining the 
concept angle as:

typedef int angle; 

Stating that angles modulo 360 are really equal and then stating that
since angle1 and angle2 don't compare equal (even when separated by 
360 degrees) somethings wrong with the (int) comparison operation 
(and perhaps should be omitted :-).

4. Language Extension

(I've changed my mind on this one). It extends the language but not (much)
more than say returning a struct does and less in my opinion than function
prototyping for example. 

5. The comparison is not simple.

Agreed if you want to be able to compare all possible structs. Disagree
if you restrict to the above definition. In that case, struct comparison
is relatively easily to implement, it certainly is easy to 
comprehend and it most certainly is a relevant, useful operation even
with the stated restrictions.

>If you want abstract data types, you know where to find them....

Sure but all I want is a simple struct comparison :-)


-- 
Its a thin red line (and spelling error) between boring and borish.

Roelof Vuurboom  SSP/V3   Philips TDS Apeldoorn, The Netherlands   +31 55 432226
domain: roelof@idca.tds.philips.nl             uucp:  ...!mcvax!philapd!roelof

gwyn@smoke.BRL.MIL (Doug Gwyn) (07/21/89)

In article <2261@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes:
>Simple "compare all bytes" is certainly simple; how useful it is is
>another question, given that comparing padding bytes is simply wrong,
>unless you *guarantee* that they're always going to have some "standard"
>value that gets in the way.

Comparing padding is wrong anyway.  Even if you could arrange for it to
"contain" some standard value (and I'm not sure how you could do that in
standards language), there is no constraint so far that says that value
must still be there later.  

The obvious definition of struct comparison would be:
	structs compare equal if and only if all their corresponding
	members compare equal.
I assure you that it was considered as one of the zillion proposed C
language extensions received by X3, but its benefits were not shown to
outweigh its drawbacks.

guy@auspex.auspex.com (Guy Harris) (07/22/89)

>Right, the component-by-component comparison is much safer, and is the 
>natural approach for Ada.  Given C's low-level approach, though, I did 
>not want to rule out from discussion a simplistic compare-all-bytes 
>implementation.

*I'd* rule it out, because it can give the wrong answer, but if you
don't care about getting the right answer, or know that in some
particular case it will never give the wrong answer, and you *really*
want a simplistic compare-all-bytes implementation, you can use "memcpy"
- which may be inlined in some implementations. 

If you want a component-by-component comparison, you can write it
yourself, obviously, or wrap it in a macro if you use it a lot.  Perhaps
not as convenient as having the compiler do it for you, but I'd find it
more convenient than, for every structure, deciding whether a compiler
notion of structure comparison would really do what I wanted (e.g., the
question of "do you compare pointer values or do you compare the values
to which they point), or would even work at all (e.g., a structure with
unions).

guy@auspex.auspex.com (Guy Harris) (07/22/89)

>1. unions, bit fields (and the related case of data padding)
>
>Agreed. But my argument above is that if I exclude these cases I've
>still got a very relevant operation.

I've seen no indication that it's sufficiently relevant *that it should
be added to the language*; any definition of structure comparison would
be peppered with caveats like "no unions" and "no arrays" and "it
compares pointer values, not what they point to" and the like.

Yes, there have been cases I've seen where it might have been
convenient, but I really *can* live with doing the member-by-member
comparison myself.  And, given that there's no prior art, I agree with
X3J11's decision to leave it out.  If somebody sticks it into their
compiler as an extension and we can really find out from experience
whether enough people actually *would* use it enough to justify its
existence, then it can be considered as an extension if it actually is
used enough and doesn't confuse people.

>3. polar complex example
>
>It took a while before I began to get the sneaky feeling that maybe 
>Henry Spencer had put one over me here.
>
>His argument was that because a certain _mathematical_ concept (complex
>comparison) couldn't be supported this was (one of the) grounds for 
>omitting a _language_ concept (struct comparison). 

I think Henry's point, like mine, is that the total number of cases
where somebody, in effect, defines an equality operator over some data
type seems likely to be much larger than the number of cases where this
operator is just a member-by-member comparison; complex numbers
represented in polar form are one example.  Given that, I'd like to see
some real-world experience with such a feature before seeing it blessed
in a standard.... 

The original poster *did*, in fact, effectively ask "why isn't structure
comparison in ANSI C"?  The question was answered, by Henry and others,
and I have yet to see a *single* response to that answer that, to me,
would justify the inclusion of such a feature *in the ANSI C standard*,
given the lack of prior art (and yes, there *was* prior art for function
prototypes, namely in C++; it may not have been in an otherwise "pure" C
implementation, but at least it existed).  I suspect *I* wouldn't use
structure comparison if it were available to me in C; I'm almost certain
I wouldn't use it if it were available to me in C++ - I'd define an "="
operator for the data type, instead, and not be forced to rely on
member-by-member comparison being the right answer.

mwm@eris.berkeley.edu (Mike (I'll think of something yet) Meyer) (07/22/89)

Can I point out something that I haven't seen mentioned here yet?

It's called "maintenance."

Consider the current situation. If you've got a structure that you
need to do comparison ons, and are thinking, you'll have an include
file with the structure & a comparison macro for that structure.

When you change the structure - in any way - you have the macro in the
same place, and can change it right there as apropos for the addition.

Now, assume you're using the hypothetical "struct comparison", you
don't have a macro - just the structure definition. If you change the
structure, you may not think about changing the equality test (no
macro nearby to remind you). When you do change it, you have to verify
that the default comparison is still legal and correct. If you need to
write a macro, you'll have to find every place that comparison is
used, and fix it. If the comparison is illegal, this isn't hard, just
tedious. If it's legal, it could be a pain.

While not something that would make you want to toss the facility
completely, it's yet another argument against it. The feature seems to
die the death of a thousand cuts.

BTW, my standard answer to people who _insist_ that C ought to have a
feature is:

	Get a copy of GNU C, and add that feature. Then, when
	they do the next revision of the standard, you'll have
	"prior practice" for the committe.  This is a lot more
	likely to get it added than arguing about it on USEnet.
	Besides which, you'll be able to write non-conforming
	code that uses that feature in the meantime.


	<mike

--
I went down to the hiring fair,				Mike Meyer
For to sell my labor.					mwm@berkeley.edu
I noticed a maid in the very next row,			ucbvax!mwm
I hoped she'd be my neighbor.				mwm@ucbjade.BITNET

peter@ficc.uu.net (Peter da Silva) (07/24/89)

In article <2266@auspex.auspex.com>, guy@auspex.auspex.com (Guy Harris) writes:
> notion of structure comparison would really do what I wanted (e.g., the
> question of "do you compare pointer values or do you compare the values
> to which they point),

You compare pointer values.

> or would even work at all (e.g., a structure with
> unions).

This should be illegal (as should initialising a union).


-- 
Peter da Silva, Xenix Support, Ferranti International Controls Corporation.
Business: peter@ficc.uu.net, +1 713 274 5180. | "...helping make the world
Personal: peter@sugar.hackercorp.com.   `-_-' |  a quote-free zone..."
Quote: Have you hugged your wolf today?  'U`  |    -- hjm@cernvax.cern.ch

roelof@idca.tds.PHILIPS.nl (R. Vuurboom) (07/25/89)

In article <2267@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes:
|
|I've seen no indication that it's sufficiently relevant *that it should
|be added to the language*; any definition of structure comparison would
|be peppered with caveats like "no unions" and "no arrays" and "it
|compares pointer values, not what they point to" and the like.
|

Ok, let me try and put my money where my mouth is:

Structure comparison is defined for structures which are recursively
derived from basic, enumerated, pointer or structure types. 

Two structs of the same derived type for which structure comparison
is defined compare equal if and only if each member (recursively) 
compares equal.


Which points out that in order to define something you don't
necessarily need to state what it isn't but just what it is.


|Yes, there have been cases I've seen where it might have been
|convenient, but I really *can* live with doing the member-by-member
|comparison myself.  

Agreed, so can I. I can live without struct assignment too but I kind of
like having it. 

I don't think the "living without" argument is really fair. We can all
live without a lot of things (probably even C :-). 

|And, given that there's no prior art, I agree with
|X3J11's decision to leave it out.  


Which makes me start wondering about volatile or - to go to another extreme -
const.

 

|If somebody sticks it into their
|compiler as an extension and we can really find out from experience
|whether enough people actually *would* use it enough to justify its
|existence, then it can be considered as an extension if it actually is
|used enough and doesn't confuse people.
|

The above I consider valid arguments, translating:

Is it relevant? viz. can you expect it to be used in "mainstream" programming?
Is it straightforward? Perhaps to put it another way: is it "natural".


|The original poster *did*, in fact, effectively ask "why isn't structure
|comparison in ANSI C"?  The question was answered, by Henry and others,
|and I have yet to see a *single* response to that answer that, to me,
|would justify the inclusion of such a feature *in the ANSI C standard*,
|given the lack of prior art (and yes, there *was* prior art for function
|prototypes, namely in C++; it may not have been in an otherwise "pure" C
|implementation, but at least it existed).  

Obviously I can't be sure that any particular argument will convince you
but this one might convince me:

enum make_t {BMW,PEUGEOUT,RENAULT,MERCEDES,...
enum colour_t {CHROMIUM_RED,CANARY_YELLOW,

typedef car
{
         make_t make;
	 colour_t colour;
}

...
	for( i=0; i < nr_cars_in_stock; i++ )
	{
		..
		if ( car_wanted == cars_in_stock[i] )

...

	
|I suspect *I* wouldn't use
|structure comparison if it were available to me in C; I'm almost certain
|I wouldn't use it if it were available to me in C++ - I'd define an "="
|operator for the data type, instead, and not be forced to rely on
|member-by-member comparison being the right answer.

The choice of whether you would want (need) to use abstract data typing
or data structure comparison is really quite clear cut. It is the cut
between whether the data structure represents an abstraction (complex
numbers case in point) or represents a combinatoric. In the first
case the object is _more_ than the sum of its parts. In the second case
the object _is_ the sum of its parts. A complex number has "more" meaning
than a pair of reals hence the extra operations needed to bring out this
meaning. A red car does not represent a higher abstraction level than the
concepts red and car considered separately. To put it simply, all applications
that use combinatoric objects (in the sense I've defined) could (and I
think would) use struct comparison.

In this particular application area struct comparison is both relevant and
straightforward.

Attempting to quantify how often this comes up and how often it needs to
come up to make into X3J11 is something I find impossible to do. All I
can say is that I have come across the above application area a number of
times.

-- 
I don't know what the question means, but the answer is yes...
(overheard on comp.lang.misc)
Roelof Vuurboom  SSP/V3   Philips TDS Apeldoorn, The Netherlands   +31 55 432226
domain: roelof@idca.tds.philips.nl             uucp:  ...!mcvax!philapd!roelof

guy@auspex.auspex.com (Guy Harris) (07/25/89)

>> notion of structure comparison would really do what I wanted (e.g., the
>> question of "do you compare pointer values or do you compare the values
>> to which they point),
>
>You compare pointer values.

Which may not always correspond to what you want; i.e., "these two
structure values have the same values in all their fields" may not be
equivalent to "the objects these structures represent are equal".  If,
say, experience with Ada indicates that they are frequently equivalent,
and that people do use structure comparison heavily, that experience
might, with some care, be applicable to C; otherwise, the right thing to
do is NOT to put it into the standard, but put it into GCC or some other
compiler and see how often it's really used. 

bbadger@x102c.harris-atd.com (Badger BA 64810) (07/25/89)

In article <2267@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes:
[..stuff deleted...]
>I've seen no indication that it's sufficiently relevant *that it should
>be added to the language*; any definition of structure comparison would
>be peppered with caveats like "no unions" and "no arrays" and "it
>compares pointer values, not what they point to" and the like.

When I entered this discussion, it was centering on whether struct comparison
was a well-defined and useful operation.  I think that has been established
for structs not containing unions.  Whether to add it to the language --
in particular whether to include it in ANSI C -- is another, more 
(mostly?) political question.  

>
>Yes, there have been cases I've seen where it might have been
>convenient, but I really *can* live with doing the member-by-member
>comparison myself.  And, given that there's no prior art, I agree with
>X3J11's decision to leave it out.  If somebody sticks it into their
>compiler as an extension and we can really find out from experience
>whether enough people actually *would* use it enough to justify its
>existence, then it can be considered as an extension if it actually is
>used enough and doesn't confuse people.

There may be ``no prior art'' with standard C, but let's not ignore
prior art from other programming languages.  Struct comparison is less
confusing than unions and bit fields.  Anyway, Real Programmers (TM)
like Guy probably aren't using struct assignment and function return
values either (:-).


>>3. polar complex example
>>
>>It took a while before I began to get the sneaky feeling that maybe 
>>Henry Spencer had put one over me here.
>>
>>His argument was that because a certain _mathematical_ concept (complex
>>comparison) couldn't be supported this was (one of the) grounds for 
>>omitting a _language_ concept (struct comparison). 
>
>I think Henry's point, like mine, is that the total number of cases
>where somebody, in effect, defines an equality operator over some data
>type seems likely to be much larger than the number of cases where this
>operator is just a member-by-member comparison; complex numbers
>represented in polar form are one example.  Given that, I'd like to see
>some real-world experience with such a feature before seeing it blessed
>in a standard.... 
>
>The original poster *did*, in fact, effectively ask "why isn't structure
>comparison in ANSI C"?  The question was answered, by Henry and others,
>and I have yet to see a *single* response to that answer that, to me,
>would justify the inclusion of such a feature *in the ANSI C standard*,
>given the lack of prior art (and yes, there *was* prior art for function
>prototypes, namely in C++; it may not have been in an otherwise "pure" C
>implementation, but at least it existed).  I suspect *I* wouldn't use
>structure comparison if it were available to me in C; I'm almost certain
>I wouldn't use it if it were available to me in C++ - I'd define an "="
>operator for the data type, instead, and not be forced to rely on
>member-by-member comparison being the right answer.

Of course, you wouldn't be *forced* to rely on member-by-member comparison!
You could still write your own function, or more likely macro.  Careful 
programmers would probably do that anyway, to maintain the abstraction.
However, wouldn't it be nice if you *could* write

#define COMPLEX_EQUAL(a,b)	((a) == (b))

instead of 

#define COMPLEX_EQUAL(a,b)	((a).real == (b).real && (a).imag == (b).imag)

(Whew!  That was a really tough one!)


What if we define:

typedef struct _vector_t {int	x,y,z;} vector_t;
typedef struct _matrix_t {vector_t	x,y,z;} matrix_t;

static const matrix_t identity_matrix = {{1,0,0},{0,1,0},{0,0,1}};
static matrix_t m1 = {{1,0,0},{0,0,1},{0,1,0}};

It may be convenient to inquire:
{	int flag = m1 == identity_matrix; ....}

Isn't it good?


I'll quote from ``C: A Reference Manual'' by Harbison and Steele, 

    p 106:
    5.7.1 Operations on Structures
    ...
     It is not permitted to compare two structures for equality. An
    object of a structure type is a sequence of components of other
    types.  Because certain data objects may be constraind by the
    target computer to lie on certain addressing boundaries, a
    structure object may contain ``holes,'' storage units that do not
    belong to any component of the structure.  The holes would make
    equality tests implemented as a wholesale bit-by-bit comparison
    unreliable, and component-by-component equality tests would be too
    expensive.  (Of course, the programmer may write
    component-by-component equality functions.)

I don't find this persuasive.  If an equality test is required, the 
programmer is going program it anyway.  The problems performing the test
don't go away just because the compiler has passed the responsibility to 
the programmer.  Perhaps the compiler will be able to produce better code
than the average programmer.  If not, (and I'm repeating myself here,) 
a programmer can usually avoid any language feature he doesn't like and 
do it the old-fashioned way.  

I don't know much about the politics and practicalities of introducing 
concepts to the current ANSI C.  I've heard that the purpose is more to 
standardize existing practice than to correct deficiencies that aren't 
outright faults.  So be it.  

I'm posting more in the hope of rescuing this simple idea from the 
criticisms by Guy and others that struct comparison is complex, 
ill-defined or not useful.

Bernard A. Badger Jr.	407/984-6385          |``Use the Source, Luke!''
Secure Computer Products                      |``Get a LIFE!''  -- J.H. Conway
Harris GISD, Melbourne, FL  32902             |Buddy, can you paradigm?
Internet: bbadger%x102c@trantor.harris-atd.com|'s/./&&/g' Tom sed expansively.

jeffrey@algor2.uu.net (Jeffrey Kegler) (07/25/89)

In article <2280@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes:
>>You compare pointer values.
>
>Which may not always correspond to what you want; i.e., "these two
>structure values have the same values in all their fields" may not be
>equivalent to "the objects these structures represent are equal".  If,
>say, experience with Ada indicates that they are frequently equivalent,
>and that people do use structure comparison heavily, that experience
>might, with some care, be applicable to C; otherwise, the right thing to
>do is NOT to put it into the standard, but put it into GCC or some other
>compiler and see how often it's really used. 

Precisely right.  If we were arbitrarily stuck with an untested
interpretation of structure comparison, we might soon regret it.  I
have my guess as the best way to compare structures by default (ignore
unions and padding, compare pointers by value), but any guess that
winds up in the standard we would be stuck with for a long time to
come.

The proper way to lobby for your favorite method of structure
comparison is to hack GCC, and submit it to a jury of our peers.  Just
sticking a best guess into a standard is not a good idea.  The ANSI C
Committee deserves congratulations for having recognized this.
-- 

Jeffrey Kegler, President, Algorists,
jeffrey@algor2.UU.NET or uunet!algor2!jeffrey
1762 Wainwright DR, Reston VA 22090

ray@philmtl.philips.ca (Raymond Dunn) (07/26/89)

In article <2280@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes:
>.........If,
>say, experience with Ada indicates that they are frequently equivalent,
>and that people do use structure comparison heavily, that experience
>might, with some care, be applicable to C

I'm glad Ada has been mentioned, because that language demonstrates clearly the
problems when all things for all men are added to a language.

'C' adequately provides the basic tools you need to build your own pet version
of struct comparison, as well as most of the other things on your particular
wish-list.

'C' is already starting to suffer from committee-itis and library-itis.

Keep it lean...   keep it lean!!!
-- 
Ray Dunn.                    | UUCP: ..!uunet!philmtl!ray
Philips Electronics Ltd.     | TEL : (514) 744-8200  Ext: 2347
600 Dr Frederik Philips Blvd | FAX : (514) 744-6455
St Laurent. Quebec.  H4M 2S9 | TLX : 05-824090

guy@auspex.auspex.com (Guy Harris) (07/26/89)

>If ANSI C had permitted even a strongly constrained comparable struct type
>these problems would have been eliminated.  (E.g. add "comparable" struct
>to the language:

OK, what if I want to see if one file offset is larger than, or smaller
than, another?  It may be uncommon, or it may not - until somebody's
scoured a large body of code, I'd assume there is code that does
comparisons on file offsets, since it is permissible on integral
types.... 

>compilers would be permitted to reorder to allow comparison as a
>byte-by-byte comparison;

Reorder structure members?  I think that's a separate issue....

>Even off_t would have been helped, as only the add and subtract operators
>would have to be macros were this possible.  Those are not _too_
>common.

Perhaps add and subtract aren't as common as other operators, but I'd
still be a bit annoyed if I couldn't add and subtract file offsets;
unlike structure comparison, I've used addition of file offsets on a
number of occasions.

>(Something has to give with off_t, and soon; 4Gb disks are coming along
>very soon.   It would be nice to find a way to deal with them without
>having to rewrite every use of off_t and lseek() in the world.

Me, I'd vote for "long long int", which I doubt would be "otherwise
unneeded" - Convex, at least, thought they needed it.  As an integral
type, it should require less rewriting (although there is the problem
that existing systems define "off_t" otherwise - but that problem exists
with other proposed changes to "off_t" as well).

guy@auspex.auspex.com (Guy Harris) (07/26/89)

>Just like the compiler doesn't have enough information to
>initialize a union, right? !
>
>A simple rule like comparing first members of unions and the
>pointers themselves would work nicely in a lot of cases and
>match the way initialization works.

Uh, as far as I'm concerned, the "first member" rule's only use is to
give a meaning to the default initialization of unions.  It means I
can't initialize, say, an array of union types or objects containing
union types if different elements of that array have different union
members "active", for example (e.g., a symbol table, with symbols
having, say, string, integral, or floating-point values, selected by a
discriminator - if I want the initial symbol table to have values of
more than one type, I lose).

Had it not been for the necessity of giving *some* meaning to
initialization of unions, I'd just as soon have left initialization of
them undefined....

nevin1@cbnewsc.ATT.COM (nevin.j.liber) (07/28/89)

In article <2410@trantor.harris-atd.com> bbadger@x102c.harris-atd.com (Badger BA 64810) writes:

>When I entered this discussion, it was centering on whether struct comparison
>was a well-defined and useful operation.  I think that has been established
>for structs not containing unions.  Whether to add it to the language --
>in particular whether to include it in ANSI C -- is another, more 
>(mostly?) political question.  

It is NOT mostly a political question!  There are problems that would
be introduced with it!  Consider the following scenario:

Jane Programmer, fresh out of college, reads all about C with the
struct equality extension (C-SEE :-)), and is raring to go program
with it.  She, along with her colleages, write 10,000,000 lines of code
in C-SEE.  An example of a struct used in this code (and there are
thousands) is:

	struct typical
	{
		int	foo;
			/* ... hundreds of variables */
		char	bar;
	}

Three years later, you come on the scene.  Your boss asks you to make
some enhancements to the ten million line program, and one of them requires
you to change struct typical (among other things).  Now it looks like:

	struct typical
	{
		int	foo;
			/* ... hundreds of variables */
		char	bar;
		short	uh_oh;
	}

Guess what happens?  The code breaks!!  By seemingly innocently adding
a field to the struct, you now have to go through and fix all the code
you broke.  If you are lucky, and you remembered to do this before you
sent the code to the testers, you could get the compiler to find all
the instances of the struct (by simply adding a union to the struct
typical definition, and having the compiler give you the error lines),
but that isn't the point.

Also, how do you plan on fixing the code?  Are you going to think about
each and every struct comparison, and decide on-the-spot whether or not
the rewrite it the way you would in standard C (if a union was added to
typical, you would *have* to rewrite ALL the C-SEE dependent stuff)?


The only situation where the C-SEE extension would be useful is when
you know that you just want to compare all the elements of the struct
together.  In order to do this, you can't abstract over the struct; you
have to *know* what each element is in order to use C-SEE safely.  It
is better self-documentation, as well as safer, to just write out the
member comparisions longhand.
-- 
NEVIN ":-)" LIBER  AT&T Bell Laboratories  nevin1@ihlpb.ATT.COM  (312) 979-4751

guy@auspex.auspex.com (Guy Harris) (07/29/89)

...

 >		char	bar;
 >		short	uh_oh;
 >	}
 >
 >Guess what happens?  The code breaks!!  By seemingly innocently adding
 >a field to the struct, you now have to go through and fix all the code
 >you broke.

Are you implying here that:

	1) "uh_oh" isn't supposed to be compared when you do structure
	   comparisons

or

	2) C-SEE compiles all structure comparisons into comparisons of
	   all the bytes of the structure?

If 2), I think it's been established that the *only* correct definition
of structure comparison would be member-by-member, and if that means the
structure comparison doesn't compile into e.g. a single "CMPCn"
instruction on a VAX, too bad.  While this may surprise some users of
the structure comparison construct (and, if it surprises a lot of them,
it means advocates of structure comparison had better be prepared to
educate said users), it means that adding a structure member that causes
padding to be added to a structure won't break structure comparisons.

peter@ficc.uu.net (Peter da Silva) (07/29/89)

In article <2092@cbnewsc.ATT.COM>, nevin1@cbnewsc.ATT.COM (nevin.j.liber) writes:
> Guess what happens?  The code breaks!!

Why?

> It is better self-documentation, as well as safer, to just write out the
> member comparisions longhand.

And how many times do you forget one?
-- 
Peter da Silva, Xenix Support, Ferranti International Controls Corporation.
Business: peter@ficc.uu.net, +1 713 274 5180. | "...helping make the world
Personal: peter@sugar.hackercorp.com.   `-_-' |  a quote-free zone..."
Quote: Have you hugged your wolf today?  'U`  |    -- hjm@cernvax.cern.ch

mcdaniel@uicsrd.csrd.uiuc.edu (Tim McDaniel) (07/29/89)

In article <2288@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris)
writes:
>OK, what if I want to see if one file offset is larger than, or smaller
>than, another?
...
>Perhaps add and subtract aren't as common as other operators, but I'd
>still be a bit annoyed if I couldn't add and subtract file offsets;
>unlike structure comparison, I've used addition of file offsets on a
>number of occasions.

The BSD 4.3 man page for fseek(3S) and ftell(3S) says:

     _Ftell_ returns the current value of the offset relative to
     the beginning of the file associated with the named _stream_.
     It is measured in bytes on UNIX; on some other systems it is
     a magic cookie, and the only foolproof way to obtain an
     _offset_ for _fseek_.

K&R 2, 1st edition, p. 248, says (for fseek):

     For a binary file, the position is set to _offset_ characters
     from _origin_, which may be SEEK_SET (beginning), SEEK_CUR
     (current postion), or SEEK_END (end of file).  For a text stream,
     _offset_ must be zero, or a value returned by _ftell_ (in which
     case _origin_ must be SEEK_SET).

Thus, in pANS C, a *text* stream offset may indeed be a "magic
cookie", for which comparison and arithmetic is undefined.  Binary
mode requires an actual character number.  (Personally, I see no point
in having the distinction; I'd like to see all _offset_s be magic
cookies.  It's too late, now that X3J11 has acted.)

--
"Let me control a planet's oxygen supply, and I don't care who makes
the laws." - GREAT CTHUHLU'S STARRY WISDOM BAND (via Roger Leroux)
 __
   \         Tim, the Bizarre and Oddly-Dressed Enchanter
    \               mcdaniel@uicsrd.csrd.uiuc.edu
    /\       mcdaniel%uicsrd@{uxc.cso.uiuc.edu,uiuc.csnet}
  _/  \_     {uunet,convex,pur-ee}!uiucuxc!uicsrd!mcdaniel

guy@auspex.auspex.com (Guy Harris) (07/30/89)

>The BSD 4.3 man page for fseek(3S) and ftell(3S) says:

Which is irrelevant, since the original poster was talking about, among
other things, "off_t" from POSIX, which *is* an integral type, and POSIX
file offsets, which *are* measured in bytes.  The fact that, in ANSI C,
you can get away with "fseek()" offsets not being integral types isn't
relevant; in POSIX, they'd better be integral types, and absent
something like "long long int", which isn't in strict ANSI C, the best
you can do is "long", which in many implementations is, for better or
worse, 32 bits.

mmengel@cuuxb.ATT.COM (Marc W. Mengel) (08/03/89)

In article <2289@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes:
>>Just like the compiler doesn't have enough information to
>>initialize a union, right? !
>Uh, as far as I'm concerned, the "first member" rule's only use is to
>give a meaning to the default initialization of unions.  It means I
>can't initialize, say, an array of union types or objects containing
>union types if different elements of that array have different union
>members "active"...

Why can't we adopt a simple rule like initializing the first union member
of the right type? something like

   union { char c; int i; float f;} bozo[3]={ (int)5, (char)5,(float)5};

would make it clear which union member is being initialized; if there
are two union members of the same type it doesnt matter which one
of the two you initialize.  For more complicated types (unions of
structures, etc.) you would have to recursively check the types of
members to find a match, but you already have to do the recursive
checks to match curly braces to sub-structures or arrays are in 
the right place, so...

>Had it not been for the necessity of giving *some* meaning to
>initialization of unions, I'd just as soon have left initialization of
>them undefined....
-- 
 Marc Mengel					mmengel@cuuxb.att.com
 						attmail!mmengel
 						...!{lll-crg|att}!cuuxb!mmengel