Sign in to follow this  
Mizipzor

missing storage-class or type specifiers

Recommended Posts

I get these errors:
d:\Documents\CPP\Visual Studio Projects\TileSystem\Game.h(30) : error C2143: syntax error : missing ';' before '*'
d:\Documents\CPP\Visual Studio Projects\TileSystem\Game.h(30) : error C2501: 'Game::Console' : missing storage-class or type specifiers
d:\Documents\CPP\Visual Studio Projects\TileSystem\Game.h(30) : error C2501: 'Game::Console' : missing storage-class or type specifiers

On this line: Console* Console; // for the console Im trying to do a console and for some reason it seems not to recognice my console class. Console.h is included and the class is defined there, if it wasnt it should state other errors to, shouldnt it? What could be wrong?

Share this post


Link to post
Share on other sites

Console * Console;






Is fine in C++, as is


Console *Console;






And



Console* Console;







The only problem with doing this is that it hides the Console class for the rest of the scope in which the Console variable is defined. For example


class Console{...};

int main()
{
Console * Console;
Console * Console2;
}






Here the Console * Console2; line will give you an error, as it thinks Console is now a variable of type pointer, and interprets Console * Console2; as a multiplication, since Console2 has not been previously defined, you get an error.

There is a way around this, you can qualify the second Console definition with the class keyword, that will tell the compiler that Console refers to a class, and not a variable of type pointer.


class Console{...};

int main()
{
Console * Console;
class Console * Console2;
}






This now compiles. This is an example of using an 'elaborated type specifier'. You will need to post more code describing the problem.

Share this post


Link to post
Share on other sites
Yup, thanks, that solved it. But on the line above I have

FontSystem* Font; // for printing text on the screen

Which works fine, why not the console? I couldnt find anywhere in the project where I did make another Console class as you showed there.

Share this post


Link to post
Share on other sites
Quote:
Original post by Mizipzor
Yup, thanks, that solved it. But on the line above I have

FontSystem* Font; // for printing text on the screen

Which works fine, why not the console? I couldnt find anywhere in the project where I did make another Console class as you showed there.


In this case it is likely that the elaborated type specifier simply declared the Console class as existing somewhere in the project. This suggests that you have not actually included the Console header file in the .cpp file that the line of code Console * Console exists in. It is worth you double checking.

Share this post


Link to post
Share on other sites
Console.h is included.

Game.h (as you can see, the console is at the top and I got other class pointers working fine)

#ifndef _GAME
#define _GAME

#include <SDL.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>

#include "SdlHandle.h"
#include "Player.h"
#include "AsciiMap.h"
#include "Log.h"
#include "Defines.h"
#include "FontSystem.h"
#include "Console.h"

class Game {
protected:
// Singleton class
static Game* m_pInstance;
Game( void );

private:
SdlHandle* Sdl; // for the graphics
Player Player; // the player
AsciiMap AsciiMap; // the tiles represented by chars
Log Log; // for the logs
FontSystem* Font; // for printing text on the screen
class Console* Console; // for the Console

bool running; // is the game running

Uint8* keys; // to keep track of the keys
SDL_Event event; // event tracker (keys)

public:
// Singleton ---
static Game* GetInstance( void ) {
if ( !m_pInstance )
m_pInstance = new Game();
return m_pInstance;
};

// init stuff ---
bool InitVideo(); // init graphics

// main stuff ---
void RenderWorld(); // draws all the tiles
void MainLoop(); // the main loop

void RawCommand(std::string Command); // for recieving raw commands from the Console
};

#endif

Share this post


Link to post
Share on other sites
That explains it then. [smile]

The problem lies in how the C++ inclusion system works. When you hit the compile button, the preprocessor takes over to replace the preprocessor keywords in every .cpp file with something the compiler will understand. What you end up with after the preprocessor has finished is something called a 'translation unit'.

One of the simplest jobs the preprocessor does is copying and pasting code for you, which is why header files are useful, it simply replaces the #include directive with the code that is inside the file that is specified, for example.


//console.h

class console{};

//some.cpp
#include"console.h"

console instance;




Here the preprocessor would see the #include preproccessor symbol and replace it with the contents of the file specified, which is console.h. Leaving the some.cpp translation unit as.


