waldemar@mit-vax.LCS.MIT.EDU (Waldemar Horwat) (01/21/89)
Sender:
Reply-To: waldemar@mit-vax.UUCP (Waldemar Horwat)
Followup-To:
Distribution:
Organization: MIT Laboratory for Computer Science, Cambridge
Keywords:
Given the declarations below, which of the following seven statements
(numbered 1, 2, 4, 5, 6, 7, and 8) are acceptable in a "portable" C++ program?
Specifically, I am trying to clarify the following issue:
If a pointer to B can be passed to a function that takes a pointer to A,
then can a pointer to a pointer to B be passed to a function that takes a
pointer to a pointer to A? What if A is void?
I'd also be interested in hearing what real compilers do with the following
code.
struct A {int x;};
struct B:A {int y;};
extern void f1(void *);
extern void g1(A *);
extern void i1(A &);
extern void f2(void **);
extern void g2(A **);
extern void h2(void *&);
extern void i2(A *&);
B b,*pb,**ppb;
void test()
{
f1(pb); //1
g1(pb); //2
i1(b); //4
f2(ppb); //5
g2(ppb); //6
h2(pb); //7
i2(pb); //8
}
Waldemar Horwat
Internet: waldemar@vx.lcs.mit.edu
ark@alice.UUCP (Andrew Koenig) (01/21/89)
In article <5485@mit-vax.LCS.MIT.EDU>, waldemar@mit-vax.LCS.MIT.EDU (Waldemar Horwat) writes: > Given the declarations below, which of the following seven statements > (numbered 1, 2, 4, 5, 6, 7, and 8) are acceptable in a "portable" C++ program? > struct A {int x;}; > struct B:A {int y;}; > extern void f1(void *); > extern void g1(A *); > extern void i1(A &); > extern void f2(void **); > extern void g2(A **); > extern void h2(void *&); > extern void i2(A *&); > B b,*pb,**ppb; Here are my comments, along with the messages that appear when I run it on the (still experimental) cfront on my machine. > void test() > { > f1(pb); //1 OK. f1 takes a void* argument so you can give it a pointer to anything at all. > g1(pb); //2 OK. g1 takes an A*, you've given it a B*, and B is (publicly) derived from A. > i1(b); //4 OK. References are just like pointers. i1 takes an A&, you've given it a B&. > f2(ppb); //5 bad argument 1 type for f2(): B ** (void ** expected) No. You can convert anything to void* but you can't convert anything to void**. f2 takes a void** and ppb is a B**. > g2(ppb); //6 bad argument 1 type for g2(): B ** (A ** expected) No. Again, you can't treat a pointer to an B* as if if it were a pointer to an A*. > h2(pb); //7 warning: coercion of lvalue needed: temporary used to initialize reference Yes (!). h2 wants a reference to a void* and you've given it a B*. So it converts the B* to a void* (no problem there) and binds the reference to that. > i2(pb); //8 warning: coercion of lvalue needed: temporary used to initialize reference Yes -- same reason. You can convert pb to an A* and then bind a reference to it. > } -- --Andrew Koenig ark@europa.att.com
waldemar@mit-vax.LCS.MIT.EDU (Waldemar Horwat) (01/22/89)
> f1(pb); //1 > g1(pb); //2 > i1(b); //4 > f2(ppb); //5 > g2(ppb); //6 > h2(pb); //7 > i2(pb); //8 I tested the following two compilers so far and got different results: AT&T CFront 1.2.1 2/16/87 accepts all seven statements and produces the expected code. GNU g++ 1.25.0 accepts all statements except 5. I find it unusual that it can coerce **B to **A but not **B to **void. Given the diversity of compiler behavior in this area, I am curious as to what the "right" behavior, if any, is. Waldemar Horwat
paul@gill.UUCP (Paul Nordstrom) (11/27/89)
While attempting to port InterViews to the Oregon C++ compiler, I came across the following code (simplified drastically): typedef const void* TextData; static TextData SELF = (void*)-1; class Text { public: Text(TextData context = SELF); protected: TextData context; int size; }; class Whitespace : public Text { public: Whitespace(int size, TextData context = SELF); virtual Text* Copy(); }; Text* Whitespace::Copy () { #ifdef OREGON if ( context == this ) return new Whitespace(size, SELF); else return new Whitespace(size, context); #else return new Whitespace(size, context==this ? SELF : context); #endif } The portion of code that is compiled when OREGON is defined compiles (presumably) correctly under Oregon C++ (1.2Ag). The #else side is the code is as distributed in the Interviews release currently on uunet. Obviously (he says blithely :-) ) the two sections of code are equivalent. The Oregon compiler ("occ") yields the following error message on the original code: 27 * return new Whitespace(size, context==this ? SELF : context); ^203 *** 203: Error: Operands are of differing or incompatible type In case the spacing gets garbled, the ^203 is pointing to the question mark in the line of code, indicating that that is where the compiler thinks the error is. Assuming that I am correct that the two pieces of code are in fact equivalent, clearly occ has a bug of some kind: one piece compiles, one does not. My question is: are both pieces of code correct, or both incorrect. The issue I see is: should the compiler allow the test "context==this" when context is a void* and this is a Whitespace* ? Any comments, anyone? -- Paul Nordstrom Gill & Co., L.P. uunet!gill!paul
dan@oresoft.uu.net (Daniel Elbaum) (12/05/89)
In article <754@gill.UUCP> paul@gill.UUCP (Paul Nordstrom) writes:
:While attempting to port InterViews to the Oregon C++ compiler, I came
:across the following code (simplified drastically):
[simplified even more drastically:]
Whitespace(int size, TextData context = SELF);
static const void* SELF = (void*)-1;
:
:Text* Whitespace::Copy () {
:#ifdef OREGON
: if ( context == this )
: return new Whitespace(size, SELF);
: else
: return new Whitespace(size, context);
:#else
: return new Whitespace(size, context==this ? SELF : context);
:#endif
:}
The OREGON code compiles under occ; the conditional expression
gives this error:
: 27 * return new Whitespace(size, context==this ? SELF : context);
: ^203
:*** 203: Error: Operands are of differing or incompatible type
The questions: 1) are the two bodies of code equivalent?
2) if so, are they both correct or are they incorrect?
One difference between the two fragments is that the first does
not impose restriction on the compatibility of |SELF| and |context|,
while the second does require that they both be arithmetic types,
or a mix of pointers and null constant expressions, or both
references, or both of type |void|.
In this case the types are both susceptible to conversion to a
common type, namely |void*|, so the type of the result will
be |void*|. This type is not valid for the return value of
the function |Copy()|.
--
The disbelievers say: "Lo! This is a mere wizard." -Quran, Surah X
({uunet,tektronix,reed,sun!nosun,osu-cis,psu-cs}!oresoft!(dan)@oresoft.uu.net)