]> git.seodisparate.com - EntityComponentMetaSystem/commitdiff
API change: context/userdata provided to functions
authorStephen Seo <seo.disparate@gmail.com>
Wed, 8 Aug 2018 07:52:12 +0000 (16:52 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Wed, 8 Aug 2018 07:52:12 +0000 (16:52 +0900)
All functions called by EC/Manager now accept an additional parameter
as a void* to support user-provided data (or context) for functions
(useful when the function is not a lambda function and doesn't have any
other data stored with it).

src/EC/Manager.hpp
src/test/ECTest.cpp

index bd15b083ea690c7a6cd6b627b3baefff7beafe7a..c8b8a7130a09fc411656ad805525df384ffd6bf8 100644 (file)
@@ -491,10 +491,12 @@ namespace EC
             static void call(
                 const std::size_t& entityID,
                 CType& ctype,
-                Function&& function)
+                Function&& function,
+                void* context = nullptr)
             {
                 function(
                     entityID,
+                    context,
                     ctype.template getEntityData<Types>(entityID)...
                 );
             }
@@ -503,10 +505,12 @@ namespace EC
             static void callPtr(
                 const std::size_t& entityID,
                 CType& ctype,
-                Function* function)
+                Function* function,
+                void* context = nullptr)
             {
                 (*function)(
                     entityID,
+                    context,
                     ctype.template getEntityData<Types>(entityID)...
                 );
             }
@@ -515,24 +519,28 @@ namespace EC
             void callInstance(
                 const std::size_t& entityID,
                 CType& ctype,
-                Function&& function) const
+                Function&& function,
+                void* context = nullptr) const
             {
                 ForMatchingSignatureHelper<Types...>::call(
                     entityID,
                     ctype,
-                    std::forward<Function>(function));
+                    std::forward<Function>(function),
+                    context);
             }
 
             template <typename CType, typename Function>
             void callInstancePtr(
                 const std::size_t& entityID,
                 CType& ctype,
-                Function* function) const
+                Function* function,
+                void* context = nullptr) const
             {
                 ForMatchingSignatureHelper<Types...>::callPtr(
                     entityID,
                     ctype,
-                    function);
+                    function,
+                    context);
             }
         };
 
@@ -542,24 +550,33 @@ namespace EC
                 Signature.
 
             The function object given to this function must accept std::size_t
-            as its first parameter and Component pointers for the rest of the
-            parameters. Tags specified in the Signature are only used as
-            filters and will not be given as a parameter to the function.
-
-            The second parameter is default 1 (not multi-threaded). If the
-            second parameter threadCount is set to a value greater than 1, then
-            threadCount threads will be used.
-            Note that multi-threading is based on splitting the task of calling
-            the function across sections of entities. Thus if there are only
-            a small amount of entities in the manager, then using multiple
-            threads may not have as great of a speed-up.
+            as its first parameter, void* as its second parameter, and Component
+            pointers for the rest of the parameters. Tags specified in the
+            Signature are only used as filters and will not be given as a
+            parameter to the function.
+
+            The second parameter is default nullptr and will be passed to the
+            function call as the second parameter as a means of providing
+            context (useful when the function is not a lambda function). The
+            third parameter is default 1 (not multi-threaded). If the third 
+            parameter threadCount is set to a value greater than 1, then
+            threadCount threads will be used.  Note that multi-threading is
+            based on splitting the task of calling the function across sections
+            of entities. Thus if there are only a small amount of entities in
+            the manager, then using multiple threads may not have as great of a
+            speed-up.
 
             Example:
             \code{.cpp}
-                manager.forMatchingSignature<TypeList<C0, C1, T0>>([] (
-                    std::size_t ID, C0* component0, C1* component1) {
+                Context c; // some class/struct with data
+                manager.forMatchingSignature<TypeList<C0, C1, T0>>([]
+                    (std::size_t ID,
+                    void* context,
+                    C0* component0, C1* component1)
+                {
                     // Lambda function contents here
                 },
+                &c, // "Context" object passed to the function
                 4 // four threads
                 );
             \endcode
@@ -568,6 +585,7 @@ namespace EC
         */
         template <typename Signature, typename Function>
         void forMatchingSignature(Function&& function,
+            void* context = nullptr,
             std::size_t threadCount = 1)
         {
             using SignatureComponents =
@@ -592,7 +610,7 @@ namespace EC
                         == signatureBitset)
                     {
                         Helper::call(i, *this,
-                            std::forward<Function>(function));
+                            std::forward<Function>(function), context);
                     }
                 }
             }