//some.cpp translation unit
class console{};

console instance;



Things get trickier when you add inclusion guards into the equation (which are used to prevent things from being defined multiple times within a translation unit). The standard states as part of its so called "One defintion rule", that you cannot have a class defined twice within a translation unit, so your compiler will give you are error if you do. So if you were to include the console.h file twice in some.cpp.


//console.h

class console{};

//some.cpp
#include"console.h"
#include"console.h"

console instance;



The subsequent generated translation unit would be.


//some.cpp translation unit

class console{};
class console{};

console instance;



As you can see, the some.cpp translation unit has the console class defined twice, which breaks the C++ standards One Definition Rule, and so issues a compiler error. This is why you need header guards, which are at the heart of your problem.


//console.h
#ifndef _console
#define _console

class console{};

#endif

//some.cpp
#include"console.h"
#include"console.h"

console instance;



Here we can see header guards at work, again we have included the file twice, but the preprocessing symbols will prevent the console class from being included twice. How it does that depends on how the preprocessor is implemented, but it could simply have a table of names that have been defined with the #define directive. When it executes the first include directive, it reaches the first #ifndef and checks to see if it has _console in its table, it doesn't, so it carries on. When it reaches its second include directive, it again reaches an #ifndef _console, so it checks to see if _console has been defined in its table, it has, so it does not carry on, and the code is not included. This leaves us with a translation unit like


//some.cpp translation unit

class console{};

console instance;




Now onto the topic in question, circular inclusions


//console.h
#ifndef _console
#define _console

#include"game.h"

class console
{
game* pgame_;
};

#endif

//game.h
#ifndef _game
#define _game

#include"console.h"

class game
{
console* pconsole_;
};

#endif


//some.cpp
#include"game.h"

console console;




Here we have introduced a circular inclusion, game.h includes console.h which includes game.h which includes console.h which... [smile]

The header guards have come to the rescue again though, and the preprocessor will not get stuck in an infinite loop. This is a slightly more involved proprocessor program, but uses the same simple rules as before.

First the preprocessor is executed on some.cpp, it sees the #include directive and so goes about copying the code from game.h into some.cpp.

It reaches the first #ifndef _game, it checks its table and sees it has not been defined yet, and so carries on.

It then reaches the #include"console.h" directive in game.h, and goes about copying that into some.cpp.

It reaches the first #ifndef _console directive, it checks its table and sees it has not been defined yet, and so carries on.

It then reaches the #include"game.h" directive in console.h, and goes about copying the game.h code into some.cpp

it reaches the #ifndef _game directive for the second time, checks its table and sees it is defined!

This stops the infinite loop, but leaves us with a slightly error prone translation unit looking like:


//some.cpp translation unit

class console
{
game* pgame_;
};

class game
{
console* pconsole_;
};

console console;




Now when the compiler is finally executed on this translation unit, it runs into a problem almost immedietly. It reaches the line game* pgame_; and asks itself what game is, is it a variable or a type? It has noway of knowing as the circular depedancy has meant that the game class has not yet been declared or defined (it is defined later in the translation unit as we can see).

The simple fix for this that we stumbled on is to use an elaborated type specifier to predeclare the type game, so the compiler knows what it is and can continue. This is done by simply prefixing game* pgame_; with the keyword class in this case. This would leave the translation unit as


//some.cpp translation unit

class console
{
class game* pgame_;
};

class game
{
console* pconsole_;
};

console console;




Now the compiler knows that game* is a type and not a variable, and so it can continue compiling. It is also worth doing the same to the console* pconsole_; line, as this would also give the same error if we had included the console.h file first in some.cpp instead of the game.h header file.

Congratulations to anyone who made it this far. [smile]

Share this post


Link to post
Share on other sites
Hehe I had to read that twice to understand :P but know I know why... also how a compiler works... hehe... thanks for taking the time to write that, two thumbs up and a rating for you. :)

Share this post


Link to post
Share on other sites
You can also solve the problem with a forward declaration, i.e. arranging such that the translation unit looks like


class game;

class console
{
game* pgame_;
};

class game
{
console* pconsole_;
};

console c;

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this