I've been working on a way to have generic properties for an entity-component style system, but without resorting to using pointers of a base class to reference the myriad of different component types a game could have, and using little to no heap allocations.
I'm building a system where the entity has a map of states (equivalent of components), only they are much more fine grained (i'm storing individual types, such as float, int, bool and the like). Whether that's gonna be efficient is yet to be seen, but that's not my question.
Starting from the outside, i have a Statemap class, which is just a wrapper around an unordered_map. The map is templated on .
The State class has template methods, but is not templated itself. It contains an integer handle and a pointer to an IDataStore interface.
The IDataStore has 2 methods, one for acquiring a handle based resource, and another for releasing a handle.
The DataStore class inherits from IDataStore, and is templated on any type (the only requirement is that the type is default constructible, but seeing as i'm currently using only primitive data types for the States makes it ok for now).
I'm at work atm, so i've only come up with an untested sample (so i dont know whether all the template stuff is actually gonna work), but i'd like to get opinions on this anyway. note, the sample ahead contains no sanity checks whether the handle is valid, but they do exist in variuos places inside the DataStore's methods.,>
class IDataStore
{
public:
int AcquireHandle(int handle = NOT_FOUND) = 0;
void ReleaseHandle(int handle) = 0;
};
template<typename T>
class DataStore : public IDataStore
{
public:
DataStore(int defaultSize)
{
_cache.reserve(defaultSize);
...
}
int AcquireHandle(int handle)
{
if(handle == NOT_FOUND)
{
if(_freeSlots.empty())
{
_refs.push_back(0);
handle = _cache.size();
_cache.push_back(T()); //maybe use emplace or something
}
else
{
handle = _freeSlots.back();
_freeSlots.pop_back();
}
}
++_refs[handle];
return handle;
}
void ReleaseHandle(int handle)
{
--_refs[handle];
if(_refs[handle] == 0)
{
_freeSlots.push_back(handle);
}
}
T& get(uint handle)
{
return _cache[handle];
}
private:
std::vector<T> _cache;
std::vector<uint> _freeSlots;
std::vector<uint> _refs;
};
class Property
{
public:
template<typename T>
T& as()
{
DataStore& store = getDataStoreReference<T>();
if(_store != &store)
{
throw exception;
}
return store.get(_handle);
}
template<typename T>
Property(T& value)
{
DataStore& store = getDataStoreReference<T>();
_store = &store;
_handle = store.Acquire();
store.get(_handle) = value;
}
template<typename T>
Property()
{
DataStore& store = getDataStoreReference<T>();
_store = &store;
_handle = store.Acquire();
}
Property(const Property& rhs)
: _store(rhs._store), _handle(rhs._handle)
{}
Property& Property::operator=(const Property& rhs)
{
if(this != &rhs)
{
_store->ReleaseHandle(_handle);
_store = rhs._store;
_handle = _store->Acquire(rhs._handle);
}
return *this;
}
~Property()
{
_store->ReleaseHandle();
}
private:
IDataStore* _store; //pointer if a allow resetting, reference if not
int _handle;
};
This is a sample of the basic outline. Theoretically (i'll test when i get home) you can allocate States on the stack, freely copy them around (as only the handle is copied, not the data itself), to whatever you want with them.
To get the data, you get a copy or reference to the State class, and call the template method as(), and if the types are ok, you get the actuall data by reference.
The function getDataStoreReference() is a bit of a problem tho. Originally i was planning to make it a small function which makes a static DataStore store; and immediatelly returns it, but if that's considered a bad thing (global masked behind a function), the function could make a call to somewhere else and get the actual store which is allocated wherever. Point is, that function can return the Store from wherever it wants, as long as it always returns the same object which doesnt move in memory.
Any thoughts on this are welcome, and criticism (and pointing out holes) is even more welcome.
edit note: the post editor is wonky...