I am fairly new to templates and compile-time functions, and I am currently trying to write a basic entity-component-system (ECS) that will allow me to store generic types (components) in containers. I want functionality to be able to setup my containers at compile-time, and run-time. There are some instances in my application where I know for certain which components will always persist for an entity so I want to be able to set these up and reserve the slots at compile-time. There are then occasions where components will need to be removed/added at runtime dynamically.
template <typename T, size_t N>
struct ComponentArray {
public:
template<typename... Args>
constexpr const T& AddComponent(const uint64_t entity, Args &&... args) const
{
//T component{ std::forward<Args>(args)... }; //i want to be able to do something like this..
//m_components[0] = component; //create the component and give it a slot, but this is const
return m_components[0];
}
std::array<T, N> m_components = {};
std::array<T, N> m_entityIds = {};
uint32_t m_used = 0;
};
template <uint64_t entityId, typename Component, typename ... Args>
constexpr const Component& AddComponentCT(Args&&... args)
{
constexpr uint64_t componentHash = GetHashForString(ComponentTypeInfo<Component>::m_name, ComponentTypeInfo<Component>::m_nameLength);
constexpr uint64_t storageHash = ComponentStorageMapping<componentHash>::m_hash;
static_assert(storageHash == componentHash);
constexpr const auto& componentStorage = ComponentStorageMapping<componentHash>::m_componentStorage;
constexpr const Component& rComponent = componentStorage.AddComponent(entityId /*std::forward<Args>(args)...*/);
return rComponent;
}
constexpr const TransformComponent transform = AddComponentCT<ENTITY_ID, TransformComponent>(5.0f, 5.0f, 5.0f);
This seems to work, and when I hover over transform
, I can see what is returned but obviously incorrect values as I can't construct the type using the arguments in the const function.
I have also tried to remove some of the const from the variables but then it complains that things aren't const or can't be evaluated.
I am looking to create a type of Component
, with the arguments provided and then find a free slot in m_components
to store it. Is something like this possible at compile-time? If anyone has suggestions on how I could achieve what I want, or improve the solution it would be much appreciated. There is some macro code that I haven't included here which generates the classes like ComponentStorageMapping
, ComponentTypeInfo
and m_componentStorage
Below is a more simplified/generic version as requested in the comments
struct A
{
float x, y, z;
};
struct B
{
int i, j, k;
};
template <typename T, size_t N>
struct C {
public:
template<typename... Args>
constexpr const T& AddToArray(Args &&... args) const
{
//T element{ std::forward<Args>(args)... }; //i want to be able to do something like this..
//m_array[0] = element; //create the component and give it a slot, but this is const
return m_array[0];
}
std::array<T, N> m_array = {};
};
template <typename T, typename ... Args>
constexpr const T& AddElement(Args&&... args)
{
constexpr const T& element = C<T, 50>::AddToArray(/*std::forward<Args>(args)...*/);
return element;
}
constexpr const A& someArrayElement = AddElement<A>(5.0f, 5.0f, 5.0f); //this would point to C<A,50>::m_array[0], fully constructed
constexpr const A& someArrayElement2 = AddElement<A>(10.0f, 10.0f, 10.0f); //this would point to C<A,50>::m_array[1], fully constructed
constexpr const B& someArrayElement3 = AddElement<B>(1, 1, 1); //this would point to C<B,50>::m_array[0], fully constructed
constexpr const B& someArrayElement4 = AddElement<B>(2, 2, 2); //this would point to C<B,50>::m_array[1], fully constructed
For the record, I created another post ( https://gamedev.stackexchange.com/questions/176301/how-can-i-create-a-templated-function-to-add-components-to-their-necessary-conta ) before this one to see how I would approach this situation and didn't receive many suggestions so I just tried to do it myself and this is the problem I have encountered. There is another framework achieving what I'm trying to do in some way, but I don't quite understand what they're doing as it's all templates. Links to the framework are in the other post.
I don't think that is possible what do you want.
See what you have written for AddToArray()
template<typename... Args>
constexpr const T& AddToArray(Args &&... args) const
{
//T element{ std::forward<Args>(args)... }; //i want to be able to do something like this..
//m_array[0] = element; //create the component and give it a slot, but this is const
// .......................^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
return m_array[0];
}
Do you want a constexpr
method, that is also const
, that add a component in a member of the class/struct.
And obviously you can't use it as follows
constexpr const A& someArrayElement = AddElement<A>(5.0f, 5.0f, 5.0f);
constexpr const A& someArrayElement2 = AddElement<A>(10.0f, 10.0f, 10.0f);
Yuo can't call AddElement()
without an object of its type.
You should start with a constexpr
object of type C
constexpr C<A, 50u> ca;
and then use it to call AddElement()
constexpr const A& someArrayElement = ca.AddElement<A>(5.0f, 5.0f, 5.0f);
constexpr const A& someArrayElement2 = ca.AddElement<A>(10.0f, 10.0f, 10.0f);
// ....................................^^^
but if ca
is constexpr
you can't add elements in it and if ca
isn't constexpr
you can't use the result of AddElement()
to initialize the value of a constexpr
variable.
The only way to I see to solve this problem is initialize the ca
object with all elements; something as
constexpr C<A, 50u> ca { A{5.0f, 5.0f, 5,0f}, A{10.0f, 10.0f, 10.0f} /* etc ... */ };
and then extract the reference to the components
constexpr A & someArrayElement = ca.GetElement(0);
constexpr A & someArrayElement2 = ca.GetElement(1);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.