Fix bug with duplicate signatures
Fixed bug where if forMatchingSignatures and forMatchingSignaturesPtr was called with some signatures in the TypeList being duplicates, then only the first duplicate and function pair would be called, and all other functions paired with other duplicate signatures would not be called.
This commit is contained in:
parent
6b0e950c84
commit
506b027655
5 changed files with 290 additions and 141 deletions
|
@ -30,6 +30,8 @@
|
|||
|
||||
#include "Meta/Combine.hpp"
|
||||
#include "Meta/Matching.hpp"
|
||||
#include "Meta/ForEachWithIndex.hpp"
|
||||
#include "Meta/ForEachDoubleTuple.hpp"
|
||||
#include "Bitset.hpp"
|
||||
|
||||
namespace EC
|
||||
|
@ -1216,20 +1218,20 @@ namespace EC
|
|||
the order of entities called is not guaranteed. Otherwise entities
|
||||
will be called in consecutive order by their ID.
|
||||
*/
|
||||
template <typename SigList, typename FuncTuple>
|
||||
void forMatchingSignatures(FuncTuple funcTuple,
|
||||
std::size_t threadCount = 1)
|
||||
template <typename SigList, typename FTuple>
|
||||
void forMatchingSignatures(
|
||||
FTuple fTuple, const std::size_t threadCount = 1)
|
||||
{
|
||||
std::vector<std::vector<std::size_t> > multiMatchingEntities(
|
||||
SigList::size);
|
||||
BitsetType signatureBitsets[SigList::size];
|
||||
|
||||
// generate bitsets for each signature
|
||||
EC::Meta::forEach<SigList>(
|
||||
[this, &signatureBitsets] (auto signature) {
|
||||
signatureBitsets[
|
||||
EC::Meta::IndexOf<decltype(signature), SigList>{} ] =
|
||||
BitsetType::template generateBitset<decltype(signature)>();
|
||||
EC::Meta::forEachWithIndex<SigList>(
|
||||
[this, &signatureBitsets] (auto signature, const auto index) {
|
||||
signatureBitsets[index] =
|
||||
BitsetType::template generateBitset
|
||||
<decltype(signature)>();
|
||||
});
|
||||
|
||||
// find and store entities matching signatures
|
||||
|
@ -1255,8 +1257,8 @@ namespace EC
|
|||
else
|
||||
{
|
||||
std::vector<std::thread> threads(threadCount);
|
||||
std::mutex mutexes[SigList::size];
|
||||
std::size_t s = currentSize / threadCount;
|
||||
std::mutex sigsMutexes[SigList::size];
|
||||
for(std::size_t i = 0; i < threadCount; ++i)
|
||||
{
|
||||
std::size_t begin = s * i;
|
||||
|
@ -1270,30 +1272,28 @@ namespace EC
|
|||
end = s * (i + 1);
|
||||
}
|
||||
threads[i] = std::thread(
|
||||
[this, &signatureBitsets, &multiMatchingEntities,
|
||||
&sigsMutexes]
|
||||
(std::size_t begin, std::size_t end) {
|
||||
for(std::size_t eid = begin; eid < end; ++eid)
|
||||
[this, &mutexes, &multiMatchingEntities, &signatureBitsets]
|
||||
(std::size_t begin, std::size_t end)
|
||||
{
|
||||
if(!isAlive(eid))
|
||||
for(std::size_t j = begin; j < end; ++j)
|
||||
{
|
||||
if(!isAlive(j))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for(std::size_t i = 0; i < SigList::size; ++i)
|
||||
for(std::size_t k = 0; k < SigList::size; ++k)
|
||||
{
|
||||
if((signatureBitsets[i]
|
||||
& std::get<BitsetType>(entities[eid]))
|
||||
== signatureBitsets[i])
|
||||
if((signatureBitsets[k]
|
||||
& std::get<BitsetType>(entities[j]))
|
||||
== signatureBitsets[k])
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(
|
||||
sigsMutexes[i]);
|
||||
multiMatchingEntities[i].push_back(eid);
|
||||
|
||||
mutexes[k]);
|
||||
multiMatchingEntities[k].push_back(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
begin, end);
|
||||
}, begin, end);
|
||||
}
|
||||
for(std::size_t i = 0; i < threadCount; ++i)
|
||||
{
|
||||
|
@ -1302,26 +1302,24 @@ namespace EC
|
|||
}
|
||||
|
||||
// call functions on matching entities
|
||||
EC::Meta::forEach<SigList>(
|
||||
[this, &multiMatchingEntities, &funcTuple, &threadCount]
|
||||
(auto signature) {
|
||||
EC::Meta::forEachDoubleTuple(
|
||||
EC::Meta::Morph<SigList, std::tuple<> >{},
|
||||
fTuple,
|
||||
[this, &multiMatchingEntities, &threadCount]
|
||||
(auto sig, auto func, auto index)
|
||||
{
|
||||
using SignatureComponents =
|
||||
typename EC::Meta::Matching<
|
||||
decltype(signature), ComponentsList>::type;
|
||||
decltype(sig), ComponentsList>::type;
|
||||
using Helper =
|
||||
EC::Meta::Morph<
|
||||
SignatureComponents,
|
||||
ForMatchingSignatureHelper<> >;
|
||||
using Index = EC::Meta::IndexOf<decltype(signature),
|
||||
SigList>;
|
||||
constexpr std::size_t index = Index{};
|
||||
if(threadCount <= 1)
|
||||
{
|
||||
for(auto iter = multiMatchingEntities[index].begin();
|
||||
iter != multiMatchingEntities[index].end(); ++iter)
|
||||
for(const auto& id : multiMatchingEntities[index])
|
||||
{
|
||||
Helper::call(*iter, *this,
|
||||
std::get<index>(funcTuple));
|
||||
Helper::call(id, *this, func);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -1342,13 +1340,16 @@ namespace EC
|
|||
end = s * (i + 1);
|
||||
}
|
||||
threads[i] = std::thread(
|
||||
[this, &multiMatchingEntities, &funcTuple]
|
||||
[this, &multiMatchingEntities, &index, &func]
|
||||
(std::size_t begin, std::size_t end)
|
||||
{
|
||||
for(std::size_t j = begin; j < end; ++j)
|
||||
for(std::size_t j = begin; j < end;
|
||||
++j)
|
||||
{
|
||||
Helper::call(multiMatchingEntities[index][j],
|
||||
*this, std::get<index>(funcTuple));
|
||||
Helper::call(
|
||||
multiMatchingEntities[index][j],
|
||||
*this,
|
||||
func);
|
||||
}
|
||||
}, begin, end);
|
||||
}
|
||||
|
@ -1357,7 +1358,8 @@ namespace EC
|
|||
threads[i].join();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -1400,8 +1402,8 @@ namespace EC
|
|||
the order of entities called is not guaranteed. Otherwise entities
|
||||
will be called in consecutive order by their ID.
|
||||
*/
|
||||
template <typename SigList, typename FuncTuple>
|
||||
void forMatchingSignaturesPtr(FuncTuple funcTuple,
|
||||
template <typename SigList, typename FTuple>
|
||||
void forMatchingSignaturesPtr(FTuple fTuple,
|
||||
std::size_t threadCount = 1)
|
||||
{
|
||||
std::vector<std::vector<std::size_t> > multiMatchingEntities(
|
||||
|
@ -1409,11 +1411,11 @@ namespace EC
|
|||
BitsetType signatureBitsets[SigList::size];
|
||||
|
||||
// generate bitsets for each signature
|
||||
EC::Meta::forEach<SigList>(
|
||||
[this, &signatureBitsets] (auto signature) {
|
||||
signatureBitsets[
|
||||
EC::Meta::IndexOf<decltype(signature), SigList>{} ] =
|
||||
BitsetType::template generateBitset<decltype(signature)>();
|
||||
EC::Meta::forEachWithIndex<SigList>(
|
||||
[this, &signatureBitsets] (auto signature, const auto index) {
|
||||
signatureBitsets[index] =
|
||||
BitsetType::template generateBitset
|
||||
<decltype(signature)>();
|
||||
});
|
||||
|
||||
// find and store entities matching signatures
|
||||
|
@ -1439,8 +1441,8 @@ namespace EC
|
|||
else
|
||||
{
|
||||
std::vector<std::thread> threads(threadCount);
|
||||
std::mutex mutexes[SigList::size];
|
||||
std::size_t s = currentSize / threadCount;
|
||||
std::mutex sigsMutexes[SigList::size];
|
||||
for(std::size_t i = 0; i < threadCount; ++i)
|
||||
{
|
||||
std::size_t begin = s * i;
|
||||
|
@ -1454,30 +1456,28 @@ namespace EC
|
|||
end = s * (i + 1);
|
||||
}
|
||||
threads[i] = std::thread(
|
||||
[this, &signatureBitsets, &multiMatchingEntities,
|
||||
&sigsMutexes]
|
||||
(std::size_t begin, std::size_t end) {
|
||||
for(std::size_t eid = begin; eid < end; ++eid)
|
||||
[this, &mutexes, &multiMatchingEntities, &signatureBitsets]
|
||||
(std::size_t begin, std::size_t end)
|
||||
{
|
||||
if(!isAlive(eid))
|
||||
for(std::size_t j = begin; j < end; ++j)
|
||||
{
|
||||
if(!isAlive(j))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for(std::size_t i = 0; i < SigList::size; ++i)
|
||||
for(std::size_t k = 0; k < SigList::size; ++k)
|
||||
{
|
||||
if((signatureBitsets[i]
|
||||
& std::get<BitsetType>(entities[eid]))
|
||||
== signatureBitsets[i])
|
||||
if((signatureBitsets[k]
|
||||
& std::get<BitsetType>(entities[j]))
|
||||
== signatureBitsets[k])
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(
|
||||
sigsMutexes[i]);
|
||||
multiMatchingEntities[i].push_back(eid);
|
||||
|
||||
mutexes[k]);
|
||||
multiMatchingEntities[k].push_back(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
begin, end);
|
||||
}, begin, end);
|
||||
}
|
||||
for(std::size_t i = 0; i < threadCount; ++i)
|
||||
{
|
||||
|
@ -1486,26 +1486,24 @@ namespace EC
|
|||
}
|
||||
|
||||
// call functions on matching entities
|
||||
EC::Meta::forEach<SigList>(
|
||||
[this, &multiMatchingEntities, &funcTuple, &threadCount]
|
||||
(auto signature) {
|
||||
EC::Meta::forEachDoubleTuple(
|
||||
EC::Meta::Morph<SigList, std::tuple<> >{},
|
||||
fTuple,
|
||||
[this, &multiMatchingEntities, &threadCount]
|
||||
(auto sig, auto func, auto index)
|
||||
{
|
||||
using SignatureComponents =
|
||||
typename EC::Meta::Matching<
|
||||
decltype(signature), ComponentsList>::type;
|
||||
decltype(sig), ComponentsList>::type;
|
||||
using Helper =
|
||||
EC::Meta::Morph<
|
||||
SignatureComponents,
|
||||
ForMatchingSignatureHelper<> >;
|
||||
using Index = EC::Meta::IndexOf<decltype(signature),
|
||||
SigList>;
|
||||
constexpr std::size_t index = Index{};
|
||||
if(threadCount <= 1)
|
||||
{
|
||||
for(auto iter = multiMatchingEntities[index].begin();
|
||||
iter != multiMatchingEntities[index].end(); ++iter)
|
||||
for(const auto& id : multiMatchingEntities[index])
|
||||
{
|
||||
Helper::callPtr(*iter, *this,
|
||||
std::get<index>(funcTuple));
|
||||
Helper::callPtr(id, *this, func);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -1526,13 +1524,16 @@ namespace EC
|
|||
end = s * (i + 1);
|
||||
}
|
||||
threads[i] = std::thread(
|
||||
[this, &multiMatchingEntities, &funcTuple]
|
||||
[this, &multiMatchingEntities, &index, &func]
|
||||
(std::size_t begin, std::size_t end)
|
||||
{
|
||||
for(std::size_t j = begin; j < end; ++j)
|
||||
for(std::size_t j = begin; j < end;
|
||||
++j)
|
||||
{
|
||||
Helper::callPtr(multiMatchingEntities[index][j],
|
||||
*this, std::get<index>(funcTuple));
|
||||
Helper::callPtr(
|
||||
multiMatchingEntities[index][j],
|
||||
*this,
|
||||
func);
|
||||
}
|
||||
}, begin, end);
|
||||
}
|
||||
|
@ -1541,7 +1542,8 @@ namespace EC
|
|||
threads[i].join();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
45
src/EC/Meta/ForEachDoubleTuple.hpp
Normal file
45
src/EC/Meta/ForEachDoubleTuple.hpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
|
||||
// This work derives from Vittorio Romeo's code used for cppcon 2015 licensed
|
||||
// under the Academic Free License.
|
||||
// His code is available here: https://github.com/SuperV1234/cppcon2015
|
||||
|
||||
|
||||
#ifndef EC_META_FOR_EACH_DOUBLE_TUPLE_HPP
|
||||
#define EC_META_FOR_EACH_DOUBLE_TUPLE_HPP
|
||||
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include "Morph.hpp"
|
||||
|
||||
namespace EC
|
||||
{
|
||||
namespace Meta
|
||||
{
|
||||
template <typename Function, typename TupleFirst,
|
||||
typename TupleSecond, std::size_t... Indices>
|
||||
constexpr void forEachDoubleTupleHelper(
|
||||
Function&& function, TupleFirst t0, TupleSecond t1,
|
||||
std::index_sequence<Indices...>)
|
||||
{
|
||||
return (void)std::initializer_list<int>{(function(
|
||||
std::get<Indices>(t0), std::get<Indices>(t1), Indices), 0)...};
|
||||
}
|
||||
|
||||
template <typename TupleFirst, typename TupleSecond, typename Function>
|
||||
constexpr void forEachDoubleTuple(
|
||||
TupleFirst&& t0, TupleSecond&& t1, Function&& function)
|
||||
{
|
||||
using TTupleSize = std::tuple_size<TupleFirst>;
|
||||
using IndexSeq = std::make_index_sequence<TTupleSize::value>;
|
||||
|
||||
return forEachDoubleTupleHelper(
|
||||
std::forward<Function>(function),
|
||||
std::forward<TupleFirst>(t0),
|
||||
std::forward<TupleSecond>(t1),
|
||||
IndexSeq{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
40
src/EC/Meta/ForEachWithIndex.hpp
Normal file
40
src/EC/Meta/ForEachWithIndex.hpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
|
||||
// This work derives from Vittorio Romeo's code used for cppcon 2015 licensed
|
||||
// under the Academic Free License.
|
||||
// His code is available here: https://github.com/SuperV1234/cppcon2015
|
||||
|
||||
|
||||
#ifndef EC_META_FOR_EACH_WITH_INDEX_HPP
|
||||
#define EC_META_FOR_EACH_WITH_INDEX_HPP
|
||||
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include "Morph.hpp"
|
||||
|
||||
namespace EC
|
||||
{
|
||||
namespace Meta
|
||||
{
|
||||
template <typename Function, typename TTuple, std::size_t... Indices>
|
||||
constexpr void forEachWithIndexHelper(
|
||||
Function&& function, TTuple tuple, std::index_sequence<Indices...>)
|
||||
{
|
||||
return (void)std::initializer_list<int>{(function(std::move(
|
||||
std::get<Indices>(tuple)), Indices), 0)...};
|
||||
}
|
||||
|
||||
template <typename TTypeList, typename Function>
|
||||
constexpr void forEachWithIndex(Function&& function)
|
||||
{
|
||||
using TTuple = EC::Meta::Morph<TTypeList, std::tuple<> >;
|
||||
using TTupleSize = std::tuple_size<TTuple>;
|
||||
using IndexSeq = std::make_index_sequence<TTupleSize::value>;
|
||||
|
||||
return forEachWithIndexHelper(
|
||||
std::forward<Function>(function), TTuple{}, IndexSeq{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -12,5 +12,7 @@
|
|||
#include "IndexOf.hpp"
|
||||
#include "Morph.hpp"
|
||||
#include "ForEach.hpp"
|
||||
#include "ForEachWithIndex.hpp"
|
||||
#include "ForEachDoubleTuple.hpp"
|
||||
#include "Matching.hpp"
|
||||
|
||||
|
|
|
@ -789,6 +789,35 @@ TEST(EC, ForMatchingSignatures)
|
|||
EXPECT_EQ(13, manager.getEntityData<C0>(eid).y);
|
||||
}
|
||||
}
|
||||
|
||||
// test duplicate signatures
|
||||
manager.forMatchingSignatures<TypeList<
|
||||
TypeList<C0, C1>,
|
||||
TypeList<C0, C1> > >(
|
||||
std::make_tuple(
|
||||
[] (std::size_t eid, C0& c0, C1& c1) {
|
||||
c0.x = 9999;
|
||||
c0.y = 9999;
|
||||
c1.vx = 9999;
|
||||
c1.vy = 9999;
|
||||
},
|
||||
[] (std::size_t eid, C0& c0, C1& c1) {
|
||||
c0.x = 10000;
|
||||
c0.y = 10000;
|
||||
c1.vx = 10000;
|
||||
c1.vy = 10000;
|
||||
}
|
||||
));
|
||||
for(auto id : e)
|
||||
{
|
||||
if(id != first && id != last)
|
||||
{
|
||||
EXPECT_EQ(10000, manager.getEntityData<C0>(id).x);
|
||||
EXPECT_EQ(10000, manager.getEntityData<C0>(id).y);
|
||||
EXPECT_EQ(10000, manager.getEntityData<C1>(id).vx);
|
||||
EXPECT_EQ(10000, manager.getEntityData<C1>(id).vy);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
TEST(EC, forMatchingPtrs)
|
||||
|
@ -899,5 +928,36 @@ TEST(EC, forMatchingPtrs)
|
|||
c.y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// test duplicate signatures
|
||||
const auto setTo9999 = [] (std::size_t eid, C0& c0, C1& c1) {
|
||||
c0.x = 9999;
|
||||
c0.y = 9999;
|
||||
c1.vx = 9999;
|
||||
c1.vy = 9999;
|
||||
};
|
||||
const auto setTo10000 = [] (std::size_t eid, C0& c0, C1& c1) {
|
||||
c0.x = 10000;
|
||||
c0.y = 10000;
|
||||
c1.vx = 10000;
|
||||
c1.vy = 10000;
|
||||
};
|
||||
manager.forMatchingSignaturesPtr<TypeList<
|
||||
TypeList<C0, C1>,
|
||||
TypeList<C0, C1> > >(
|
||||
std::make_tuple(
|
||||
&setTo9999,
|
||||
&setTo10000
|
||||
));
|
||||
for(auto id : e)
|
||||
{
|
||||
if(id != first && id != last)
|
||||
{
|
||||
EXPECT_EQ(10000, manager.getEntityData<C0>(id).x);
|
||||
EXPECT_EQ(10000, manager.getEntityData<C0>(id).y);
|
||||
EXPECT_EQ(10000, manager.getEntityData<C1>(id).vx);
|
||||
EXPECT_EQ(10000, manager.getEntityData<C1>(id).vy);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue