Advertisement

Circular inclusion problem

Started by September 21, 2017 04:42 PM
14 comments, last by Yxjmir 7 years, 5 months ago

I think I am having the problem mentioned in the title, but I can't be sure of it as well...

Got 3 classes, GameMode, Entity, Paddle. 

Paddle Inherit from Entity, and both Paddle and Entity need to #include "GameMode.h" in order to pass down the constructor a GameMode* and store it inside of every Entity.

I pretty much forward declared everything inside everything else but stuff just won't compile, the error I am getting is C2504 "Entity: base class undefined" .

I'll paste below the .h and .cpp for the Entity class, and only .h for the other two. Can someone tell me what am I doing wrong? :/

Entity.h


#pragma once
#include "SDL2\SDL.h"
#include "GameMode.h"
#include "Utility.h"
using namespace util;

class GameMode;
enum class PivotMode: Uint8 {CENTER,TOP_LEFT};

class Entity
{
protected://variables
	float XCenter;
	float YCenter;
	SDL_Rect CollisionBox;
	SDL_Texture* Sprite;
	GameMode* Game;
	SDL_Renderer* Renderer;

public://constructors
	Entity(GameMode* gameRef, PivotMode inputMode, int x, int y, std::string path);
	virtual ~Entity();
	Entity(const Entity&) = delete;
	Entity& operator=(const Entity&) = delete;
	Entity(Entity&&) = delete;
	Entity& operator=(Entity&&) = delete;

public://methods
	virtual void Update(float deltaTime) = 0;
	virtual void Draw(float interpolation) = 0;

private://methods
SDL_Texture* RequestTexture(std::string path)const;
void SetCollisionBox(int x, int y, PivotMode InputMode);
};

Entity.cpp


#include "Entity.h"

Entity::Entity(GameMode* gameRef, PivotMode inputMode, int x, int y, std::string path)
	:Game{ gameRef }, XCenter{ static_cast<float>(x) }, YCenter{ static_cast<float>(y) }
{
	if (Game) { Renderer = Game->GetRenderer(); }
	Sprite = RequestTexture(path); 

	if (Sprite)
	{ SetCollisionBox(x, y, inputMode); }
}

Entity::~Entity()
{
}

SDL_Texture* Entity::RequestTexture(std::string path)const
{
	if (Game->IsRunning())
	{
		return Game->RequestTexture(path);
	}
	return nullptr;
}

void Entity::SetCollisionBox(int x, int y, PivotMode InputMode)
{
	SDL_QueryTexture(Sprite, nullptr, nullptr, &CollisionBox.w, &CollisionBox.h);

	switch (InputMode)
	{
		case PivotMode::CENTER:
		{
			CollisionBox.x = x - CollisionBox.w / 2;
			CollisionBox.y = y - CollisionBox.h / 2;
			XCenter = static_cast<float>(x);
			YCenter = static_cast<float>(y);
		}break;
		case PivotMode::TOP_LEFT:
		{
			CollisionBox.x = x;
			CollisionBox.y = y;
			XCenter = static_cast<float>(x) + CollisionBox.w / 2;
			YCenter = static_cast<float>(y) + CollisionBox.h / 2;
		}break;
	}
}

Paddle.h


#pragma once
#include "Entity.h"
#include "GameMode.h"

class GameMode;
enum class PivotMode:Uint8;

class Paddle : public Entity
{
public://methods
	Paddle(GameMode* gameRef, PivotMode inputMode, int x, int y, std::string path);
	~Paddle();

	virtual void Update(float deltaTime);
	virtual void Draw(float interpolation);
};

GameMode.h


#pragma once
#include "SDL2\SDL.h"
#include <string>
#include <vector>
#include <memory>
#include "App.h"
#include "Entity.h"
#include "Paddle.h"

class Entity;
class Paddle;

class GameMode
{
	friend class App;
private://variables
	bool Running;
	SDL_Window* Window;
	SDL_Renderer* Renderer;
	App* AppRef;

public://constructors
	GameMode(SDL_Window* Window, SDL_Renderer* Renderer, App* App);
	~GameMode();

	GameMode(const GameMode&) = delete;
	GameMode& operator=(const GameMode&) = delete;	
	GameMode(GameMode&&) = delete;
	GameMode& operator=(GameMode&&) = delete;

public://methods
	SDL_Texture* RequestTexture(std::string path)const;
	bool IsRunning()const;
	SDL_Renderer* GetRenderer()const;

private://methods
	void Update(float deltaTime);
	void Draw(float interpolation);
};

 

