How do I create a vector which stores all the instances of a class? Then how do I iterate over them and call one of their member functions?
Here's a condensed example of what I've been trying to do.
#include <vector>
struct Entity{
Entity::Draw(){
// do drawing things
}
};
static std::vector<Entity> entities;
Entity player;
Entity enemy;
void renderEntities() {
for (std::vector<Entity>::iterator iter = entities.begin();
iter < entities.end();
iter++) {
iter->Draw; // Error in the example. I'm using Draw(); in the actual code.
}
But renderEntities() isn't doing anything. The Draw member function works, if I use e.g. player->Draw. I'm either screwing up the vector or the iterator or both and I can't figure out how to fix it. I've tried using references and pointers, which I assume is the thing to do, but whenever I try that I get errors that I haven't been able to fix.
UPDATE: I appreciate all the help, I'm learning a lot. However my render_entities function still isn't doing anything. Here's all the code.
Any function call that starts with terminal_ is from the BearLibTerminal library.
main.cpp
#include <BLT/BearLibTerminal.h>
#include <iostream>
#include <string.h>
#include <vector>
#include "entity.h"
const int WindowSizeX{50};
const int WindowSizeY{20};
const std::string Title{"BLT Test"};
const std::string Font{"../res/SourceCodePro-Regular.ttf"};
const int FontSize{24};
bool quit_game{false};
static Entity player;
static Entity enemy;
void initialize();
void handle_input(int key, Entity &entity);
void draw_player(int x, int y, const char *symbol);
void render_entities();
void clear_entities();
int main() {
initialize();
while (!quit_game) {
terminal_refresh();
int key{terminal_read()};
if (key != TK_CLOSE) {
handle_input(key, player);
}
else {
quit_game = true;
break;
}
clear_entities();
}
terminal_close();
return 0;
}
void initialize() {
terminal_open();
std::string size{"size=" + std::to_string(WindowSizeX) + "x" +
std::to_string(WindowSizeY)};
std::string title{"title='" + Title + "'"};
std::string window{"window: " + size + "," + title};
std::string fontSize{"size=" + std::to_string(FontSize)};
std::string font{"font: " + Font + ", " + fontSize};
std::string concatWndFnt{window + "; " + font};
const char *setWndFnt{concatWndFnt.c_str()};
terminal_set(setWndFnt);
terminal_clear();
player.x = 0;
player.y = 0;
player.layer = 0;
player.symbol = "P";
player.color = "green";
enemy.x = 10;
enemy.y = 10;
enemy.layer = 0;
enemy.symbol = "N";
enemy.color = "red";
}
void handle_input(int key, Entity &entity) {
int dx{0};
int dy{0};
switch (key) {
case TK_LEFT:
case TK_H:
dx = -1;
dy = 0;
break;
case TK_RIGHT:
case TK_L:
dx = 1;
dy = 0;
break;
case TK_UP:
case TK_K:
dx = 0;
dy = -1;
break;
case TK_DOWN:
case TK_J:
dy = 1;
dx = 0;
break;
case TK_Y:
dx = -1;
dy = -1;
break;
case TK_U:
dx = 1;
dy = -1;
break;
case TK_B:
dx = -1;
dy = 1;
break;
case TK_N:
dx = 1;
dy = 1;
break;
case TK_ESCAPE:
quit_game = true;
break;
}
player.Move(dx, dy);
if (player.x > WindowSizeX - 1) {
player.x = WindowSizeX - 1;
}
else if (player.x < 0) {
player.x = 0;
}
if (player.y > WindowSizeY - 1) {
player.y = WindowSizeY - 1;
}
else if (player.y < 0) {
player.y = 0;
}
player.Draw(); // This works.
enemy.Draw(); // So do this.
entity.Draw(); // This draws only player.
render_entities(); // This doesn't do anything.
// Player X and Y are printed out correctly, Entities is always 0.
std::cout << "Player X: " << player.x << std::endl;
std::cout << "Player Y: " << player.y << std::endl;
std::cout << "Entities: " << entities.size() << std::endl;
}
void render_entities() {
for (auto entity : entities) {
entity->Draw();
}
}
void clear_entities() {
for (auto entity : entities) {
entity->Clear();
}
}
entity.h
#ifndef ENTITY_H_
#define ENTITY_H_
struct Entity {
int x;
int y;
int layer;
const char *symbol;
const char *color;
Entity();
~Entity();
void Move(int dx, int dy);
void Draw();
void Clear();
};
static std::vector<Entity *> entities;
#endif /* ENTITY_H_ */
entity.cpp
#include <BLT/BearLibTerminal.h>
#include <vector>
#include <algorithm>
#include "entity.h"
Entity::Entity() {
entities.push_back(this);
}
// Entity(const Entity &) : Entity() {}
// I get an "expected unqualified-id" when I uncomment this. Why?
Entity::~Entity() {
auto iter = std::find(entities.begin(), entities.end(), this);
if (iter != entities.end())
entities.erase(iter);
}
void Entity::Move(int dx, int dy) {
this->x += dx;
this->y += dy;
}
void Entity::Draw() {
terminal_layer(this->layer);
terminal_color(color_from_name(this->color));
terminal_print(this->x, this->y, this->symbol);
}
void Entity::Clear() {
terminal_layer(this->layer);
terminal_print(this->x, this->y, " ");
}
In main.cpp, at the bottom of handle_input() you will see...
player.Draw(); // This works.
enemy.Draw(); // So do this.
entity.Draw(); // This draws only player.
render_entities(); // This doesn't do anything.
// Player X and Y are printed out correctly, Entities is always 0.
std::cout << "Player X: " << player.x << std::endl;
std::cout << "Player Y: " << player.y << std::endl;
std::cout << "Entities: " << entities.size() << std::endl;
renderEntities()
does not do anything because you didn't add any Entity
objects to the vector
. When you declare your player
and enemy
objects, they are just hanging around in memory, they are not automatically added to the vector
. You need to add them explicitly, such as by calling entities.push_back()
.
I would suggest using the Entity
constructor and destructor to update the vector
automatically, instead of you having to remember to do it manually. That way, every Entity
object is accounted for by renderEntities()
, eg:
#include <vector>
#include <algorithm>
struct Entity;
static std::vector<Entity*> entities;
struct Entity
{
Entity()
{
entities.push_back(this);
}
Entity(const Entity &)
: Entity()
{
}
~Entity()
{
auto iter = std::find(entities.begin(), entities.end(), this);
if (iter != entities.end())
entities.erase(iter);
}
void Draw()
{
// do drawing things
}
};
Entity player;
Entity enemy;
void renderEntities()
{
for (auto *entity : entities)
{
entity->Draw();
}
}
UPDATE: after seeing your full code, I can see that you are still making some mistakes.
In main.cpp
, there is no entity
variable in scope of handle_input()
, so calling entity.Draw()
should not compile.
The really big mistake is in entity.h
, though. DO NOT declare your entities
variable as static
in that file! This causes every .cpp
which #include
's your entity.h
file to get its own copy of the variable. This means that main.cpp
and entity.cpp
are operating on separate std::vector
objects! That is why you see entities
is always empty in main.cpp
- the Entity
objects are never being added to the std::vector
that exists in main.cpp
, only to the std::vector
that exists in entities.cpp
.
You need to move the actual std::vector
variable into entity.cpp
(without static
), and then declare the variable as extern
in entity.h
so that all of your .cpp
files can access and share that single variable.
Try this instead:
entity.h
#ifndef ENTITY_H_
#define ENTITY_H_
#include <vector>
#include <string>
struct Entity {
int x = 0;
int y = 0;
int layer = 0;
std::string symbol;
std::string color;
Entity();
Entity(const Entity&);
~Entity();
Entity& operator=(const Entity&) = default;
...
};
extern std::vector<Entity *> entities; // <-- extern, NOT static!
#endif /* ENTITY_H_ */
entity.cpp
#include <BLT/BearLibTerminal.h>
#include <vector>
#include <algorithm>
#include "entity.h"
std::vector<Entity *> entities; // <-- real variable, also NOT static!
Entity::Entity() {
entities.push_back(this);
}
Entity::Entity(const Entity &src) : Entity() {
*this = src;
}
Entity::~Entity() {
auto iter = std::find(entities.begin(), entities.end(), this);
if (iter != entities.end())
entities.erase(iter);
}
...
void Entity::Draw() {
terminal_layer(layer);
terminal_color(color_from_name(color.c_str()));
terminal_print(x, y, symbol.c_str());
}
...