Reputation: 100
I've got a map where keys are entity id's (uint32_t
) and values are "signatures," aka uint64_t
's with certain bits set that represent a type: std::unordered_map<uint32_t, uint64_t> m_entityComponents{};
My goal is to write a template function that accepts a variable amount of types and returns whether or not ALL of those types exist inside an entity. Let's call that function bool hasAllComponents(uint32_t entity)
.
To do this, I've written a method which returns a unique id for each type:
template <class T>
uint8_t getTypeId()
{
static uint8_t s_type = ++m_nextComponentType;
return s_type;
}
And then, given any number of types, I construct a new "signature" by calling getTypeId()
using a fold expression to set each bit for every type present to 1:
template<typename... Types>
uint64_t getTypesSignature()
{
return ((1 << getTypeId<Types>()) | ... | 0);
}
Given these two helper functions, I should be able to check if an entity in my map has all of the passed in types with my hasAllComponents
method, which uses a bitwise &
operator to compare the cached signature in the map with the newly created one:
template<typename... Types>
bool hasAllComponents(uint32_t entity)
{
uint64_t signature = getTypesSignature<Types...>();
return (m_entityComponents[entity] & signature);
}
However, hasAllComponents
still returns true when only some of the bits match, not all. I think the issue might be with my getTypesSignature
fold logic not setting all bits correctly, although printing out the results looks fine to me. There's a flaw in my logic somewhere and would appreciate any help tracking it down!
Upvotes: 3
Views: 299
Reputation: 596307
You are generating a signature
that contains all the required bits set to 1, but you are not correctly checking if the entity's value actually has all of those same bits set to 1. You are checking if the value has any of those same bits is set to 1 instead.
In this comparison:
return (m_entityComponents[entity] & signature);
The result of the &
operator will be a uint64_t
, which is implicitly convertible to bool
, so the return
value will be true
if the resulting uint64_t
is non-zero, otherwise it will be false
.
So, let's say the calculated signature
is 107
(b01101011
). That means any combination of values with any of those bits set will result in true
, eg:
1: b00000001 & b01101011 = b00000001, != 0? true
2: b00000010 & b01101011 = b00000010, != 0? true
4: b00000100 & b01101011 = b00000000, != 0? false
8: b00001000 & b01101011 = b00001000, != 0? true
16: b00010000 & b01101011 = b00000000, != 0? false
32: b00100000 & b01101011 = b00100000, != 0? true
64: b01000000 & b01101011 = b01000000, != 0? true
107: b01101011 & b01101011 = b01101011, != 0? true
128: b10000000 & b01101011 = b00000000, != 0? false
255: b11111111 & b01101011 = b01101011, != 0? true
To make sure the value has all of the required bits are set, you simply need to change the comparison to this instead:
return ((m_entityComponents[entity] & signature) == signature);
The result will be true
only if the value has at least all of the signature
's bits set (it can have more), eg:
1: b00000001 & b01101011 = b00000001, == b01101011? false
2: b00000010 & b01101011 = b00000010, == b01101011? false
4: b00000100 & b01101011 = b00000000, == b01101011? false
8: b00001000 & b01101011 = b00001000, == b01101011? false
16: b00010000 & b01101011 = b00000000, == b01101011? false
32: b00100000 & b01101011 = b00100000, == b01101011? false
64: b01000000 & b01101011 = b01000000, == b01101011? false
107: b01101011 & b01101011 = b01101011, == b01101011? true
128: b10000000 & b01101011 = b00000000, == b01101011? false
255: b11111111 & b01101011 = b01101011, == b01101011? true
Upvotes: 4