I'm trying to figure out the best solution to the following problem:
I have a library (which is not modifiable directly) that manages the graphics of a game. I have a Entity class which is the logical entity of a displayable entity which is paired by a Sprite instance.
The graphics library already contains support to manage a collection of sprites so I'd like to use it even for the logic without letting the logic know about the graphical engine at all (I don't want to need to include the header), for all the purposes that are related to iterating over the entities (which are many are require many updates per second).
To obtain this solution while preserving encapsulation I figured a solution similar to this one (I oversimplified it):
gfx_engine.h (not modifiable)
class Sprite
{
...
};
class SpriteBatch
{
private:
std::vector<Sprite*> sprites;
public:
const std::vector<Sprite*>& const getSprites() { return sprites; }
}
entity_sprite.h
#include "gfx_engine.h"
class EntitySprite : public Sprite
{
private:
Entity *entity;
public:
...
void setEntity(Entity* entity) { this->entity = entity; }
Entity* getEntity() { return entity; }
};
entities.h
class SpriteBatch;
template<typename T>
class EntityCollection
{
private:
SpriteBatch* batch;
public:
class const_iterator
{
private:
std::vector<Sprite*>::const_iterator inner;
public:
const_iterator(const std::vector<Sprite*>::const_iterator& it) : inner(it) { }
inline bool operator!=(const const_iterator& other) const { return inner != other.inner; }
inline const const_iterator& operator++() { ++inner; return *this; }
T* operator*() const;
};
const_iterator begin() const;
const_iterator end() const;
EntityCollection() : batch(nullptr) { }
explicit EntityCollection(SpriteBatch* batch) : batch(batch) { }
};
entities.cpp
#include "Entities.h"
#include "EntitySprite.h"
template<typename T>
typename EntityCollection<T>::const_iterator EntityCollection<T>::begin() const { return const_iterator(batch->getChildren().cbegin()); }
template<typename T>
typename EntityCollection<T>::const_iterator EntityCollection<T>::end() const { return const_iterator(batch->getChildren().cend()); }
template<typename T>
T* EntityCollection<T>::const_iterator::operator*() const { return static_cast<T*>(static_cast<EntitySprite*>(*inner)->getEntity()); }
Now, this works (I can do for (Entity* entity : entities) but it has 2 drawbacks:
- I'm forced to include
EntitySprite.hinEntities.cppwhich should be a logic only file (minor drawback) - I need to implement
operator*()overload in a source file to avoid includingEntitySprite.h(and consequently the whole gfx engine) insideEntities.h, but I want to avoid such a trivial function to be non inlined
The problem is that the compiler is unable to inline or optimize the call to operator* (I already checked the -O2 generated binary) but this operation is done on thousands of entities 60 times per second so to obtain encapsulation I have to trade it for perfomance.
I'm trying to avoid this but I haven't come with a good solution yet. Any suggestions?
Aucun commentaire:
Enregistrer un commentaire