Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Strange C++ error: ISO C++ forbids declaration of 'player' with no type

This thread is locked; no one can reply to it. rss feed Print
 1   2   3 
Strange C++ error: ISO C++ forbids declaration of 'player' with no type
edfredcoder
Member #7,985
November 2006
avatar

Hi, I am having a strange problem compiling a program I am writing. I have a "game" class, defined in game.h like this:

1#ifndef GAME_H
2#define GAME_H
3 
4#include "player.h"
5#include "planet.h"
6#include <string>
7#include <vector>
8 
9class game {
10 private:
11 std::vector<player *> players;
12 
13 /*Irrelivant code now shown*/
14 
15 public:
16 game();
17 void update();
18 void draw();
19 void add_player(std::string name, int ptype);
20 
21 /*Irrelivant code not shown*/
22 
23 player * get_player(std::string name);
24};
25 
26#endif

The player class needs to know about which game it belongs to. Here is player.h:

1#ifndef PLAYER_H
2#define PLAYER_H
3 
4#include "unit.h"
5#include "game.h"
6#include <vector>
7#include <string>
8 
9class player {
10 private:
11/*Irrelivant code not shown*/
12 std::string name;
13 game *owner;
14 
15 public:
16 player(std::string name, int ptype, game *g);
17 
18//More code not shown
19 
20 game * get_owner();
21};
22 
23#endif

I get the error:

In file included from game.h:4,
                 from main.cpp:9:
player.h:20: error: ISO C++ forbids declaration of 'game' with no type
player.h:20: error: expected ';' before '*' token
player.h:23: error: 'game' has not been declared
player.h:31: error: ISO C++ forbids declaration of 'game' with no type
player.h:31: error: expected ';' before '*' token

??? I don't understand what the problem is. player.h includes game.h, so class game should be defined. Yet gcc says it isn't.

Does anyone know of what might be wrong? I'm more used to C, not C++.

Vanneto
Member #8,643
May 2007

Try including game.h in player.c.

In capitalist America bank robs you.

edfredcoder
Member #7,985
November 2006
avatar

Nope, didn't work :(

EDIT: Just solved it. I had to put

class game;

at the top of player.h

Nicol Bolas
Member #9,238
November 2007

It is important to understand what the problem here is.

The actual problem is this. game.h includes player.h, and player.h includes game.h.

So, when the compiler enters game.h, the compiler will hit the #include and go to player.h. The compiler will then hit the #include "game.h" in player.h. However, since game.h has its include guards up, it will immediately exit, without having included the vital symbols into player.h.

Adding a forward declaration of the class game solved the problem because the class player never got to see the declaration of game.

Important Object Lesson: never include anything in a header file. Especially if that header can cause a circular reference. Always rely on forward declarations, and use Pimpl to hide uses of objects that forward declarations work with.

Jimage
Member #4,205
January 2004

Nicol Bolas said:

Important Object Lesson: never include anything in a header file.

On that note, though in a C context: I use a standard set of file reading functions throughout my programs, which I've just started converting into a split .h and .c structure.

fileread.h

int read_byte(FILE * fp);
int read_word(FILE * fp);
int read_dword(FILE * fp);
int write_byte(int byte, FILE * fp);
int write_word(int word, FILE * fp);

Without #including <stdio.h>, which is currently included in fileread.c, it won't compile due to the FILE pointers. Can anyone see a way of doing this without having to use an #include? Or is it best just to include standard C libraries in the headers for the sake of consistency, similar to Allegro with its base.h?

aj5555
Member #9,033
September 2007

what is wrong with including headers ?

just do it right, instead of trying to be clever.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Rash
Member #2,374
May 2002
avatar

Nicol Bolas said:

Important Object Lesson: never include anything in a header file.

Can you give anti-cookies for retarded statements like this?

Vanneto
Member #8,643
May 2007

No, but you can sig them so they everyone can see their retardedness.. :P

In capitalist America bank robs you.

Nicol Bolas
Member #9,238
November 2007

Quote:

Can anyone see a way of doing this without having to use an #include? Or is it best just to include standard C libraries in the headers for the sake of consistency, similar to Allegro with its base.h?

It may be that the lesson was too strict.

You can include a header if you are certain that it will not subsequently include this file. So obviously standard library files are all perfectly fine to include.

