[comp.lang.c++] g++ core dump from improper use of extern "C"

pcg@aber-cs.UUCP (Piercarlo Grandi) (01/12/90)

In article <494@ucl-cs.UUCP> T.Day@ucl-cs.UUCP writes:
    From: Tim Day <T.Day@uk.ac.ucl.cs>
    
    Piercarlo "Peter" Grandi writes:
    > using 'struct some *' before having seen 'struct some {...'
    > is legal and without problem in both C and C++
    
    Not quite.  You have to say
    	struct frob;
    before you can use pointers or references to frob.

Not really, neither in C nor in C++. Try compiling:

	struct a {struct b *b;} a;
	struct b {struct a *a;} b;

It shall work, both in C and C++. There has never been a
requirement that the name of a struct be known before its use,
if its use is to declare a pointer to the struct. The compiler
need not bother; anything that comes after 'struct' must be a
struct name, and a pointer has always the same size.

You are confusing this point with the the fact that in C++,
saying 'struct a;', which in it as well as in C++ is an empty
declaration, and no special construct all, also has the effect
of typedef'ing 'a' as 'struct a'. The compiler needs to know
that a name is a typedef, otherwise parsing declarations cannot
be done.

In this, the C++ practice of saying 'class c;' is NOT a forward
declaration; it i just the old harmless practice of having empty
declarations, coupled with the new side effect of having an
implicit typedef. Saying beforehand 'class c;' in C++ is not
needed if you write 'class c *m;' instead of 'c *m;'.

The effect is the same if you just say 'typedef class a ;'.
With the following preprocessor macro you get the same effect
in C:

	#define mode(FLAVOR,NAME) \
		typedef FLAVOR NAME NAME; FLAVOR NAME

Note that the typedef appears *before* the struct definition, if
any; if you follow this with a ';' the struct definition becomes
a harmless empty delcaration.

You can use this in C as

	mode(struct,complex) { float re,im; };
	complex a,b,c;

or

	mode(struct,clutch);
	mode(struct,car) { ...; clutch *theClutch; ... };

just like in C++, if you have got fed up with using leading
'struct', 'union', 'enum' (enums are special though in C)...
-- 
Piercarlo "Peter" Grandi           | ARPA: pcg%cs.aber.ac.uk@nsfnet-relay.ac.uk
Dept of CS, UCW Aberystwyth        | UUCP: ...!mcvax!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk

mike@umn-cs.CS.UMN.EDU (Mike Haertel) (01/13/90)

In article <1581@aber-cs.UUCP> pcg@cs.aber.ac.uk (Piercarlo Grandi) writes:
>Not really, neither in C nor in C++. Try compiling:
>
>	struct a {struct b *b;} a;
>	struct b {struct a *a;} b;
>
>It shall work, both in C and C++. There has never been a
>requirement that the name of a struct be known before its use,
>if its use is to declare a pointer to the struct.

Just to be pedantic, this is not strictly true.  It is OK to
declare a pointer to an incomplete type, so this works *here*.
But consider the following counterexample:

/*1*/	struct b { int x; };
/*2*/
/*3*/	void foo() {
/*4*/	    struct b;
/*5*/	    struct a { struct b *b; } *a;
/*6*/	    struct b { struct a *a; } *b;
/*7*/
/*8*/	    b = a->b;
/*9*/	}

In this case, line 4 is necessary to make "struct b" an incomplete type.
Otherwise, the "struct b *b" in line 5 would refer to the "struct b"
declared on line 1, and line 8 would be a pointer type mismatch.

The moral of the story is to declare all your types at file scope.
-- 
Mike Haertel <mike@ai.mit.edu>
"Everything there is to know about playing the piano can be taught
 in half an hour, I'm convinced of it." -- Glenn Gould

rfg@ics.uci.edu (Ron Guilmette) (01/14/90)

