1. 4FIPS
  2. PHOTOS
  3. VIDEOS
  4. APPS
  5. CODE
  6. FORUMS
  7. ABOUT
/*
(c) 2012 +++ Filip Stoklas, aka FipS, http://www.4FipS.com +++
THIS CODE IS FREE - LICENSED UNDER THE MIT LICENSE
ARTICLE URL: http://forums.4fips.com/viewtopic.php?f=3&t=727
*/

#include <vector>
#include <string>
#include <cstdio>
#include <cassert>

/// Represents a read-only entity state (a kind of public data interface).
struct Entity
{
    const char *name;
    int x, y;

    Entity(const char *name, int x, int y) : name(name), x(x), y(y) {}
};

/// Entity table with just a single getter and multiple setters.
class Entity_table
{
 public:

    /// Adds a new entity, returns its index.
    size_t add(const char *name, int x, int y)
    {
        assert(name);
        _records.emplace_back(Entity_record(Entity_data(name, x, y)));
        return _records.size() - 1;
    }

    /// Returns the size of the table.
    size_t size() const { return _records.size(); }

    /// Returns an existing entity (its whole read-only state).
    const Entity & get(size_t index) const
    {
        assert(index < _records.size());
        return _records[index].entity;
    }

    /// Sets entity name.
    void set_name(size_t index, const char *name)
    {
        assert(index < _records.size());
        assert(name);
        const Entity_data &orig_data = _records[index].data;
        _records[index] = Entity_record(Entity_data(name, orig_data.x, orig_data.y));
    }

    /// Sets entity position.
    void set_position(size_t index, int x, int y)
    {
        assert(index < _records.size());
        const Entity_data &orig_data = _records[index].data;
        _records[index] = Entity_record(Entity_data(orig_data.name, x, y));
    }

 private:
    
    /// Represents a private entity state (data holder). 
    struct Entity_data
    {
        std::string name;
        int x, y;
        // here might come more implementation details that are not exposed in Entity

        Entity_data(const std::string &name, int x, int y) : name(name), x(x), y(y) {}
    };

    /// Contains entity and its data, it's responsible for keeping them linked and in sync.
    struct Entity_record
    {
        Entity_data data;
        Entity entity;

        /// Initializes data, links entity to the data.
        Entity_record(const Entity_data &data):
        data(data),
        entity(data.name.c_str(), data.x, data.y)
        {}

        /// Copies data, links entity to the new data.
        Entity_record(const Entity_record &rhs):
        data(rhs.data),
        entity(data.name.c_str(), data.x, data.y)
        {}

        /// Copies data, links entity to the new data.
        Entity_record & operator = (const Entity_record &rhs)
        {
            data = rhs.data;
            entity = Entity(data.name.c_str(), data.x, data.y);
            return *this;
        }
    };

    std::vector<Entity_record> _records;
};

int main()
{
    Entity_table entities;

    entities.add("foo", 10, 20);
    entities.add("bar", 30, 40);

    for(size_t i = 0, n = entities.size(); i < n; ++i)
    {
        const Entity &e = entities.get(i); // atomic getter
        printf("entity[%u] : name='%s', pos=[%d, %d]\n", unsigned(i), e.name, int(e.x), int(e.y));
    }

    // fine grained setters
    entities.set_name(0, "FOO");
    entities.set_position(0, 50, 60);
    entities.set_name(1, "BAR");
    entities.set_position(1, 70, 80);

    for(size_t i = 0, n = entities.size(); i < n; ++i)
    {
        const Entity &e = entities.get(i); // atomic getter
        printf("entity[%u] : name='%s', pos=[%d, %d]\n", unsigned(i), e.name, int(e.x), int(e.y));
    }

    return 0;
}

// output:
// entity[0] : name='foo', pos=[10, 20]
// entity[1] : name='bar', pos=[30, 40]
// entity[0] : name='FOO', pos=[50, 60]
// entity[1] : name='BAR', pos=[70, 80]