API change: context/userdata provided to functions

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).
This commit is contained in:
Stephen Seo 2018-08-08 16:52:12 +09:00
parent 2411125003
commit 6bf239a43b
2 changed files with 300 additions and 88 deletions

View 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.
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 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.
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.
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 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.
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);

View 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);
}