views:

109

answers:

4

Hello,

The following code:

foo.h

#include "bar.h"
class foo{ 
public:
   enum my_enum_type { ONE, TWO, THREE }; 
   foo(); 
   ~foo() {} 
};

foo.cpp

foo::foo()
{
   int i = bar::MY_DEFINE;
}

bar.h

#include "foo.h"
class bar{
public:
   static const int MY_DEFINE = 10;
   foo::my_enum_type var;
   bar() {};
   ~bar() {};
};

Makes g++ compiler complain about my_enum_type "does not name a type". Why ? All headers have multiple inclusion defines (not shown here for clarity).

Thanks

+3  A: 

Problems:

  • no multiple inclusion protections
  • cyclic inclusion
  • type use before its declaration caused by cyclic inclusion

Your foo.h being processed by C preprocessor looks like infinite empty string sequence.

With multiple inclusion protection foo.h is preprocessed to:

> cpp foo.h
class bar{ // preprocessed from #include "bar.h"
public:
   static const int MY_DEFINE = 10;
   foo::my_enum_type var;
   bar() {};
   ~bar() {};
};
// end of #include "bar.h"
class foo{ 
public:
   enum my_enum_type { ONE, TWO, THREE }; 
   foo(); 
   ~foo() {} 
};

This obviously is not a valid C++ code - foo is used in bar body without previous declaration. C++, unlike Java, requires types to be declared before use.

  • Use multiple inclusion protection. Depending on your platform those might be #ifndef macros or #pragma once directives
  • Remove bar.h inclusion from foo.h.
  • Place forward declarations where needed (in your case bar might be forward declared in foo.h, your example doesn't reveal neccesity of this though).
  • Move as much implementation as possible to *.cpp files.

If the situation can't be resolved with these recommendations, use PIMPL idiom.

In short - just remove #include "bar.h" directive from foo.h

Basilevs
My headers have multiple inclusion protection. If I remove bar.h from foo.h, foo.cpp can't use bar::MY_DEFINE any more ('bar' has not been declared).
Rémy DAVID
Wrong. foo.cpp still can use bar if you include bar.h in foo.cpp itself.
Basilevs
Yes, that's what I didn't know ;)
Rémy DAVID
A: 
foo()
{
   int i = bar::MY_DEFINE;
}

should be

foo::foo()
{
   //...
}

Also note that

static const int MY_DEFINE = 10;

is still a declaration, although it has an initializer. In bar.cpp you should have the following line

const int bar::MY_DEFINE;

Also you can't include bar.h from foo.h and foo.h from bar.h... that's physically impossible :)

Armen Tsirunyan
A: 

You must remove the cyclic dependency so you need to consider foo.cpp and foo.h as different units for this purpose.

  • bar class definition must see foo::my_enum_type so probably bar.h including foo.h is a necessity.

  • foo class definition does not use any of bar, so foo.h does not need to include bar.h

  • foo.cpp does need to see bar for MY_DEFINE so foo.cpp should include bar.h. That would actually also bring in foo.h automatically but you may wish to include it anyway in foo.cpp, just in case you remove the dependency later.

Presumably your headers have multiple include guards.

CashCow
I didn't know you had to consider .h and .cpp dependency separately. I thought including more than one .h in a .cpp was bad design. I was probably wrong, your answer solve my problem. Thanks :)
Rémy DAVID
A: 

Either you write "typedef" in the enum declaration

typedef enum my_enum_type { ONE, TWO, THREE };

or you repeat "enum" in every variable declaration.

enum foo::my_enum_type var;
Sebastian N.
-1 for solving C problem instead of C++ one
Basilevs
Hm, I always thought it were that way in C++, too, but I tried and indeed you only have to do it in old C.
Sebastian N.