Search code examples
c++classvectordereference

Dereferenced Pointer to Vector Element in Object doesn't Update Value


I am trying to make a toy Entity Component System to improve on my C++ skills. To do so, I have an ECS_Manager Class, which uses templates to have multiple storage classes for each data type I'd like to use as a component.

Each component is just a struct with an int and whatever else data I'd like. In the example, it contains a Vector2D object. Nothing special.

As a starting point, internally the storage class for each component type has its own vector. I can store new data just fine (it eventually prints out if I do so). However, when I try to save new data to the element in the vector using a pointer dereference (see Physics_System below), the new values aren't saved. I cannot figure out why.

#include <iostream>
#include <vector>
#include <map>
#include <typeinfo>
#include <memory>
#include <array>

#include "Vector2D.hpp"
#include "./ECS/components/Position_comp.hpp"


class VComponentStorage {
  
  public:
      virtual ~VComponentStorage() = default;
};

template <typename T>
class ComponentStorage : public VComponentStorage{
    
  public:

    ComponentStorage(){
        this->storage_container_index = 0;
    }

    void add_component(T component){
    
        this->storage_container.push_back(component);
        this->storage_container_index++;
    
    }

    T *get_component(int entity_id){

        T* start_ptr = this->storage_container.data();
        
        return &(start_ptr[entity_id]); 
    }

    size_t get_component_count(){
        return 1;    
    }
  
  private:
      std::vector<T> storage_container;
      size_t storage_container_index;
};

class ECS_Mananger{

  public:
    
    ECS_Mananger(){

    }
    
    template<typename T> void register_component(){
        const char *type_name = typeid(T).name();
        
        ComponentStorage<T> *comp_storage_ptr = new ComponentStorage<T>;

        this->T_to_comp_storage_Map.insert({type_name, comp_storage_ptr});
        
    }
    
    template<typename T>
    void add_component(T component){
        const char *type_name = typeid(T).name();
        
        std::cout << type_name << std::endl;
        ComponentStorage<T>* my_ptr = static_cast<ComponentStorage<T>*>(this->T_to_comp_storage_Map[type_name]);
        my_ptr->add_component(component);
    }

    template<typename T>
    T *get_component(int entity_id){
        const char *type_name = typeid(T).name();

        ComponentStorage<T>* my_ptr = static_cast<ComponentStorage<T>*>(this->T_to_comp_storage_Map[type_name]);
        return my_ptr->get_component(entity_id);
    }

  private:

    std::map<std::string, VComponentStorage*> T_to_comp_storage_Map;
    

};

void Physics_System(ECS_Mananger &world){

    Position_Component* my_ptr = world.get_component<Position_Component>(0);
    *my_ptr = { 0, Vector2D(5.0, 2.0)};

};
int main() {
    
    //Create Arrays
   
    ECS_Mananger my_world;

    my_world.register_component<Position_Component>();
    
    Position_Component init_pos_val = {0, Vector2D(1.0, 2.0)};

    my_world.add_component<Position_Component>(init_pos_val);
    // Main game loop
    while (1) // Detect window close button or ESC key
    {
        // Update
        Physics_System(my_world); 
        my_world.get_component<Position_Component>(0)->position.print();
    }

    return 0;
}

In the Physics_System function, I would expect the value to be updated. Any reason why it isn't? My C++ is a little rusty. At first, I was doing this: 'world.get_component<Position_Component>(0)->position = Vector2D(4.4, 3.3);' to change the value. It wasn't working either, so I thought maybe it had something to do with right/left-hand issues with the language. I suspect that even though I am using pointers, somewhere something is getting copied. I just can't tell where.

Edit:

Position_Component and the Vector2D specification:

#include "Vector2D.hpp"

struct Position_Component {
    
    int entity_id;

    Vector2D position;

};
class Vector2D {
  public:
    double x;
    double y;


    Vector2D() {
        this->x = 0.0;
        this->y = 0.0;
    }

    Vector2D(double x, double y){
        this->x = x;
        this->y = y;
    }

    void print(){ std::cout << "(" << this->x << "," << this->y << ")" << std::endl; };


    Vector2D operator+(const Vector2D& vec){
        Vector2D result;
        result.x = this->x + vec.x;
        result.y = this->y + vec.y;
        
        return result;
    }

    Vector2D operator-(const Vector2D& vec){
        Vector2D result;
        result.x = this->x - vec.x;
        result.y = this->y - vec.y;

        return result;
    }

    Vector2D operator=(const Vector2D& vec){
        Vector2D result;
        result.x = vec.x;
        result.y = vec.y;
        return result;
    }
};


Vector2D operator*(const double& s, Vector2D vec){
        Vector2D result;
        result.x = s*vec.x;
        result.y = s*vec.y;
        
        return result;
}


Solution

  • The problem is your Vector2D assignment operator:

    Vector2D operator=(const Vector2D& vec){
        Vector2D result;
        result.x = vec.x;
        result.y = vec.y;
        return result;
    }
    

    The assignment operator is supposed to assign to this object, not create a new object. It should also return a reference to *this (to allow chained assignments).

    Because of that problem, the assignment

    *my_ptr = { 0, Vector2D(5.0, 2.0)};
    

    is simply thrown away, you never actually modify *my_ptr.

    To solve it change to e.g.:

    Vector2D& operator=(const Vector2D& vec){
        x = vec.x;
        y = vec.y;
        return *this;
    }
    

    I recommend spending some time reading this canonical implementation reference for overloading operators.