Search code examples
c++inheritanceincludecompositioncircular-dependency

Circular Inclusion and Inheritence with Forward Declarations Leads to C2504 base class undefined


I am getting a C2504 compilation error in PlayerController.h saying that my base class (Updateable) is undefined. I have searched for several hours for a solution to a circular inclusion with inheritance problem and their solutions are to remove the circular inclusions and jsut use a forward declaration. As far as I understand, this works if no methods from the forward declared class are called. However, in my program my Updateables class calls a method on its member inherited gameObject object and the GameObjects also call methods on their member Updateables. Because of this, Updateables need to include GameObject.h and GameObjects need to include Updateables.h. This leads to a C2504 in PlayerController.h saying that the base class Updateable can not be found.

Here are my relevant classes:

Component.h

#pragma once
#include "Vector3.h"

class GameObject;

class Component {
public:
    GameObject* gameObject = nullptr;

    Component();
};

Component.cpp

#include "Component.h"

Component::Component() {}

Updateable.h

#pragma once

#include "Component.h"
#include "GameObject.h"

class GameObject;

class Updateable : public Component {

public:
    ~Updateable();
    virtual void update() = 0;
};

Updateable.cpp

#include "Updateable.h"

Updateable::~Updateable() { 

    if (gameObject) {
        gameObject->removeUpdateable(this);
    }
}

GameObject.h

#pragma once

#include "Updateable.h"
#include "GameManager.h"

class Updateable;

class GameObject {

public:

    GameObject();
    ~GameObject();

    void runUpdateables();
    void addUpdateable(Updateable* updateable);
    void removeUpdateable(Updateable* updateable);

private:
     vector<Updateable*> updateables;
};

GameObject.cpp

#include "GameObject.h"

GameObject::GameObject() {

    updateables = vector<Updateable*>();
    GameManager::addGameObject(this);
}

GameObject::~GameObject() {

    GameManager::removeGameObject(this);
}

void GameObject::runUpdateables() {

    for (unsigned int i = 0; i < updateables.size(); i++) {
        updateables[i]->update();
    }
}

void GameObject::addUpdateable(Updateable* updateable) {

    updateables.push_back(updateable);
    updateable->gameObject = this;
}

void GameObject::removeUpdateable(Updateable* updateable) {

    auto it = find(updateables.begin(), updateables.end(), updateable);
    if (it != updateables.end()) {
        updateables.erase(it);
    }
}

PlayerController.h

#pragma once

#include "Updateable.h"
//#include "GameObject.h"
#include "Input.h"

class Updateable;

class PlayerController : public Updateable {

public:

    float speed = 5.0f;

    void update();
};

PlayerController.cpp

#include "PlayerController.h"

void PlayerController::update() {

    float x = 0;

    if (Input::getKeyDown(GLFW_KEY_A)) {
        x = -speed;
    }

    if (Input::getKeyDown(GLFW_KEY_D)) {
        x = speed;
    }

    cout << x << endl;

    gameObject->getRigidBody()->velocity.x = x;
    //yes this is a method in GameObject that I removed from this post
    //because it would take up more space, rigidbody.h does not create
    //a circular dependency
}

GameManager.h

#pragma once

#include "GameObject.h"
#include "PlayerController.h"

class GameManager {

public:

    static void init();
    static void addGameObject(GameObject* go);
    static void removeGameObject(GameObject* go);

    static void onFrame();

private:
    static vector<GameObject*> gameObjects;
    static GameObject* box;

GameManager.cpp

#include "GameManager.h"

vector<GameObject*> GameManager::gameObjects;
GameObject* GameManager::box;

void GameManager::init() {

    gameObjects = vector<GameObject*>();

    box = new GameObject();
    box->addUpdateable(new PlayerController());
}

void GameManager::addGameObject(GameObject* go) {
    gameObjects.push_back(go);
}

void GameManager::removeGameObject(GameObject* go) {

    auto it = find(gameObjects.begin(), gameObjects.end(), go);
    if (it != gameObjects.end()) {
        gameObjects.erase(it);
    }
}

void GameManager::onFrame() {

    for (unsigned int i = 0; i < gameObjects.size(); i++) {
        gameObjects[i]->runUpdateables();
    }
}

Here is the exact error message: Error C2504 'Updateable': base class undefined Basic Platformer c:\users\default.sixcore-pc\documents\visual studio 2015\projects\basic platformer\basic platformer\playercontroller.h 9


Solution

  • A lot of your files have both #include "Class.h" and class Class; declarations. You never need both; use one or the other.

    A definition of a class X must be visible when:

    • accessing the members of X
    • creating an object of type X
    • defining a class derived from X
    • using X as a template argument to a template which requires the corresponding template parameter to be a complete type (such as what standard library containers require of their element type). Note that this applies when using X, not X*.

    In other cases (such as creating a pointer to X or declaring a function taking of returning X), a non-defining declaration (class X;) is enough.

    Using these rules (plus moving function bodies from headers to source files when necessary), you can solve any circular dependency issues.


    To directly address your files as presented:

    • Updateable.h does not need to #include "GameObject.h". It doesn't even need the forward declaration of GameObject.
    • GameObject.h doesn't need any of the two #includes in it.
    • GameManager.h doesn't need any #includes. It needs a declaration of class GameObject; though.