8 minutes ago, Hodgman said:
std::function<B*()> getter = [&c2s, c2index]() { return &c2s[c2index]; }
Interesting.
9 minutes ago, Hodgman said:
The system can dereference a component's index-like-handle by combining it with it's own pointer. To catch programming errors, in debug builds, my index-like handles contain an index and a pointer/ID as well, so that the dereference process can be validated (is this handle being used to index the correct container?).
I currently pass indices around, but that does not feel right. It exposes way to many internals. On my scene's side, I have something like this:
template< typename ElementT, typename... ConstructorArgsT >
ID Create(ConstructorArgsT&&... args);
template< typename ElementT >
ElementT &Get(ID id) noexcept;
template< typename ElementT >
const ElementT &Get(ID id) const noexcept;
template< typename ElementT >
size_t GetNumberOf() const noexcept;
template< typename ElementT, typename ActionT >
void ForEach(ActionT action);
template< typename ElementT, typename ActionT >
void ForEach(ActionT action) const;
Here, ID is a typedef for size_t. This approach forces the user to chain Create -> Get with the same template type for instance. Adding type information as wel to the ID, does not feel right (because I expect some type enum and some switch/case statements to appear). The largest problem, however, is the getter: it returns a reference. You may use the reference, but not store it. If the std::vectors are enlarged, all references and pointers are dirty. Only the IDs will stay the same.
I also took a look at the LumixEngine which does not expose references or pointers at all, but uses the "ID" for all operations, resulting in some giant monolithic universe/scene interface.
At the side of my Entity, I have something like this:
ID m_parent;
std::vector< ID > m_childs;
std::unordered_multimap< std::type_index, ID > m_components;
It requires some book keeping as well with regard to the template parameter. Therefore, I thought to use some handle which basically works as a pointer.
Alternatively, I can keep working with IDs internally and create an Handle< C > anyway for only the most derived types.
so
template< typename ComponentT >
ID Get() const noexcept;
template< typename ComponentT >
std::vector< ID > GetAll() const;
becomes
template< typename ComponentT >
Handle< ComponentT > Get() const noexcept;
template< typename ComponentT >
std::vector< Handle< ComponentT > > GetAll() const;
I just do not allow to use the base Component type anymore.