mercredi 21 mars 2018

c++ Circular includes (implementing component pattern) [duplicate]

This question already has an answer here:

Implementing the Component pattern in C++, I came upon a problem related with includes.


I have an Entity class, which has the following components: Renderer, Physics and Collider. They inherit from Component, which has a single virtual method Component->update(elapsedTime) and a reference to the Entity it belongs to.

Each time an Entity is updated, its components are updated too. As the components alter the Entity's state, I thought it would be a good idea making them have a reference to the Entity they belong too (I could pass the Entity as a parameter in the Component->update() call but for performance I prefer not doing so).

Now I must include Renderer, Physics and Collider from Entity, and include Entity from Component. Luckily, Entity calls methods from the above but Component just stores a reference to Entity, so I should include normally Renderer, Physics and Collider from Entity, and forward declare Entity from Component. After doing so, I found a problem which I cannot solve or know how to investigate. So I tried the other way around, and other problems risen up.

  • Including normally Renderer, Physics and Collider from Entity, and forward declare Entity from Component:

A class which includes Entity would complain saying that Entity is ambiguous. This is due to (I think) that class loading Entity, then loading its Components which forward declare Entity. How can I forward declare Entity without having other classes complaining about this ambiguity?

  • Including normally Entity from Component, and forward declaring Renderer, Physics and Collider from Entity:

In my Entity->update() method I do this->collision->update() which breaks, by saying that pointer to incomplete class is not allowed.

How can I solve this without changing my class structure or behavior?

Here are the files, using the first example (including the components and forward declaring Entity from Component:

Entity.h

#ifndef ENTITY_H

#define ENTITY_H

#include "Collision.h"
#include "Physics.h"
#include "Renderer.h"
#include "TextureRenderer.h"

using namespace Components;

namespace Entities
{
    class Entity
    {
        public:

            Entity();

            Collision* getCollision();

            virtual void update(float elapsedTime);

        protected:
            Collision* collision;
            Physics* physics;
            Renderer* renderer;
    };
}
#endif

Entity.cpp

#include "Entity.h"

namespace Entities
{

    Entity::Entity()
    {
    }

    Collision* Entity::getCollision()
    {
        return this->collision;
    }

    void Entity::update(float elapsedTime)
    {
        collision->update(elapsedTime);
        physics->update(elapsedTime);
        renderer->update(elapsedTime);
    }

}

Component.h

#ifndef COMPONENT_H

#define COMPONENT_H

namespace Components
{
    class Component
    {
        public:
            Component(Entity* entity);

            virtual void update(float elapsedTime) = 0;
        protected:
            Entity* entity;
        private:
            friend class Entity;
    };
}

#endif

Component.cpp

#include "Component.h"
#include "Entity.h"

namespace Components
{
    Component::Component(Entity* entity)
    {
        this->entity = entity;
    }
}

Collision.h

#ifndef COLLISION_H

#define COLLISION_H

#include "Component.h"

namespace Components
{
    class Collision : public Component
    {
        public:
            void collide(Entity* entity);

        protected:
            virtual bool collidesWith(Entity* entity) = 0;
    };
}

#endif

Collision.cpp

#include "Collision.h"

namespace Components
{
    void Collision::collide(Entity* entity)
    {
        if (this->collidesWith(entity))
        {
            ...
        }
    }
}

Renderer.h

#ifndef RENDERER_H

#define RENDERER_H

#include "Component.h"

namespace Components
{
    class Renderer : public Component
    {
        public:
            Renderer(Entity* entity);

            virtual void update(float elapsedTime) override;
    };
}


#endif

Renderer.cpp

#include "Renderer.h"

namespace Components
{
    Renderer::Renderer(Entity* entity) : Component(entity){ }

    void Renderer::update(float elapsedTime){}
}

And Physics is pretty much empty and I don't feel like adding it right now.

Thanks a lot


I'm having way to much problems with includes. My code does a lot of double referencing for performance reasons, and each pair of classes accesses the methods of each other. C++ hates this, and defining which class should forward declare the other is a pain in the ass.

Aucun commentaire:

Enregistrer un commentaire