Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Storing struct instances in a std::map

I'm trying to map some structs to some other instances, like this:

template <typename T>
class Component {
public:

    typedef std::map<EntityID, T> instances_map;

    instances_map instances;

    Component() {};

    T add(EntityID id) {
        T* t = new T();
        instances[id] = *t;
        return *t;
    };  
};

Then I use it like this:

struct UnitInfos {
    int owner_id;
    int health;
    float x, y;
};

class LogicComponent : public Component<UnitInfos> {};

The problem is that when it later retrieve data later on, like this:

comp.instance[id];

I get a breand new object with properties initialized at default values.

Is there something inherently wrong with this piece of code, or am I leaving out information about the problem?


As per @aaa suggestion, i change the code to

typedef std::map<EntityID, T> instances_map;
instances_map instances;
T& add(EntityID id) {
    instances[id] = T();
    return instances[id];
};

but when I access it

UnitInfos &info = logic_c.instances[id];

the value of info.x is still 0. Any pointers?


The problem was how I stored the reference to LogicComponent in another class. using LogicComponent logic_c; instead of LogicComponent& logic_c;. It now works, but I'm storing pointers in the map (instead of @aaa's suggestion). Is this a bad idea?

like image 598
sharvey Avatar asked Jul 31 '10 02:07

sharvey


2 Answers

Clarify the operations you want to perform on LogicComponent. Assuming you are trying to achieve something like this:

Step 1: Add a new entry to the map:

LogicComponent comp; 
EntityID id = 99;
UnitInfos info = comp.add(id);

Step 2: Initialize the info:

info.x = 10.0;
info.y = 11.0
// etc

Step 3: Get the info object again:

UnitInfos info2 = comp.instances[id]; // this is uninitialized.

Then, a few code comments are in order:

The info object returned by comp.add is a COPY of the object you added to the map. By modifying it, you are not modifying what is in the map.

The simplest fix is to create a map of pointers to the object instead of the object itself.

typedef std::map<EntityID, T*> pinstances_map;

T * add(EntityID id) {
    T* t = new T();
    instances[id] = t;
    return t;
};  

// initialize as 
UnitInfo *info = comp.add(id);
info->x = 10.0;
info->y = 11.0;

// retrieve as 
UnitInfos *info = comp.instances[id];

Also, do use an accessor method to get the mapped value, instead of exposing the map object as public. Make the instances variable protected, and add a public get() method.

Edit: This code works fine for me:

#include <map>
#include <iostream>
using namespace std;

template<typename T>
class Component
{
public:
        typedef map<long, T*> pinstances_map;
        pinstances_map instances;

        T * add(long id)
        {
                T *t = new T();
                instances[id] = t;
                return t;
        }
};

struct UnitInfo 
{
        float x, y;
};

class LogicComponent: public Component<UnitInfo> {};

int main()
{
        LogicComponent comp;
        UnitInfo *info = comp.add(99);
        info->x = 10.0;
        info->y = 11.0;

        UnitInfo *info2 = comp.instances[99];
        cout << info2->x << " " << info2->y;

        return 0;
}
like image 88
carlsborg Avatar answered Oct 04 '22 22:10

carlsborg


might be that

T add(EntityID id) {
    T* t = new T();
    instances[id] = *t;
    return *t;  // return value and map instance are not the same anymore
};  

should be

T& add(EntityID id) {
    instances[id] = T();
    return instances[id];
};  
like image 24
Anycorn Avatar answered Oct 04 '22 23:10

Anycorn