In article <18157@umn-cs.CS.UMN.EDU> mike@umn-cs.cs.umn.edu (Mike Haertel) writes:
>
>Just to be pedantic, this is not strictly true.  It is OK to
>declare a pointer to an incomplete type, so this works *here*.
>But consider the following counterexample:
>
>/*1*/	struct b { int x; };
>/*2*/
>/*3*/	void foo() {
>/*4*/	    struct b;
>/*5*/	    struct a { struct b *b; } *a;
>/*6*/	    struct b { struct a *a; } *b;
>/*7*/
>/*8*/	    b = a->b;
>/*9*/	}
>
>In this case, line 4 is necessary to make "struct b" an incomplete type.
>Otherwise, the "struct b *b" in line 5 would refer to the "struct b"
>declared on line 1...

Buzzzzzzzt!  Sorry, no!  But thanks for playing our game.  Just for being
such a good sport you win a free copy of our home game, complete with the
new 2.0 Reference Manual.

:-) :-) :-)

// rfg

rfg@ics.uci.edu (Ron Guilmette) (01/15/90)

In article <25AFE9B7.14113@paris.ics.uci.edu> rfg@ics.uci.edu (Ron Guilmette) writes:
>In article <18157@umn-cs.CS.UMN.EDU> mike@umn-cs.cs.umn.edu (Mike Haertel) writes:
>>
>>Just to be pedantic, this is not strictly true.  It is OK to
>>declare a pointer to an incomplete type, so this works *here*.
>>But consider the following counterexample:
>>
>>/*1*/	struct b { int x; };
>>/*2*/
>>/*3*/	void foo() {
>>/*4*/	    struct b;
>>/*5*/	    struct a { struct b *b; } *a;
>>/*6*/	    struct b { struct a *a; } *b;
>>/*7*/
>>/*8*/	    b = a->b;
>>/*9*/	}
>>
>>In this case, line 4 is necessary to make "struct b" an incomplete type.
>>Otherwise, the "struct b *b" in line 5 would refer to the "struct b"
>>declared on line 1...
>
>Buzzzzzzzt!  Sorry, no!  But thanks for playing our game.  Just for being
>such a good sport you win a free copy of our home game, complete with the
>new 2.0 Reference Manual.

OK, before you folks send me 8 zillion letters telling me what a dumbs***
I am for having posted this, let me just say that I know already!

Section 9.1 of the new 2.0 manual does indeed require the declaration of
struct b in the example above.

I guess that the booby prize (the 2.0 Ref. Man.) goes to me instead, together
with a set of spectacles to read it with!

>
>:-) :-) :-)

Well, at least I had to good sense to put in smiles so that nobody would
think that I was being hostile.  (No... not hostile... just stupid.  I trust
that stupidity is a forgivable sin in this particular newsgroup.)

// rfg

pcg@rupert.cs.aber.ac.uk (Piercarlo Grandi) (01/15/90)

In article <25B0E7F7.6002@paris.ics.uci.edu> rfg@ics.uci.edu (Ron Guilmette) writes:

    In article <25AFE9B7.14113@paris.ics.uci.edu> rfg@ics.uci.edu (Ron Guilmette) writes:
    >In article <18157@umn-cs.CS.UMN.EDU> mike@umn-cs.cs.umn.edu (Mike Haertel) writes:
    >>
    >>Just to be pedantic, this is not strictly true.  It is OK to
    >>declare a pointer to an incomplete type, so this works *here*.
    >>But consider the following counterexample:
    >>
    >>/*1*/	struct b { int x; };
    >>/*2*/
    >>/*3*/	void foo() {
    >>/*4*/	    struct b;
    >>/*5*/	    struct a { struct b *b; } *a;
    >>/*6*/	    struct b { struct a *a; } *b;
    >>/*7*/
    >>/*8*/	    b = a->b;
    >>/*9*/	}
    >>
    >>In this case, line 4 is necessary to make "struct b" an incomplete type.
    >>Otherwise, the "struct b *b" in line 5 would refer to the "struct b"
    >>declared on line 1...

