]> git.seodisparate.com - EntityComponentMetaSystem/commitdiff
Fix bug with duplicate signatures
authorStephen Seo <seo.disparate@gmail.com>
Wed, 15 Nov 2017 06:36:04 +0000 (15:36 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Wed, 15 Nov 2017 06:36:04 +0000 (15:36 +0900)
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.

src/EC/Manager.hpp
src/EC/Meta/ForEachDoubleTuple.hpp [new file with mode: 0644]
src/EC/Meta/ForEachWithIndex.hpp [new file with mode: 0644]
src/EC/Meta/Meta.hpp
src/test/ECTest.cpp

index 51a981fa49fc8280713f119b719fe4de72369026..064feaa31297885697c235560580eb530c5eda00 100644 (file)
@@ -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,94 +1272,94 @@ 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)
+                    {
+                        for(std::size_t j = begin; j < end; ++j)
                         {
-                            if(!isAlive(eid))
+                            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)
                 {
                     threads[i].join();
                 }
             }
-
+            
             // call functions on matching entities
-            EC::Meta::forEach<SigList>(
-            [this, &multiMatchingEntities, &funcTuple, &threadCount]
-            (auto signature) {
-                using SignatureComponents =
-                    typename EC::Meta::Matching<
-                        decltype(signature), 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)
+            EC::Meta::forEachDoubleTuple(
+                EC::Meta::Morph<SigList, std::tuple<> >{},
+                fTuple,
+                [this, &multiMatchingEntities, &threadCount]
+                (auto sig, auto func, auto index)
                 {
-                    for(auto iter = multiMatchingEntities[index].begin();
-                        iter != multiMatchingEntities[index].end(); ++iter)
+                    using SignatureComponents =
+                        typename EC::Meta::Matching<
+                            decltype(sig), ComponentsList>::type;
+                    using Helper =
+                        EC::Meta::Morph<
+                            SignatureComponents,
+                            ForMatchingSignatureHelper<> >;
+                    if(threadCount <= 1)
                     {
-                        Helper::call(*iter, *this,
-                            std::get<index>(funcTuple));
+                        for(const auto& id : multiMatchingEntities[index])
+                        {
+                            Helper::call(id, *this, func);
+                        }
                     }
-                }
-                else
-                {
-                    std::vector<std::thread> threads(threadCount);
-                    std::size_t s = multiMatchingEntities[index].size()
-                        / threadCount;
-                    for(std::size_t i = 0; i < threadCount; ++i)
+                    else
                     {
-                        std::size_t begin = s * i;
-                        std::size_t end;
-                        if(i == threadCount - 1)
+                        std::vector<std::thread> threads(threadCount);
+                        std::size_t s = multiMatchingEntities[index].size()
+                            / threadCount;
+                        for(std::size_t i = 0; i < threadCount; ++i)
                         {
-                            end = multiMatchingEntities[index].size();
+                            std::size_t begin = s * i;
+                            std::size_t end;
+                            if(i == threadCount - 1)
+                            {
+                                end = multiMatchingEntities[index].size();
+                            }
+                            else
+                            {
+                                end = s * (i + 1);
+                            }
+                            threads[i] = std::thread(
+                            [this, &multiMatchingEntities, &index, &func]
+                            (std::size_t begin, std::size_t end)
+                            {
+                                for(std::size_t j = begin; j < end;
+                                    ++j)
+                                {
+                                    Helper::call(
+                                        multiMatchingEntities[index][j],
+                                        *this,
+                                        func);
+                                }
+                            }, begin, end);
                         }
-                        else
+                        for(std::size_t i = 0; i < threadCount; ++i)
                         {
-                            end = s * (i + 1);
+                            threads[i].join();
                         }
-                        threads[i] = std::thread(
-                        [this, &multiMatchingEntities, &funcTuple]
-                        (std::size_t begin, std::size_t end)
-                        {
-                            for(std::size_t j = begin; j < end; ++j)
-                            {
-                                Helper::call(multiMatchingEntities[index][j],
-                                    *this, std::get<index>(funcTuple));
-                            }
-                        }, begin, end);
-                    }
-                    for(std::size_t i = 0; i < threadCount; ++i)
-                    {
-                        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,94 +1456,94 @@ 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)
+                    {
+                        for(std::size_t j = begin; j < end; ++j)
                         {
-                            if(!isAlive(eid))
+                            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)
                 {
                     threads[i].join();
                 }
             }
-
+            
             // call functions on matching entities
-            EC::Meta::forEach<SigList>(
-            [this, &multiMatchingEntities, &funcTuple, &threadCount]
-            (auto signature) {
-                using SignatureComponents =
-                    typename EC::Meta::Matching<
-                        decltype(signature), 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)
+            EC::Meta::forEachDoubleTuple(
+                EC::Meta::Morph<SigList, std::tuple<> >{},
+                fTuple,
+                [this, &multiMatchingEntities, &threadCount]
+                (auto sig, auto func, auto index)
                 {
-                    for(auto iter = multiMatchingEntities[index].begin();
-                        iter != multiMatchingEntities[index].end(); ++iter)
+                    using SignatureComponents =
+                        typename EC::Meta::Matching<
+                            decltype(sig), ComponentsList>::type;
+                    using Helper =
+                        EC::Meta::Morph<
+                            SignatureComponents,
+                            ForMatchingSignatureHelper<> >;
+                    if(threadCount <= 1)
                     {
-                        Helper::callPtr(*iter, *this,
-                            std::get<index>(funcTuple));
+                        for(const auto& id : multiMatchingEntities[index])
+                        {
+                            Helper::callPtr(id, *this, func);
+                        }
                     }
-                }
-                else
-                {
-                    std::vector<std::thread> threads(threadCount);
-                    std::size_t s = multiMatchingEntities[index].size()
-                        / threadCount;
-                    for(std::size_t i = 0; i < threadCount; ++i)
+                    else
                     {
-                        std::size_t begin = s * i;
-                        std::size_t end;
-                        if(i == threadCount - 1)
+                        std::vector<std::thread> threads(threadCount);
+                        std::size_t s = multiMatchingEntities[index].size()
+                            / threadCount;
+                        for(std::size_t i = 0; i < threadCount; ++i)
                         {
-                            end = multiMatchingEntities[index].size();
+                            std::size_t begin = s * i;
+                            std::size_t end;
+                            if(i == threadCount - 1)
+                            {
+                                end = multiMatchingEntities[index].size();
+                            }
+                            else
+                            {
+                                end = s * (i + 1);
+                            }
+                            threads[i] = std::thread(
+                            [this, &multiMatchingEntities, &index, &func]
+                            (std::size_t begin, std::size_t end)
+                            {
+                                for(std::size_t j = begin; j < end;
+                                    ++j)
+                                {
+                                    Helper::callPtr(
+                                        multiMatchingEntities[index][j],
+                                        *this,
+                                        func);
+                                }
+                            }, begin, end);
                         }
-                        else
+                        for(std::size_t i = 0; i < threadCount; ++i)
                         {
-                            end = s * (i + 1);
+                            threads[i].join();
                         }
-                        threads[i] = std::thread(
-                        [this, &multiMatchingEntities, &funcTuple]
-                        (std::size_t begin, std::size_t end)
-                        {
-                            for(std::size_t j = begin; j < end; ++j)
-                            {
-                                Helper::callPtr(multiMatchingEntities[index][j],
-                                    *this, std::get<index>(funcTuple));
-                            }
-                        }, begin, end);
-                    }
-                    for(std::size_t i = 0; i < threadCount; ++i)
-                    {
-                        threads[i].join();
                     }
                 }
-            });
+            );
         }
 
         /*!
diff --git a/src/EC/Meta/ForEachDoubleTuple.hpp b/src/EC/Meta/ForEachDoubleTuple.hpp
new file mode 100644 (file)
index 0000000..8e71ad6
--- /dev/null
@@ -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
+
diff --git a/src/EC/Meta/ForEachWithIndex.hpp b/src/EC/Meta/ForEachWithIndex.hpp
new file mode 100644 (file)
index 0000000..2c05f6f
--- /dev/null
@@ -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
+
index f7f8ca97aa701458f8e8d613b05dcf083dba808c..c64e45210d19eaaa246e8f6962958c4a083a379c 100644 (file)
@@ -12,5 +12,7 @@
 #include "IndexOf.hpp"
 #include "Morph.hpp"
 #include "ForEach.hpp"
+#include "ForEachWithIndex.hpp"
+#include "ForEachDoubleTuple.hpp"
 #include "Matching.hpp"
 
index ca084ffe853234ca067e5a59c19c8745c644ecf2..c5d8c9acdc0bab992572a8ce6bc3d5ddb9a6bad4 100644 (file)
@@ -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);
+        }
+    };
 }