The problem comes from willy-nilly including headers just to get a symbol defined. This is the quickest way to back oneself into such a corner. And forward declarations won't buy your way out of every problem.

It should also be pointed out that including headers inside headers increases compile times. You should try to write headers such that they do not include features that are important only for the implementation of the functions or classes. So, if a class uses a std::vector internally, which you cannot forward declare due to being templated and almost certainly not being used by reference, you should hide that implementation detail inside another class that you can forward declare.

So, rather than:

#include <vector>

class MyClass
{
public:

private:
  std::vector<SomeType> m_anArray;
};

You have:

struct MyClassData;

class MyClass
{
public:

private:
  MyClassData *m_pData;
};

You define MyClassData in your .cpp file. You allocate m_pData in the constructor, and delete it in the destructor.

As someone who has worked on large projects with over a thousand files, both on projects that operate like this and those that don't, trust me; you will be thankful. Especially for things like std::vector and std::map, which typically have gigantic implementations.

In your case, you should #include<stdio.h> in your header, but you should also make sure that no other header needs to include this specific header file.

Quote:

what is wrong with including headers ?

My apologies; I thought I was being clear. The problem presented in this thread happened because of including headers inside of other headers, rather than using forward declarations. Had edfredcoder been writing his code with respect towards not including header files inside header files, then this would have been averted.

SiegeLord
Member #7,827
October 2006
avatar

Hmm, how does that work for inheritance?

Say I have three files:

file1.h

struct BaseData;

struct BaseStruct
{
    BaseData* data;
    void DoStuff();
};

file1.cpp

#include "file1.h"

struct BaseData
{
    int z, d;
};

void BaseStruct::DoStuff()
{
    data->z += data->d;
}

file2.cpp

#include "file1.h"

struct NewStruct : BaseStruct
{
    void DoStuff();  
};

void NewStruct::DoStuff()
{
    data->d += data->z;  
}

Naturally that does not compile, since the child struct has absolutely no idea what the data struct is supposed to contain. How would this problem be solved?

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Nicol Bolas
Member #9,238
November 2007

If you are deriving a class from another class, then that means that you intend for them to do so. If you do not intend for them to do so, locking the actual data members up in a struct that they do not have access to is a pretty good way of saying, "Don't derive from this." This is even more true in light of the fact that C++ has no concept of "final" the way that Java does.

If you do intend to allow derivation, then all you need to do is this:

1//BaseClass.h
2class BaseClassImpl; //Not mere data anymore.
3 
4class BaseClass
5{
6public:
7 BaseClass();
8 virtual ~BaseClass(); //ALL CLASSES INTENDED FOR DERIVATION MUST DO THIS!!!!
9 
10 virtual void SomeInterfaceFunction();
11 
12protected:
13 BaseClass(BaseClassImpl *pImpl);
14 
15 BaseClassData *m_pImpl;
16};
17 
18//BaseClass.cpp
19#include BaseClassImpl.h
20 
21BaseClass::BaseClass()
22 : m_pImpl(new BaseClassImpl())
23{}
24 
25BaseClass::BaseClass(BaseClassImpl *pImpl)
26 : m_pImpl(pImpl)
27{}
28 
29BaseClass::~BaseClass()
30{delete pImpl;}

Note that, in BaseClass.cpp, we do not define BaseClassImpl. Instead, it lives in its own header, where it includes all manor of things:

1//BaseClassImpl.h
2 
3#include <vector>
4#include <boost/format.hpp>
5#include <map>
6 
7class BaseClassImpl
8{
9public:
10 BaseClassImpl();
11 virtual ~BaseClassImpl()
12 
13 //Other interface functions.
14 
15protected:
16 typedef std::map<int, boost::format> MapType;
17 typedef std::vector<MapType> StrangeArrayType;
18 
19 StrangeArrayType m_strangeArrayThingy;
20};
21 
22//BaseClassImpl.cpp
23BaseClassImpl::BaseClassImpl() {}
24BaseClassImpl::~BaseClassImpl() {}
25 
26//Other interface functions.

Even though we have put BaseClassImpl in its own pair of files, you still keep compile times down. This is because the only files that actually need to include BaseClassImpl are .cpp files that actually derive from BaseClass. The mere use of BaseClass, which is 90% of the time you need to include it, will not include the junk that BaseClass needs in order to function.

So deriving a new class from BaseClass requires deriving a new BaseClassImpl too. Notice that BaseClass has a protected constructor that can take a BaseClassImpl object to use directly. This would be a class that your derived class constructor uses, when it creates the object.

You do not need to derive a new BaseClassImpl; you could simply let the base class have its Impl data, and your derived class would have Impl data for your own needs. The fact that the data pointer is protected allows you to access it from your derived class.

HoHo
Member #4,534
April 2004
avatar

I know that QT and KDE use similar system that Nicol describes.

__________
In theory, there is no difference between theory and practice. But, in practice, there is - Jan L.A. van de Snepscheut
MMORPG's...Many Men Online Role Playing Girls - Radagar
"Is Java REALLY slower? Does STL really bloat your exes? Find out with your friendly host, HoHo, and his benchmarking machine!" - Jakub Wasilewski

Tobias Dammers
Member #2,604
August 2002
avatar

If you only ever use a type by pointer or by reference, then you should use the short declaration (class game;). If you use actual objects of the type in the file, you need to include the header.
Example:
-- foo.h --

#ifndef FOO_H
#define FOO_H

class Bar; // no need to #include, because we only use pointers/references

class Foo {
  public:
    void doSomeStuff(const Bar& bar);
};

#endif

-- bar.h --

#ifndef BAR_H
#define BAR_H

#include "foo.h"

class Bar {
  protected:
    Foo myFoo;
};

#endif

---
Me make music: Triofobie
---
"We need Tobias and his awesome trombone, too." - Johan Halmén

SiegeLord
Member #7,827
October 2006
avatar

Nicol Bolas said:

...

Well, this is just my opinion, but that is the ugliest hack I've ever seen. I simply fail to see how doing that mess is any better than this:

//header1
struct A
{
    virtual ~A();
    virtual void SomeFn();
};
//header2
#include "header1"
struct B : A
{
    virtual ~B();
    virtual void SomeFn();
}

It is short and easy to understand. I've been yelled at in this forum about optimizing too soon, namely the order was supposed to go as follows:

1. Make your code easy to modify and read
2. Make your code fast at run time

To that I'd add a third point:

3. Make your code compile fast

Making your code compile fast is the very last thing that a project should care about, i.e. it should only care about it if and when it becomes a problem. If you are changing your headers often enough for there to be a need to recompile often, then that is a problem of planning, not a problem of design.

If it was a problem then I'd just do what Tobias Dammers suggested, and convert every single mention of an object to a pointer and remove all header includes that way. That is clean and easy to understand, compared to the disaster of hidden implementations.

Lastly,

Nicol Bolas said:

"Don't derive from this."

I really hate when library coders do that. I, as a user of a library, want to be able to derive a class from whatever I set my heart to. That is why I never use the 'class', 'public', 'private' or 'protected' keywords anymore. All they do is limit my creatitivy and encourage ugly hacks (Get(), Set() monstrosities and 'friend' class hacks). If you don't want anyone to change your parameters, write a comment above the parameter. That's what comments are for, they tell you how to use the code.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Nicol Bolas
Member #9,238
November 2007

Quote:

If you only ever use a type by pointer or by reference, then you should use the short declaration

There are many times when this does not work. You cannot forward declare template classes for example. So any STL class, or many of Boost's classes, cannot be used this way.

Quote:

it should only care about it if and when it becomes a problem.

There is a problem with this logic: it only becomes a problem when a project has a number of code files well into the hundreds.

The process of converting from non-Pimpl to Pimpl coding style is not an automatable task. So you cannot write a simple script to read through files and convert them. You are in effect saying that at some point, you expect all development to stop and everyone to go through and perform major refactoring on virtually every system.

This is simply not reasonable. I've been on such projects, and it would have been virtually impossible to do this on those files. At the very least, it would have taken a long time.

The reason that performance optimizations come last is because they are almost always localized fixes. You change how a small set of functions or classes work. Converting a codebase to Pimpl style requires converting the entire thing to get the benefits of it.

Quote:

I, as a user of a library, want to be able to derive a class from whatever I set my heart to.

If I may ask, why?

C++ has proven time and again that if you give someone the opportunity to do the wrong thing, then they will do so and do it often. If it is wrong to derive from a class, if doing so will never serve a valuable purpose, then locking the class off from derivation is perfectly reasonable.

If the "not-base" class does not have any actual virtual methods on it, deriving from it is of little value. You cannot change how code that expects the "not-base" class will behave just from deriving. This can only happen with virtual methods, and virtual methods are defined by the creator of the library, not the user.

So I do not know what it is that you expect to get out of being able to derive classes that are not built for derivation.

Quote:

If you don't want anyone to change your parameters, write a comment above the parameter. That's what comments are for, they tell you how to use the code.

Unread comments are of no value. Counting on programmers to read your comments is, to put it mildly, unwise. Indeed, it might be wiser to count on programmers not reading your comments.

The set of code that will not be used incorrectly is the set of code that cannot be used incorrectly. This is one of the reasons why exceptions are preferred over error codes; they cannot be ignored. Thus, while the program may crash from the uncaught exception, it will crash exactly where the error happened, and not miles away with some arbitrary corrupted data.

Self-documenting code is the most effective way of keeping down user error. It would not be unreasonable to say that it is the only way to do so.

SiegeLord
Member #7,827
October 2006
avatar

Nicol Bolas said:

There are many times when this does not work. You cannot forward declare template classes for example. So any STL class, or many of Boost's classes, cannot be used this way.

Good point, still I'd rather do this than resort to Pimpl:

class vector_int : public std::vector<int> {};

Which can be easily forward declared.

Re your comments about derivation: The most common feature I add to classes I derive from library classes is ability to alter some parameters that the library coders were too short sited to allow me to alter (in every case I've done it this was perfectly reasonable, i.e. it wasn't a hack or anything). I'd rather not modify the library's source code for one little function, when I can simply derive a new class and use it instead. Naturally I would not use it for any virtual business if I can't.

Re your comments about comments: If a programmer chooses not to read the comments, then it is his own fault that his program will have hard-to-find bugs. Most of us do not live in Soviet Russia, so we read the fine manual... the fine manual does not read us.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Nicol Bolas
Member #9,238
November 2007

Quote:

If a programmer chooses not to read the comments, then it is his own fault that his program will have hard-to-find bugs. Most of us do not live in Soviet Russia, so we read the fine manual... the fine manual does not read us.

While I understand your point, I just don't find it compelling.

Take anti-lock breaks (ABL) on cars for example. ABL doesn't do anything that the driver couldn't do. The driver can recognize that the car is losing traction and pump the breaks. Most drivers learned to do this at some point.

This does not mean that ABL is a bad idea. It's automatic; no thought is required. It turns on when needed. And it is more sensitive to traction loss than any human can possibly be, and it can pump the breaks faster than a human foot can move.

The fact that a programmer can write their code around the minefield that is your library doesn't mean that they should have to. Part of good API design is making it impossible for the person to do the wrong thing. It seems poor service to the end-user to force them to check every function/object's use in the manual/code comments to see if it violates some requirement. Certainly, when a programmer is up at 6AM trying to resolve some issue for a build, it would be best that all stupid mistakes were covered.

And of course, this assumes that the comment is well-written. I have seen several warning comments that might or might not apply to my particular use, depending on interpretation.

In fact, I was just writing some matrix functions today when I came upon the following conundrum. Matrix inversion can be impossible for certain matrices. The test for this is fairly straightforward: if the determinant is zero, then the matrix is not invertible. Now tell me, which of the following is the best way to handle this?

Matrix4x4 someMat;
if(someMat.Determinant())
{
  Matrix4x4 inverse = Inverse(someMat);
  //do stuff with inverse.
}

or

Matrix4x4 someMat;
if(someMat.IsInvertable())
{
  Matrix4x4 inverse = Inverse(someMat);
  //do stuff with inverse.
}

or

Matrix4x4 someMat;
boost::optional<Matrix4x4> inverse = Inverse(someMat);
if(inverse)
{
  Matrix4x4 theInv = inverse.get();
  //do stuff with theInv.
}

Code segment 1 is obtuse. Without significant knowledge of matrix math, which a surprising number of people using matrices lack, it makes no sense to check the determinant of the matrix before inverting it. Since it seems like a strange operation, users will likely decide that it is something special about this particular usage, and not do it in their code.

Code segment 2 is better, since there is an explicit function for inverse detection. The name of this function suggests its purpose. However it does not make you test your matrix for inversion before inverting it. It should also be pointed out that the inverse function itself will compute the determinant as part of its computation, so it happens twice needlessly.

Code segment 3 is, to my mind, the best. The return value is optionally returned. This forces the user to check that there is a value before using it. The optional::get function will not do nice things if it doesn't actually contain a value. In short, you can't possibly screw up, even if you're awake at 6AM, trying to get a build out to the publisher before a deadline. It is also immediately obvious to anyone using the function that it can fail, which helps reinforce the notion that it can fail.

SiegeLord
Member #7,827
October 2006
avatar

Well, I guess it just goes to show how our outlooks on code structure are different since I prefer the first method. I don't want to have to waste CPU cycles checking to see if every matrix is invertable (say I know for sure that it is) with every call of invert. So I'd rather have the check and the operation as separate functions. I certainly would't mind a InvertSafe conglomerate that would do what your third method does, but not providing a checkless Invert function just adds unnecessary limits to one's code.

Basically I am only fine with rewarding mediocrity as long as it does not interfere with the ability of someone who knows what they are doing to get the job done.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

bamccaig
Member #7,536
July 2006
avatar

I think I prefer...

Matrix::Matrix4x4 oMatrix4x4;
Matrix::Matrix4x4 oInverseMatrix4x4;

if(oMatrix4x4.determinant() != 0)
{
    oInverseMatrix4x4 = oMatrix4x4.inverse();
    std::cout << "The matrix was inverted." << std::endl;
    // etc...
}
else
{
    std::cout << "The matrix cannot be inverted because the determinant is zero." << std::endl;
    // etc...
}

// etc...

Or...

Matrix::Matrix4x4 oMatrix4x4;
Matrix::Matrix4x4 oInverseMatrix4x4;

try
{
    oInverseMatrix4x4 = oMatrix4x4.inverse();
    std::cout << "The matrix was inverted." << std::endl;
    // etc...
}
catch(Matrix::DeterminantIsZeroException ex)
{
    std::cout << "The matrix cannot be inverted because the determinant is zero." << std::endl;
    // etc...
}

// etc...

Note: this is just an example. I haven't done enough exception handling [in C/C++] to know if this is valid code.

I've heard that try...catch can be extremely expensive, however, so that may be something to consider.

ImLeftFooted
Member #3,935
October 2003
avatar

Nicol said:

.. Silly code ..

You are destroying C++ for the sake of compile time! Its just silly.

If its taking too long to include vector or map, try pre-compiling those headers.

Nicol Bolas
Member #9,238
November 2007

Quote:

I don't want to have to waste CPU cycles checking to see if every matrix is invertable (say I know for sure that it is) with every call of invert.

I seem to recall you yourself mentioning something about not optimizing too soon? ;D

Quote:

You are destroying C++ for the sake of compile time!

Pimpl is a fairly common C/C++ programming idiom; using it is not "destroying C++". And when touching a certain header file that is commonly included can cause a 1 hour recompile of a project (I have born witness to this), the "sake of compile time" clearly becomes an issue of significant importance.

SiegeLord
Member #7,827
October 2006
avatar

I mentioned not optimizing too soon as being something people yell at me about. I tend to optimize so soon that my code as written is pre-optimized... if only it were easy to read. I would certainly be a Pimpl kind of guy, but I just like the alternatives better.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

HoHo
Member #4,534
April 2004
avatar

Quote:

You are destroying C++ for the sake of compile time! Its just silly.

Tell that to KDE people :P

__________
In theory, there is no difference between theory and practice. But, in practice, there is - Jan L.A. van de Snepscheut
MMORPG's...Many Men Online Role Playing Girls - Radagar
"Is Java REALLY slower? Does STL really bloat your exes? Find out with your friendly host, HoHo, and his benchmarking machine!" - Jakub Wasilewski

tobing
Member #5,213
November 2004
avatar

Our project at work takes several hours to compile, up to 17h on the slowest platform. It's about 2 million lines of codes, with enough templates to let compilers go hickup. Without extensive usage of pimpl patterns and the like, as well as using precompiled stuff wherever possible, the whole thing could not be handled at all.

For small projects that only have some dozen files all these considerations are probably not important or necessary at all. As soon as your projects grow to include hundreds of files, or i you join a team that works on a project of that size, these consideration ARE important, so it may be a good idea to get used to think about compile times, even if that's not an immediate problem.

 1   2   3 


Go to: