views:

1294

answers:

5

Hello!

I'm getting this error when dealing with a number of classes including each other:

error: expected class-name before '{' token

I see what is going on, but I do not know how to properly correct it. Here is an abstracted version of the code:

A.h

#ifndef A_H_
#define A_H_

#include "K.h"

class A
{
    public:
     A();

};

#endif /*A_H_*/

A.cpp

#include "A.h"

A::A() {}

B.h

#ifndef B_H_
#define B_H_

#include "A.h"

class B : public A
{ // error: expected class-name before '{' token
    public:
     B();
};

#endif /*B_H_*/

B.cpp

#include "B.h"

B::B() : A() {}

J.h

#ifndef J_H_
#define J_H_

#include "B.h"

class J
{
    public:
     J();
};

#endif /*J_H_*/

J.cpp

#include "J.h"

J::J() {}

K.h

#ifndef K_H_
#define K_H_

#include "J.h"

class K : public J
{ // error: expected class-name before '{' token
    public:
     K();
};

#endif /*K_H_*/

K.cpp

#include "K.h"

K::K() : J() {}

main.cpp

#include "A.h"

int main()
{
    return 0;
}


Starting in main.cpp, I can determine that this is what the compiler sees:

#include "A.h"

#ifndef A_H_
#define A_H_

#include "K.h"

#ifndef K_H_
#define K_H_

#include "J.h"

#ifndef J_H_
#define J_H_

#include "B.h"

#ifndef B_H_
#define B_H_

#include "A.h"

class B : public A
{ // error: expected class-name before '{' token

So, A's definition is not complete when we get to B. I've been told that sometimes you need to use a forward declaration and then move the #include statement into the .cpp file, but I'm not having any luck with that. If I try anything like that, I simply get the additional error:

error: forward declaration of 'struct ClassName'

I think maybe I'm just not doing things in the right places. Can someone please show me how to get this code to compile? Thank you very much!


Edit: I want to point out that this is just abstracted version of the real code. I realize that there are no references to K in A or B in J, but there are in the real code and I feel that they're completely necessary. Perhaps if I give a brief description of the real classes, someone can help me restructure or fix my code.

Class A is an abstract node class that acts as an interface for nodes in a graph. Class B is one of what will be a number of different implementations of A. In the same manner, class J is an abstract Visitor class and K is the corresponding implementation. Here is the code with a little more context:

A.h (Abstract Node)

#ifndef A_H_
#define A_H_

#include "K.h"

class K;

class A
{
    public:
     A();

     virtual void accept(const K&) const = 0;
};

#endif /*A_H_*/

A.cpp

#include "A.h"

A::A() {}

B.h (Concrete Node)

#ifndef B_H_
#define B_H_

#include "A.h"

class K;

class B : public A
{ // error: expected class-name before '{' token
    public:
     B();

     virtual void accept(const K&) const;
};

#endif /*B_H_*/

B.cpp

#include "B.h"

B::B() : A() {}

void B::accept(const K& k) const { k.visit(this); }

J.h (Abstract Visitor)

#ifndef J_H_
#define J_H_

#include "B.h"

class B;

class J
{
    public:
     J();

     virtual void visit(const B*) const = 0;
};

#endif /*J_H_*/

J.cpp

#include "J.h"

J::J() {}

K.h (Concrete Visitor)

#ifndef K_H_
#define K_H_

#include "J.h"

class B;

class K : public J
{ // error: expected class-name before '{' token
    public:
     K();

     virtual void visit(const B*) const;
};

#endif /*K_H_*/

K.cpp

#include "K.h"

K::K() : J() {}

void K::visit(const B*) const {};

main.cpp

#include "A.h"

int main()
{
    return 0;
}

I had to add some forward declarations to make some additional errors that appeared (when I added detail) go away. Some of them may not be necessary or correct.

+5  A: 

Your header inclusions are circular. A -> K -> J -> B -> A. Using forward declarations is the simplest way to avoid such complexity, but it is only possible when the class being declared only uses references or pointers to the class being included. You can't use forward declaration in K.h or B.h, for example, because they inherit from J and A respectively. However, you may be able to replace the #include "K.h" in A.h with class K; depending on how you are actually using K in A.

Talk about confusing.

Volte
I tried this and I got the "forward declaration of 'struct K'" error. I added some more detail to my question, so hopefully you can reevaluate it.
Scott
I notice that in your first example you have both the #include "K.h" and the class K; line. Did you try removing the #include line altogether or is that just an oversight in your post?
Volte
I don't understand. In my first example? I include K in class A, but there's no forward declaration. I tried removing the "#include" and adding a forward declaration like you said, but as I said, I got the error: "forward declaration of 'struct K'."
Scott
Sorry, I meant in the first snippet of your updated example. The one labelled 'A.h (Abstract Node)'. You have both an #include and a forward declaration in that snippet.
Volte
Ah yes, it's strange. I know that when everything compiles, I probably wont need that. However, strangely enough, when I don't forward declare class K, on the line that has the "accept" method declaration I get: "error: ISO C++ forbids declaration of 'K' with no type."
Scott
Perhaps I should remove all of the forward declarations and add the new errors in comments. Would you prefer that I do this?
Scott
+2  A: 

The problem is that your header files cyclically depend on each other. Don't include K.h in A.h and B.h in J.h. They aren't needed there.

Regarding your edit: this is not the best way for your objects to interact. Rethink your design. Who calls node.accept(visitor)? Can't you call visitor(node) directly?

Also, if you're trying to design a graph library, have a look at Boost.Graph library.

That said, since you only use a pointer to B in J.h, you don't have to include B.h, only forward-declare the class.

class B;

struct J
{
    virtual void visit(const B*) const = 0;
};

Then include B.h in your K.cpp file.

#include "K.h"
#include "B.h"

void K::visit(const B* b) const
{
    // use b here any way you want
}
avakar
I'm sorry if my example was misleading, but in the real version of the code, the inclusions are necessary. I've edited my question, so perhaps you could take another look at it.
Scott
+3  A: 

In this concrete example, remove the

#include "B.h"

from file J.h since it' s not needed there. If that doesn't work, we would need some more details about how J uses B...

EDIT

Since J only uses a pointer to B, the advice stays the same :-) :

Remove #include "B.h" from J.h and replace it by a forward declaration of B:

(J.h)
class B;

class J
{
  // ...
  virtual void visit(const B*) const = 0; // This will work with forward declaration
}

Also remove #include "K.h"from A.h.

IMPORTANT

Of course you need to add the needed includes in the respective CPP files:

(J.cpp)
#include "B.h"
// ...
// Go ahead with implementation of J
// ...

(Same for A.cpp, include K.h)

MartinStettner
I added some context to my question. I hope it helps explain what I'm trying to do.
Scott
+2  A: 

The main problem is that your header files include each other in a circular manner. A includes K which includes J which includes B which then includes A again... You should never require a situation where this happens. You should go back to the design drawing board and either cut some of the dependencies, or reorganize your classes entirely so that this circular dependency doesn't happen.

MahlerFive
Can you please take a look at my updated question? I added some more detail so perhaps you could help me restructure the code in some way.
Scott
+1  A: 

Circular inclusions do not work.

Try to keep inclusions to a strict minimum. If you do that, you'll either be fine altogether, or you'll discover problems in your design. In your case, i don't see anything wrong with your design.

When defining class K, you're only using a pointer to an object of type B. That does not require B to be defined (as in "include the header file"), only to be declared (forward declaration is fine). So, in your case, removing inclusion to header "B.h" replaced by "class B;" is sufficient. (the same goes for class J)

Benoît