4 minutes ago, MarcusAseth said:

Paddle Inherit from Entity, and both Paddle and Entity need to #include "GameMode.h" in order to pass down the constructor a GameMode* and store it inside of every Entity.

No they don't. You can forward declare GameMode if all you need is to refer to it by pointer.

 

Then just include GameMode.h in the CPP files.

Hello to all my stalkers.

Advertisement
7 minutes ago, MarcusAseth said:

Paddle Inherit from Entity, and both Paddle and Entity need to #include "GameMode.h" in order to pass down the constructor a GameMode* and store it inside of every Entity.

 

No they don't; the forward declaration you have of GameMode is sufficient to declare pointers to GameMode. You only need to include the header if you need anything that requires the full definition of GameMode, which you don't unless you actually try to call a method from it, take its size, et cetera. Generally you can defer the inclusion of GameMode.h to the .cpp in this case.

What you are seeing here is that during the compilation of Entity.cpp, you include Entity.h (which, remember, is basically just pasting the content of Entity.h into Entity.cpp -- the compiler is only compiling the TU resulting from preprocessing Entity.cpp). Entity.h includes SDL stuff which is irrelevant, and then includes GameMode.h. GameMode.h. GameMode.h includes a bunch of crap, and then Entity.h (whcih is skipped due to the pragma) and then Paddle.h. Paddle.h doesn't do anything with the include of Entity.h due to the aforementioned pragma.

Thus the compiler is seeing, from top-to-bottom (with irrelevant stuff removed):

  • the content of paddle.h
  • the content of gamemode.h
  • the content of entity.h
  • the content of entity.cpp

So the compiler is seeing you define Paddle as a subclass of Entity which hasn't actually been defined yet. You can verify this by having the compiler emit the preprocessed source for Entity.cpp, if you like (although it will be a lot of stuff to wade through).

Since Entity.h doesn't need GameMode.h, remove it. That will break the above chain (although you may have this problem elsewhere and may need to perform a similar analysis and correction elsewhere).

 

Thanks for the answers guys, that totally fixed it!

Also double thanks for the explanation @jpetrie which clarified things a bit, though even with that I can see that I still can't fully wrap my head around it, so I'll try and search for more explanations(but with images, I always need images to easily understand :D )

Make a new text and copy Entity.cpp into it. Then, as long as there is any #include "YourFile.h" left in the text:

  • Open YourFile.h.
  • If YourFile.h contains #pragma once and you've already seen YourFile.h, stop.
  • Otherwise, copy all of YourFile.h.
  • Paste it into the text file, replacing the original #include. 

You can skip the SDL and C++ library includes for sanity. Once you've done that for every #include of your files, read the resulting source code. This is what the compiler is seeing, and it should be clear why this is an issue then.

Ok, maybe I am getting it, so the problem is not that Entity.h is not included, the problem is that it appears below the content of Paddle.h

Does this means I could have solved the problem above also by forward declaring class Entity; inside of Paddle.h? :S

 

Advertisement
26 minutes ago, MarcusAseth said:

Does this means I could have solved the problem above also by forward declaring class Entity; inside of Paddle.h? :S

 

Not in this case, because Paddle.h declares Paddle which is a subclass of Entity, and inheriting from some type requires the full definition of that type. A forward-declaration won't suffice.

I see, this will be useful to know for sure in the future :)

 

You can avoid passing GameMode* gameRef as a parameter to the entity, and paddle, etc. by using a class that gets the data from its list of entities, and its reference to GameMode, then does whatever you have entity doing with it.

Then you shouldn't have to include GameMode.h at all in Entity.h or Paddle.h or their .cpp files. Also, it doesn't look like you need to include Entity.h or Paddle.h in GameMode.h or even declare the classes, just include them in GameMode.cpp if needed, which you probably shouldn't need to.

If GameMode required a pointer to Entity or Paddle this would create a problem with them having references to each other, using shared_ptr or not handling them properly as they are now with a reference counting system, neither one would be released/deleted when the game closed resulting in a memory leak.

23 minutes ago, Yxjmir said:

You can avoid passing GameMode* gameRef as a parameter to the entity, and paddle, etc. by using a class that gets the data from its list of entities, and its reference to GameMode, then does whatever you have entity doing with it.

I don't understand this x_x

Quote

If GameMode required a pointer to Entity or Paddle this would create a problem with them having references to each other, using shared_ptr or not handling them properly as they are now with a reference counting system, neither one would be released/deleted when the game closed resulting in a memory leak.

and this as well x_x

This topic is closed to new replies.

Advertisement