Well, not in C; in C line 4 is an empty declaration and the type
used is the one defined on line 1 (see K&R version 1, "The ice is
admittedly thin here" footnote). In C++ 2.0 line 4 does declare a
new type, but only because of special casing:

    >Buzzzzzzzt!  Sorry, no!  But thanks for playing our game.  Just for being
    >such a good sport you win a free copy of our home game, complete with the
    >new 2.0 Reference Manual.

    OK, before you folks send me 8 zillion letters telling me what a dumbs***
    I am for having posted this, let me just say that I know already!

The special case is this: in C++ a struct or class implies an
automatic typedef; a typedef in theory does not declare a new
type, but only names an existing one; so line 4 should just
define a new typedef called 'b' for the type defined in line 1.
This means that it is virtually impossible then to define
mutually recursive types in an inner scope if *both* of them have
the same name as one in an outer scope. It would also make for
great hidden bugs. Try the following C program:

    typedef short t;

    static struct a {struct b *b; long l[1];} a;

    main()
    {
	{
	      auto t t1;
	      typedef double t;
	      auto t t2;

	      printf(" size t1 %u, size t2 %u\n",sizeof t1, sizeof t2);
    	}

    	{
	      struct a;
	      struct c {struct a *a; long l[2];} c;
	      struct a {struct c *c; long l[3];} a;

	      printf(" size a %u, size c %u, size c.a %u\n",
		  sizeof a, sizeof c, sizeof *(c.a));
    	}

    	return 0;
    }

This should print, on your typical 32 bit byte oriented machine, something
like 8 and 2 on the first line and 16, 12, 8 on the second line.

	NOTE: it will fail to compile on some ancient C compilers that
	erroneously handle a typedef like a #define, e.g. Sun's. Actually
	the problem is even more subtle, and has to do with the fact that
	certain classes of identifiers in C/C++ have global scope even if
	defined in an inner scope; struct/union/class names used to be such,
	if memory serves me right, and one could not redefine a name for one
	in an inner scope. C++, and C++ 2.0, complicates scope rules even
	further.

I am not suprised then that in the new C++ 2.0 empty declarations
have been special cased to imply the definition of a NEW type:

    Section 9.1 of the new 2.0 manual does indeed require the declaration of
    struct b in the example above.

... but this adds a new wart. Well, happy hacking! :-(.
--
Piercarlo "Peter" Grandi           | ARPA: pcg%cs.aber.ac.uk@nsfnet-relay.ac.uk
Dept of CS, UCW Aberystwyth        | UUCP: ...!mcvax!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk

pcg@aber-cs.UUCP (Piercarlo Grandi) (01/17/90)

I wrote:

        typedef short t;
    
        static struct a {struct b *b; long l[1];} a;
    
        main()
        {
	    {
    	      auto t t1;
    	      typedef double t;
    	      auto t t2;
    
    	      printf(" size t1 %u, size t2 %u\n",sizeof t1, sizeof t2);
	    }
    
	    {
    	      struct a;
    	      struct c {struct a *a; long l[2];} c;
    	      struct a {struct c *c; long l[3];} a;
    
    	      printf(" size a %u, size c %u, size c.a %u\n",
    		  sizeof a, sizeof c, sizeof *(c.a));
	    }
    
	    return 0;
        }
    
    This should print, on your typical 32 bit byte oriented machine, something
    like 8 and 2 on the first line and 16, 12, 8 on the second line.
    
    	NOTE: it will fail to compile on some ancient C compilers that
    	erroneously handle a typedef like a #define, e.g.
	Sun's. [ .... ] 

By the way, try it taking out the line 'struct a;'.

It has pointed out to me (a few pieces of Hate Mail :->) that
in Ansi C (which I love a lot :->) now 'struct b' *is* a forward
declaration, just like in C++. Special casing is still strong;
this means that 

On the other hand, of the (few: G++, Sun, Gould, Mips)
compilers that I have tried, *none* could handle correctly the
above code save for G++; they all chickened out at the
redefinition of 't', save for one that allows for type 'short
double' apparently...

G++, meritoriously, handled the above monstrosity with flair,
even issuing the most appropriate warnings, and printing 16, 2,
16 on the second line.

What's amusing is that the presence of the 'struct a;' line
does not seem to affect it. I think its absence should: as far
as I know, the only (regrettable) exception to the one-pass-rule
in C++ is class members, where in a member you can refer to
members declared later.
-- 
Piercarlo "Peter" Grandi           | ARPA: pcg%cs.aber.ac.uk@nsfnet-relay.ac.uk
Dept of CS, UCW Aberystwyth        | UUCP: ...!mcvax!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk