[comp.lang.c++] how do you define classes that refer to each other ???

mh@awds26.imsd.contel.com (Mike Hoegeman) (03/20/90)

This is probably a stupid question, but here goes anyway.  how do you
set up two classes that refer to each other ?  Here's a simple example
to demonstrate my problem.


    class thing {
	int data[5];
	void execute(stack &s) {
	    s.push(this);
	}
    }

    class stack {
	stack_store thing[500];
	int si;
    private:
	void push(thing *t);
	stack() { 
	    si = 0; 
	}
    }

    void 
    stack::push(thing *t) {
	stack_store[si++] = *t;
    }

    main() {
	thing mything;
	stack mystack;

	mything.execute();
    }

Now, the question is. How do I properly forward declare class 'stack'
to class 'thing' so that everybody is happy ?

I have the stroustrup C++ book as well as the Dewhurst/Stark C++ book
so an admonishing "you'll find this on page <n> in Stroustrup you
moron" is fine :-). I've scanned through these two books but have not
found the answer.


thanks, 

- mike hoegeman, mh@wlbr.imsd.contel.com

rfg@ics.uci.edu (Ronald Guilmette) (03/20/90)

In article <49689@wlbr.IMSD.CONTEL.COM> mh@awds26.UUCP (Mike Hoegeman) writes:
>
>This is probably a stupid question, but here goes anyway.  how do you
>set up two classes that refer to each other ?  Here's a simple example
>to demonstrate my problem.
>
>
>    class thing {
>	int data[5];
>	void execute(stack &s) {
>	    s.push(this);
>	}
>    }
>
>    class stack {
>	stack_store thing[500];
>	int si;
>    private:
>	void push(thing *t);
>	stack() {
>	    si = 0;
>	}
>    }
>
>    void
>    stack::push(thing *t) {
>	stack_store[si++] = *t;
>    }
>
>    main() {
>	thing mything;
>	stack mystack;
>
>	mything.execute();
>    }
>
>Now, the question is. How do I properly forward declare class 'stack'
>to class 'thing' so that everybody is happy ?

Declare:

	class stack;

before the declaration of class "thing".  Then move the body of the function
thing::execute out of the "thing" class declaration and put it after the
class declaration for class "stack".

>I have the stroustrup C++ book as well as the Dewhurst/Stark C++ book
>so an admonishing "you'll find this on page <n> in Stroustrup you
>moron" is fine :-). I've scanned through these two books but have not
>found the answer.

You'll find this on page <n> in Stroustrup you moron. :-)

(Sorry.  I couldn't resist. :-)


// Ron Guilmette (rfg@ics.uci.edu)
// C++ Entomologist
// Motto:  If it sticks, force it.  If it breaks, it needed replacing anyway.

gbaciu@watcgl.waterloo.edu (George Baciu [CGL]) (03/21/90)

In article <49689@wlbr.IMSD.CONTEL.COM>, mh@awds26.imsd.contel.com (Mike Hoegeman) writes:
> 
> This is probably a stupid question, but here goes anyway.  how do you
> set up two classes that refer to each other ?  Here's a simple example
> to demonstrate my problem.

..... example changed for correct compilation below.....

> 
> Now, the question is. How do I properly forward declare class 'stack'
> to class 'thing' so that everybody is happy ?
> 
> - mike hoegeman, mh@wlbr.imsd.contel.com

This is a real problem in C++ and it is reminiscent of early implementations
of Pascal. Now, most Pascal compilers have a "forward" keyword which allows
circular declarations of procedural blocks. I wish C++ would provide the same
sort of facility in the next releases.

Now, in order to deal with circular declarartions in C++ I had to do
the following:
	0. declare the names of all the classes involved, as
		class x;
		class y;

	1. data members of type class can only be references or pointers  but
	   NOT objects.

	2. all the inline functions, implementing the methods containing
	   the member classes involved, must follow
	   the declarations of all the classes involved; this implies
	   that you must have only the prototype declarations of the methods
	   within the class itself.

For your example, the following change should work:

------------------------------ cut here --------------------------------

     class	thing;
     class	stack;

     class thing {
 	int data[5];
     public:
 	void execute(stack&);		// implemented after all classes
     };
 
     class stack {
 	thing stack_store[500];
 	int si;
     public:
 	void push(thing *t);		// this may be implemented here since
 	stack() { si = 0; }		// class thing is already declared.
     };
 
     void 
     stack::push(thing *t) { stack_store[si++] = *t; }

     void
     thing::execute(stack &s) { s.push(this); }
 
     main() {
 	thing mything;
 	stack mystack;
 
 	mything.execute(mystack);
     }
 
------------------------------ cut here --------------------------------

Note, that the changed code compiles correctly with cfront 2.0.

    -- George Baciu -------------------------------------------------
    |  GBaciu@watcgl.Waterloo.edu        GBaciu@watcgl.UWaterloo.CA  |
    |                  * Computer Graphics Lab *                     |
    |  University of Waterloo - Waterloo, Ontario, Canada - N2L 3B5  |
    ------------------------------------------------------------------

jpw@bagend.UUCP (Parnell Watkins) (03/22/90)

In article <49689@wlbr.IMSD.CONTEL.COM> mh@awds26.UUCP (Mike Hoegeman) writes:
>
>Now, the question is. How do I properly forward declare class 'stack'
>to class 'thing' so that everybody is happy ?
>
The answer is quit simple but I haven't seen it anywhere either ( I
wasn't particularly looking).  I just borrowed from C.

	class Foo {
		friend class Bar;
		...
	};

	class Bar {
		friend class Foo;
		...
	};

The order of the class declarations is no longer a concern.  Forward
declarations are made by the use of the keyword class.


J. Parnell Watkins, Jr. 	gatech![rebel|galbp]!dkstar!jpw
Atlanta, Ga. 30243		gatech!bagend!jpw
"The country of Colombia...has over twice as many species of birds as the 
continental United States though it is only one-seventh the size."  
---John S. Dunning, Portraits of Tropical Birds

pcg@odin.cs.aber.ac.uk (Piercarlo Grandi) (03/23/90)

In article <49689@wlbr.IMSD.CONTEL.COM> mh@awds26.imsd.contel.com (Mike Hoegeman) writes:

   This is probably a stupid question, but here goes anyway.  how do you
   set up two classes that refer to each other ?

Another heathen that does not hold my excessively precious advice as
sacred :-)!

If you write all your member functions *after* the class definition, a
lot of problem disappear. You may also need to forward declare the
classes, of course (if you use the typedef style names).

The general scheme is:


	struct c1;	/* forward declarations */
	struct c2;
	struct ...;

	struct c1
	{
		int	m1;
		c2	*m2;

		int	p1(int);

		friend	f1(c2 &,char);
		...
	};

	struct c2
	{
		double	m1;
		c1	m2;

		double	p1(double);

		friend	f2(c2 &,short);
		...
	};

	...

	int	c1::p1(int i) { ... }

	int	f1(c2 &o, char c) { ... }

	double	c2::p1(double d) { ... }

	int	f2(c2 &o,short s) { ... }

	...

Notice that of course you *cannot* have two classes that inherit from
each other or that appear as fields in each other; true recursive
definitions are not allowed.
--
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

Lindsay.Marshall@newcastle.ac.uk (Lindsay F. Marshall) (03/26/90)

Just to be awkward, can anybody suggest a legal way of doing the
following :-

	class B;

	class A
	{
	public:
	    A();
    
	    void fa(void (*B::fb)());
	};

	class B
	{
	public:
	    B();
    
	    void fb(void (*A::fa)());
	};

CC 2.0 gives syntax errors when you try to do this, g++ 1.37.1 gives
reasonable error messages. I don't know why you might want to do this,
but I'm sure someone out there will want to do it some time.

Lindsay
--
MAIL : Lindsay.Marshall@newcastle.ac.uk (UUCP: s/\(.*\)/...!ukc!\1/)
POST : Computing Laboratory, The University, Newcastle upon Tyne, UK NE1 7RU
VOICE: +44-91-222-8267 		FAX: +44-91-222-8232

rfg@ics.uci.edu (Ronald Guilmette) (03/28/90)

In article <1990Mar26.115427.14031@newcastle.ac.uk> Lindsay.Marshall@newcastle.ac.uk (Lindsay F. Marshall) writes:
>Just to be awkward, can anybody suggest a legal way of doing the
>following :-
>
>	class B;
>
>	class A
>	{
>	public:
>	    A();
>
>	    void fa(void (*B::fb)());
>	};
>
>	class B
>	{
>	public:
>	    B();
>
>	    void fb(void (*A::fa)());
>	};
>
>CC 2.0 gives syntax errors when you try to do this...

As well it should.  It seems that you have the *'s in the wrong places.
Try using these function declarations instead:

	void fa(void (B::*fb)());

	void fb (void (A::*fa)());


>g++ 1.37.1 gives reasonable error messages.

I'd like to know how you define "reasonable". g++ certainly doesn't allow
such things, either when they are written with correct syntax or not.

When written incorrectly (as shown in your example) I have no idea what
g++ will say.

When written with correct syntax, g++ will probably just say "syntax error".

// Ron Guilmette (rfg@ics.uci.edu)
// C++ Entomologist
// Motto:  If it sticks, force it.  If it breaks, it needed replacing anyway.