@@ -612,7 +630,8 @@ namespace EC
                     {
                         end = s * (i + 1);
                     }
-                    threads[i] = std::thread([this, &function, &signatureBitset]
+                    threads[i] = std::thread(
+                        [this, &function, &signatureBitset, &context]
                             (std::size_t begin,
                             std::size_t end) {
                         for(std::size_t i = begin; i < end; ++i)
@@ -627,7 +646,7 @@ namespace EC
                                 == signatureBitset)
                             {
                                 Helper::call(i, *this,
-                                    std::forward<Function>(function));
+                                    std::forward<Function>(function), context);
                             }
                         }
                     },
@@ -646,26 +665,35 @@ namespace EC
                 Signature.
 
             The function pointer given to this function must accept std::size_t
-            as its first parameter and Component pointers for the rest of the
-            parameters. Tags specified in the Signature are only used as
-            filters and will not be given as a parameter to the function.
-
-            The second parameter is default 1 (not multi-threaded). If the
-            second parameter threadCount is set to a value greater than 1, then
-            threadCount threads will be used.
-            Note that multi-threading is based on splitting the task of calling
-            the function across sections of entities. Thus if there are only
-            a small amount of entities in the manager, then using multiple
-            threads may not have as great of a speed-up.
+            as its first parameter, void* as its second parameter,  and
+            Component pointers for the rest of the parameters. Tags specified in
+            the Signature are only used as filters and will not be given as a
+            parameter to the function.
+
+            The second parameter is default nullptr and will be passed to the
+            function call as the second parameter as a means of providing
+            context (useful when the function is not a lambda function). The
+            third parameter is default 1 (not multi-threaded). If the third
+            parameter threadCount is set to a value greater than 1, then
+            threadCount threads will be used. Note that multi-threading is based
+            on splitting the task of calling the function across sections of
+            entities. Thus if there are only a small amount of entities in the
+            manager, then using multiple threads may not have as great of a
+            speed-up.
 
             Example:
             \code{.cpp}
-                auto function = [] (std::size_t ID, C0* component0,
-                    C1* component1) {
+                Context c; // some class/struct with data
+                auto function = []
+                    (std::size_t ID,
+                    void* context,
+                    C0* component0, C1* component1)
+                {
                     // Lambda function contents here
                 };
                 manager.forMatchingSignaturePtr<TypeList<C0, C1, T0>>(
                     &function, // ptr
+                    &c, // "Context" object passed to the function
                     4 // four threads
                 );
             \endcode
@@ -674,6 +702,7 @@ namespace EC
         */
         template <typename Signature, typename Function>
         void forMatchingSignaturePtr(Function* function,
+            void* context = nullptr,
             std::size_t threadCount = 1)
         {
             using SignatureComponents =
@@ -697,7 +726,7 @@ namespace EC
                     if((signatureBitset & std::get<BitsetType>(entities[i]))
                         == signatureBitset)
                     {
-                        Helper::callPtr(i, *this, function);
+                        Helper::callPtr(i, *this, function, context);
                     }
                 }
             }
@@ -717,7 +746,8 @@ namespace EC
                     {
                         end = s * (i + 1);
                     }
-                    threads[i] = std::thread([this, &function, &signatureBitset]
+                    threads[i] = std::thread(
+                        [this, &function, &signatureBitset, &context]
                             (std::size_t begin,
                             std::size_t end) {
                         for(std::size_t i = begin; i < end; ++i)
@@ -731,7 +761,7 @@ namespace EC
                                     & std::get<BitsetType>(entities[i]))
                                 == signatureBitset)
                             {
-                                Helper::callPtr(i, *this, function);
+                                Helper::callPtr(i, *this, function, context);
                             }
                         }
                     },
@@ -750,9 +780,11 @@ namespace EC
     private:
         std::unordered_map<std::size_t, std::tuple<
             BitsetType,
+            void*,
             std::function<void(
                 std::size_t,
-                std::vector<std::size_t>)> > >
+                std::vector<std::size_t>,
+                void*)> > >
             forMatchingFunctions;
         std::size_t functionIndex = 0;
 
@@ -775,10 +807,16 @@ namespace EC
             std::size_t). Calling clearForMatchingFunctions() will reset this
             counter to zero.
 
+            Note that the context pointer provided here (default nullptr) will
+            be provided to the stored function when called.
+
             Example:
             \code{.cpp}
-                manager.addForMatchingFunction<TypeList<C0, C1, T0>>([] (
-                    std::size_t ID, C0* component0, C1* component1) {
+                manager.addForMatchingFunction<TypeList<C0, C1, T0>>([]
+                    (std::size_t ID,
+                    void* context,
+                    C0* component0, C1* component1)
+                {
                     // Lambda function contents here
                 });
 
@@ -795,7 +833,9 @@ namespace EC
                 or calling with callForMatchingFunction().
         */
         template <typename Signature, typename Function>
-        std::size_t addForMatchingFunction(Function&& function)
+        std::size_t addForMatchingFunction(
+            Function&& function,
+            void* context = nullptr)
         {
             while(forMatchingFunctions.find(functionIndex)
                 != forMatchingFunctions.end())
@@ -818,9 +858,11 @@ namespace EC
                 functionIndex,
                 std::make_tuple(
                     signatureBitset,
+                    context,
                     [function, helper, this] 
-                    (std::size_t threadCount,
-                        std::vector<std::size_t> matching)
+                        (std::size_t threadCount,
+                        std::vector<std::size_t> matching,
+                        void* context)
                 {
                     if(threadCount <= 1)
                     {
@@ -828,7 +870,8 @@ namespace EC
                         {
                             if(isAlive(eid))
                             {
-                                helper.callInstancePtr(eid, *this, &function);
+                                helper.callInstancePtr(
+                                    eid, *this, &function, context);
                             }
                         }
                     }
@@ -849,14 +892,15 @@ namespace EC
                                 end = s * (i + 1);
                             }
                             threads[i] = std::thread(
-                                [this, &function, &helper]
+                                [this, &function, &helper, &context]
                                     (std::size_t begin,
                                     std::size_t end) {
                                 for(std::size_t i = begin; i < end; ++i)
                                 {
                                     if(isAlive(i))
                                     {
-                                        helper.callInstancePtr(i, *this, &function);
+                                        helper.callInstancePtr(
+                                            i, *this, &function, context);
                                     }
                                 }
                             },
@@ -950,8 +994,8 @@ namespace EC
         /*!
             \brief Call all stored functions.
 
-            A second parameter can be optionally used to specify the number
-            of threads to use when calling the functions. Otherwise, this
+            The first (and only) parameter can be optionally used to specify the
+            number of threads to use when calling the functions. Otherwise, this
             function is by default not multi-threaded.
             Note that multi-threading is based on splitting the task of calling
             the functions across sections of entities. Thus if there are only
@@ -960,8 +1004,10 @@ namespace EC
 
             Example:
             \code{.cpp}
-                manager.addForMatchingFunction<TypeList<C0, C1, T0>>([] (
-                    std::size_t ID, C0* component0, C1* component1) {
+                manager.addForMatchingFunction<TypeList<C0, C1, T0>>([]
+                    (std::size_t ID,
+                    void* context,
+                    C0* component0, C1* component1) {
                     // Lambda function contents here
                 });
 
@@ -993,7 +1039,8 @@ namespace EC
                 iter != forMatchingFunctions.end();
                 ++iter)
             {
-                std::get<1>(iter->second)(threadCount, matching[i++]);
+                std::get<2>(iter->second)(
+                    threadCount, matching[i++], std::get<1>(iter->second));
             }
         }
 
@@ -1012,7 +1059,7 @@ namespace EC
             \code{.cpp}
                 std::size_t id =
                     manager.addForMatchingFunction<TypeList<C0, C1, T0>>(
-                        [] (std::size_t ID, C0* c0, C1* c1) {
+                        [] (std::size_t ID, void* context, C0* c0, C1* c1) {
                     // Lambda function contents here
                 });
 
@@ -1036,7 +1083,8 @@ namespace EC
             std::vector<std::vector<std::size_t> > matching =
                 getMatchingEntities(std::vector<BitsetType*>{
                     &std::get<BitsetType>(iter->second)}, threadCount);
-            std::get<1>(iter->second)(threadCount, matching[0]);
+            std::get<2>(iter->second)(
+                threadCount, matching[0], std::get<1>(iter->second));
             return true;
         }
 
@@ -1047,8 +1095,11 @@ namespace EC
 
             Example:
             \code{.cpp}
-                manager.addForMatchingFunction<TypeList<C0, C1, T0>>([] (
-                    std::size_t ID, C0* component0, C1* component1) {
+                manager.addForMatchingFunction<TypeList<C0, C1, T0>>([]
+                    (std::size_t ID,
+                    void* context,
+                    C0* component0, C1* component1)
+                {
                     // Lambda function contents here
                 });
 
@@ -1170,6 +1221,22 @@ namespace EC
             return forMatchingFunctions.erase(index) == 1;
         }
 
+        /*!
+            \brief Sets the context pointer of a stored function
+
+            \return True if id is valid and context was updated
+        */
+        bool changeForMatchingFunctionContext(std::size_t id, void* context)
+        {
+            auto f = forMatchingFunctions.find(id);
+            if(f != forMatchingFunctions.end())
+            {
+                std::get<1>(f->second) = context;
+                return true;
+            }
+            return false;
+        }
+
         /*!
             \brief Call multiple functions with mulitple signatures on all
                 living entities.
@@ -1191,6 +1258,9 @@ namespace EC
             See the Unit Test of this function in src/test/ECTest.cpp for
             usage examples.
 
+            The second parameter (default nullptr) will be provided to every
+            function call as a void* (context).
+
             This function was created for the use case where there are many
             entities in the system which can cause multiple calls to
             forMatchingSignature to be slow due to the overhead of iterating
@@ -1209,7 +1279,9 @@ namespace EC
         */
         template <typename SigList, typename FTuple>
         void forMatchingSignatures(
-            FTuple fTuple, const std::size_t threadCount = 1)
+            FTuple fTuple,
+            void* context = nullptr,
+            const std::size_t threadCount = 1)
         {
             std::vector<std::vector<std::size_t> > multiMatchingEntities(
                 SigList::size);
@@ -1294,7 +1366,7 @@ namespace EC
             EC::Meta::forEachDoubleTuple(
                 EC::Meta::Morph<SigList, std::tuple<> >{},
                 fTuple,
-                [this, &multiMatchingEntities, &threadCount]
+                [this, &multiMatchingEntities, &threadCount, &context]
                 (auto sig, auto func, auto index)
                 {
                     using SignatureComponents =
@@ -1310,7 +1382,7 @@ namespace EC
                         {
                             if(isAlive(id))
                             {
-                                Helper::call(id, *this, func);
+                                Helper::call(id, *this, func, context);
                             }
                         }
                     }
@@ -1332,7 +1404,8 @@ namespace EC
                                 end = s * (i + 1);
                             }
                             threads[i] = std::thread(
-                            [this, &multiMatchingEntities, &index, &func]
+                            [this, &multiMatchingEntities, &index, &func,
+                                &context]
                             (std::size_t begin, std::size_t end)
                             {
                                 for(std::size_t j = begin; j < end;
@@ -1343,7 +1416,8 @@ namespace EC
                                         Helper::call(
                                             multiMatchingEntities[index][j],
                                             *this,
-                                            func);
+                                            func,
+                                            context);
                                     }
                                 }
                             }, begin, end);
@@ -1381,6 +1455,9 @@ namespace EC
             See the Unit Test of this function in src/test/ECTest.cpp for
             usage examples.
 
+            The second parameter (default nullptr) will be provided to every
+            function call as a void* (context).
+
             This function was created for the use case where there are many
             entities in the system which can cause multiple calls to
             forMatchingSignature to be slow due to the overhead of iterating
@@ -1399,6 +1476,7 @@ namespace EC
         */
         template <typename SigList, typename FTuple>
         void forMatchingSignaturesPtr(FTuple fTuple,
+            void* context = nullptr,
             std::size_t threadCount = 1)
         {
             std::vector<std::vector<std::size_t> > multiMatchingEntities(
@@ -1484,7 +1562,7 @@ namespace EC
             EC::Meta::forEachDoubleTuple(
                 EC::Meta::Morph<SigList, std::tuple<> >{},
                 fTuple,
-                [this, &multiMatchingEntities, &threadCount]
+                [this, &multiMatchingEntities, &threadCount, &context]
                 (auto sig, auto func, auto index)
                 {
                     using SignatureComponents =
@@ -1500,7 +1578,7 @@ namespace EC
                         {
                             if(isAlive(id))
                             {
-                                Helper::callPtr(id, *this, func);
+                                Helper::callPtr(id, *this, func, context);
                             }
                         }
                     }
@@ -1522,7 +1600,8 @@ namespace EC
                                 end = s * (i + 1);
                             }
                             threads[i] = std::thread(
-                            [this, &multiMatchingEntities, &index, &func]
+                            [this, &multiMatchingEntities, &index, &func,
+                                &context]
                             (std::size_t begin, std::size_t end)
                             {
                                 for(std::size_t j = begin; j < end;
@@ -1533,7 +1612,8 @@ namespace EC
                                         Helper::callPtr(
                                             multiMatchingEntities[index][j],
                                             *this,
-                                            func);
+                                            func,
+                                            context);
                                     }
                                 }
                             }, begin, end);
index 3167a525f35f3fdbc3fd7666f76aa85ea02613e5..1e76c51187ecd3ef7fcff864edb39c8f093c62ca 100644 (file)
@@ -58,6 +58,38 @@ struct Derived : public Base
 
 typedef std::unique_ptr<Base> TestPtr;
 
+struct Context
+{
+    int a, b;
+};
+
+void assignContextToC0(std::size_t /* id */, void* context, C0* c)
+{
+    Context* contextPtr = (Context*) context;
+    c->x = contextPtr->a;
+    c->y = contextPtr->b;
+}
+
+void assignC0ToContext(std::size_t /* id */, void* context, C0* c)
+{
+    Context* contextPtr = (Context*) context;
+    contextPtr->a = c->x;
+    contextPtr->b = c->y;
+}
+
+void setC0ToOneAndTwo(std::size_t /* id */, void* /* context */, C0* c)
+{
+    c->x = 1;
+    c->y = 2;
+}
+
+void setContextToThreeAndFour(std::size_t /* id */, void* context, C0* /* c */)
+{
+    Context* contextPtr = (Context*) context;
+    contextPtr->a = 3;
+    contextPtr->b = 4;
+}
+
 TEST(EC, Bitset)
 {
     {
@@ -101,12 +133,16 @@ TEST(EC, Manager)
         vel->vy = 1;
     }
 
-    auto posUpdate = [] (std::size_t id, C0* pos, C1* vel) {
+    auto posUpdate = []
+        (std::size_t /* id */, void* /* context */, C0* pos, C1* vel)
+    {
         pos->x += vel->vx;
         pos->y += vel->vy;
     };
 
-    auto updateTag = [] (std::size_t id, C0* pos, C1* vel) {
+    auto updateTag = []
+        (std::size_t /* id */, void* /* context */, C0* pos, C1* vel)
+    {
         pos->x = pos->y = vel->vx = vel->vy = 0;
     };
 
@@ -139,7 +175,7 @@ TEST(EC, Manager)
 
     std::size_t count = 0;
 
-    auto updateTagOnly = [&count] (std::size_t id) {
+    auto updateTagOnly = [&count] (std::size_t /* id */, void* /* context */) {
         std::cout << "UpdateTagOnly was run." << std::endl;
         ++count;
     };
@@ -167,7 +203,7 @@ TEST(EC, MoveComponentWithUniquePtr)
         int x = 0;
         int y = 0;
         manager.forMatchingSignature<EC::Meta::TypeList<C0Ptr> >([&x, &y]
-                (std::size_t eID, C0Ptr* ptr) {
+                (std::size_t /* id */, void* /* context */, C0Ptr* ptr) {
             x = (*ptr)->x;
             y = (*ptr)->y;
         });
@@ -186,7 +222,9 @@ TEST(EC, MoveComponentWithUniquePtr)
 
         int result = 0;
 
-        auto getResultFunction = [&result] (std::size_t eID, TestPtr* ptr) {
+        auto getResultFunction = [&result]
+            (std::size_t /* id */, void* /* context */, TestPtr* ptr)
+        {
             result = (*ptr)->getInt();
         };
 
@@ -246,31 +284,31 @@ TEST(EC, FunctionStorage)
     manager.addComponent<C3>(eid);
 
     auto f0index = manager.addForMatchingFunction<EC::Meta::TypeList<C0>>(
-            [] (std::size_t eid, C0* c0) {
+            [] (std::size_t /* id */, void* /* context */, C0* c0) {
         ++c0->x;
         ++c0->y;
     });
 
     auto f1index = manager.addForMatchingFunction<EC::Meta::TypeList<C0, C1>>(
-            [] (std::size_t eid, C0* c0, C1* c1) {
+            [] (std::size_t /* id */, void* /* context */, C0* c0, C1* c1) {
         c1->vx = c0->x + 10;
         c1->vy = c1->vy + c1->vx + c0->y + 10;
     });
 
     auto f2index = manager.addForMatchingFunction<EC::Meta::TypeList<C0>>(
-            [] (std::size_t eid, C0* c0) {
+            [] (std::size_t /* id */, void* /* context */, C0* c0) {
         c0->x = c0->y = 9999;
     });
 
     auto f3index = manager.addForMatchingFunction<EC::Meta::TypeList<C1>>(
-            [] (std::size_t eid, C1* c1) {
+            [] (std::size_t /* id */, void* /* context */, C1* c1) {
         c1->vx = c1->vy = 10000;
     });
 
     EXPECT_EQ(2, manager.removeSomeMatchingFunctions({f2index, f3index}));
 
     auto f4index = manager.addForMatchingFunction<EC::Meta::TypeList<C0>>(
-            [] (std::size_t eid, C0* c0) {
+            [] (std::size_t /* id */, void* /* context */, C0* c0) {
         c0->x = 999;
         c0->y = 888;
     });
@@ -281,7 +319,7 @@ TEST(EC, FunctionStorage)
     }
 
     auto f5index = manager.addForMatchingFunction<EC::Meta::TypeList<C0>>(
-            [] (std::size_t eid, C0* c0) {
+            [] (std::size_t /* id */, void* /* context */, C0* c0) {
         c0->x = 777;
         c0->y = 666;
     });
@@ -418,10 +456,11 @@ TEST(EC, MultiThreaded)
     }
 
     manager.forMatchingSignature<EC::Meta::TypeList<C0> >(
-        [] (const std::size_t& eid, C0* c) {
+        [] (const std::size_t& /* id */, void* /* context */, C0* c) {
             c->x = 1;
             c->y = 2;
         },
+        nullptr,
         2
     );
 
@@ -443,10 +482,11 @@ TEST(EC, MultiThreaded)
     }
 
     manager.forMatchingSignature<EC::Meta::TypeList<C0> >(
-        [] (const std::size_t& eid, C0* c) {
+        [] (const std::size_t& /* id */, void* /* context */, C0* c) {
             c->x = 3;
             c->y = 4;
         },
+        nullptr,
         8
     );
 
@@ -467,7 +507,7 @@ TEST(EC, MultiThreaded)
     }
 
     auto f0 = manager.addForMatchingFunction<EC::Meta::TypeList<C0> >(
-        [] (const std::size_t& eid, C0* c) {
+        [] (const std::size_t& /* id */, void* /* context */, C0* c) {
             c->x = 1;
             c->y = 2;
         }
@@ -482,7 +522,7 @@ TEST(EC, MultiThreaded)
     }
 
     auto f1 = manager.addForMatchingFunction<EC::Meta::TypeList<C0> >(
-        [] (const std::size_t& eid, C0* c) {
+        [] (const std::size_t& /* id */, void* /* context */, C0* c) {
             c->x = 3;
             c->y = 4;
         }
@@ -559,13 +599,13 @@ TEST(EC, ForMatchingSignatures)
         TypeList<TypeList<C0>, TypeList<C0, C1> >
     >(
         std::make_tuple(
-        [] (std::size_t eid, C0* c) {
+        [] (std::size_t /* id */, void* /* context */, C0* c) {
             EXPECT_EQ(c->x, 0);
             EXPECT_EQ(c->y, 0);
             c->x = 1;
             c->y = 1;
         },
-        [] (std::size_t eid, C0* c0, C1* c1) {
+        [] (std::size_t /* id */, void* /* context */, C0* c0, C1* c1) {
             EXPECT_EQ(c0->x, 1);
             EXPECT_EQ(c0->y, 1);
             EXPECT_EQ(c1->vx, 0);
@@ -612,7 +652,8 @@ TEST(EC, ForMatchingSignatures)
     >
     (
         std::make_tuple(
-        [&first, &last, &cx, &cy, &cxM, &cyM] (std::size_t eid, C0* c) {
+        [&first, &last, &cx, &cy, &cxM, &cyM]
+        (std::size_t eid, void* /* context */, C0* c) {
             if(eid != first && eid != last)
             {
                 {
@@ -641,7 +682,7 @@ TEST(EC, ForMatchingSignatures)
             }
         },
         [&c0x, &c0y, &c1vx, &c1vy, &c0xM, &c0yM, &c1vxM, &c1vyM]
-        (std::size_t eid, C0* c0, C1* c1) {
+        (std::size_t eid, void* /* context */, C0* c0, C1* c1) {
             {
                 std::lock_guard<std::mutex> guard(c0xM);
                 c0x.insert(std::make_pair(eid, c0->x));
@@ -665,6 +706,7 @@ TEST(EC, ForMatchingSignatures)
             c0->x = 1;
             c0->y = 2;
         }),
+        nullptr,
         3
     );
     
@@ -729,13 +771,13 @@ TEST(EC, ForMatchingSignatures)
         TypeList<C0, C1>,
         TypeList<C0, C1> > >(
         std::make_tuple(
-            [] (std::size_t eid, C0* c0, C1* c1) {
+            [] (std::size_t /* id */, void* /* context */, C0* c0, C1* c1) {
                 c0->x = 9999;
                 c0->y = 9999;
                 c1->vx = 9999;
                 c1->vy = 9999;
             },
-            [] (std::size_t eid, C0* c0, C1* c1) {
+            [] (std::size_t /* id */, void* /* context */, C0* c0, C1* c1) {
                 c0->x = 10000;
                 c0->y = 10000;
                 c1->vx = 10000;
@@ -785,14 +827,15 @@ TEST(EC, forMatchingPtrs)
         }
     }
 
-    const auto func0 = [] (std::size_t eid, C0* c0, C1* c1)
+    const auto func0 = []
+        (std::size_t /* id */, void* /* context */, C0* c0, C1* c1)
     {
         c0->x = 1;
         c0->y = 2;
         c1->vx = 3;
         c1->vy = 4;
     };
-    const auto func1 = [] (std::size_t eid, C0* c0)
+    const auto func1 = [] (std::size_t /* id */, void* /* context */, C0* c0)
     {
         c0->x = 11;
         c0->y = 12;
@@ -864,13 +907,17 @@ TEST(EC, forMatchingPtrs)
     }
 
     // test duplicate signatures
-    const auto setTo9999 = [] (std::size_t eid, C0* c0, C1* c1) {
+    const auto setTo9999 = []
+        (std::size_t /* id */, void* /* context */, 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) {
+    const auto setTo10000 = []
+        (std::size_t /* id */, void* /* context */, C0* c0, C1* c1)
+    {
         c0->x = 10000;
         c0->y = 10000;
         c1->vx = 10000;
@@ -895,3 +942,88 @@ TEST(EC, forMatchingPtrs)
     };
 }
 
+TEST(EC, context)
+{
+    EC::Manager<ListComponentsAll, ListTagsAll> manager;
+    auto e0 = manager.addEntity();
+    auto e1 = manager.addEntity();
+
+    manager.addComponent<C0>(e0, 1, 2);
+    manager.addComponent<C0>(e1, 3, 4);
+
+    Context c;
+    c.a = 2000;
+    c.b = 5432;
+
+    EXPECT_EQ(1, manager.getEntityData<C0>(e0)->x);
+    EXPECT_EQ(2, manager.getEntityData<C0>(e0)->y);
+    EXPECT_EQ(3, manager.getEntityData<C0>(e1)->x);
+    EXPECT_EQ(4, manager.getEntityData<C0>(e1)->y);
+
+    manager.forMatchingSignature<EC::Meta::TypeList<C0>>(assignContextToC0, &c);
+
+    EXPECT_EQ(2000, manager.getEntityData<C0>(e0)->x);
+    EXPECT_EQ(5432, manager.getEntityData<C0>(e0)->y);
+    EXPECT_EQ(2000, manager.getEntityData<C0>(e1)->x);
+    EXPECT_EQ(5432, manager.getEntityData<C0>(e1)->y);
+
+    c.a = 1111;
+    c.b = 2222;
+
+    using C0TL = EC::Meta::TypeList<C0>;
+    manager.forMatchingSignatures<EC::Meta::TypeList<C0TL, C0TL, C0TL>>(
+        std::make_tuple(
+            setC0ToOneAndTwo, assignContextToC0, setContextToThreeAndFour),
+        &c);
+    EXPECT_EQ(1111, manager.getEntityData<C0>(e0)->x);
+    EXPECT_EQ(2222, manager.getEntityData<C0>(e0)->y);
+    EXPECT_EQ(1111, manager.getEntityData<C0>(e1)->x);
+    EXPECT_EQ(2222, manager.getEntityData<C0>(e1)->y);
+
+    EXPECT_EQ(3, c.a);
+    EXPECT_EQ(4, c.b);
+
+    manager.forMatchingSignaturesPtr<EC::Meta::TypeList<C0TL, C0TL>>(
+        std::make_tuple(
+            &setC0ToOneAndTwo, &assignC0ToContext),
+        &c);
+
+    EXPECT_EQ(1, c.a);
+    EXPECT_EQ(2, c.b);
+
+    EXPECT_EQ(1, manager.getEntityData<C0>(e0)->x);
+    EXPECT_EQ(2, manager.getEntityData<C0>(e0)->y);
+    EXPECT_EQ(1, manager.getEntityData<C0>(e1)->x);
+    EXPECT_EQ(2, manager.getEntityData<C0>(e1)->y);
+
+    c.a = 1980;
+    c.b = 1990;
+
+    auto fid = manager.addForMatchingFunction<C0TL>(assignContextToC0, &c);
+
+    manager.callForMatchingFunction(fid);
+    EXPECT_EQ(1980, manager.getEntityData<C0>(e0)->x);
+    EXPECT_EQ(1990, manager.getEntityData<C0>(e0)->y);
+    EXPECT_EQ(1980, manager.getEntityData<C0>(e1)->x);
+    EXPECT_EQ(1990, manager.getEntityData<C0>(e1)->y);
+
+    c.a = 2000;
+    c.b = 2010;
+
+    manager.callForMatchingFunctions();
+    EXPECT_EQ(2000, manager.getEntityData<C0>(e0)->x);
+    EXPECT_EQ(2010, manager.getEntityData<C0>(e0)->y);
+    EXPECT_EQ(2000, manager.getEntityData<C0>(e1)->x);
+    EXPECT_EQ(2010, manager.getEntityData<C0>(e1)->y);
+
+    Context altC;
+    altC.a = 999;
+    altC.b = 1999;
+
+    manager.changeForMatchingFunctionContext(fid, &altC);
+    manager.callForMatchingFunctions();
+    EXPECT_EQ(999, manager.getEntityData<C0>(e0)->x);
+    EXPECT_EQ(1999, manager.getEntityData<C0>(e0)->y);
+    EXPECT_EQ(999, manager.getEntityData<C0>(e1)->x);
+    EXPECT_EQ(1999, manager.getEntityData<C0>(e1)->y);
+}