Changed implementation of stored functions to map

An unordered_map stores functions, allowing for functions to be
removed by index, and bulk removals with filtering by index.

Also added reset(), which changes the state of the Manager to be
almost identical to a newly constructed one.
This commit is contained in:
Stephen Seo 2016-09-21 21:01:48 +09:00
parent 2e115cd7a2
commit 09a7546509
2 changed files with 198 additions and 20 deletions

View file

@ -14,6 +14,9 @@
#include <tuple> #include <tuple>
#include <utility> #include <utility>
#include <functional> #include <functional>
#include <map>
#include <set>
#include <unordered_set>
#include "Meta/Combine.hpp" #include "Meta/Combine.hpp"
#include "Meta/Matching.hpp" #include "Meta/Matching.hpp"
@ -427,7 +430,8 @@ namespace EC
} }
private: private:
std::vector<std::function<void()> > forMatchingFunctions; std::map<unsigned long long, std::function<void()> > forMatchingFunctions;
unsigned long long functionIndex = 0;
public: public:
/*! /*!
@ -440,6 +444,12 @@ namespace EC
The syntax for the Function is the same as with forMatchingSignature(). The syntax for the Function is the same as with forMatchingSignature().
Note that functions will be called in the same order they are inserted.
Old functions may be overwritten if there are more functions than
sizeof(unsigned long long) as they are stored in a map with
unsigned long long as the key (index).
Example: Example:
\code{.cpp} \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, C0& component0, C1& component1) {
@ -450,9 +460,13 @@ namespace EC
manager.clearForMatchingFunctions(); // remove all stored functions manager.clearForMatchingFunctions(); // remove all stored functions
\endcode \endcode
\return The index of the function, used for deletion with
deleteForMatchingFunction() or filtering with
clearSomeMatchingFunctions().
*/ */
template <typename Signature, typename Function> template <typename Signature, typename Function>
void addForMatchingFunction(Function&& function) unsigned long long addForMatchingFunction(Function&& function)
{ {
using SignatureComponents = typename EC::Meta::Matching<Signature, ComponentsList>::type; using SignatureComponents = typename EC::Meta::Matching<Signature, ComponentsList>::type;
using Helper = EC::Meta::Morph<SignatureComponents, ForMatchingSignatureHelper<> >; using Helper = EC::Meta::Morph<SignatureComponents, ForMatchingSignatureHelper<> >;
@ -460,7 +474,7 @@ namespace EC
Helper helper; Helper helper;
BitsetType signatureBitset = BitsetType::template generateBitset<Signature>(); BitsetType signatureBitset = BitsetType::template generateBitset<Signature>();
forMatchingFunctions.emplace_back( [function, signatureBitset, helper, this] () { forMatchingFunctions.emplace(std::make_pair(functionIndex, [function, signatureBitset, helper, this] () {
for(std::size_t i = 0; i < this->currentSize; ++i) for(std::size_t i = 0; i < this->currentSize; ++i)
{ {
if(!std::get<bool>(this->entities[i])) if(!std::get<bool>(this->entities[i]))
@ -472,7 +486,9 @@ namespace EC
helper.callInstance(i, *this, function); helper.callInstance(i, *this, function);
} }
} }
}); }));
return functionIndex++;
} }
/*! /*!
@ -493,13 +509,15 @@ namespace EC
{ {
for(auto functionIter = forMatchingFunctions.begin(); functionIter != forMatchingFunctions.end(); ++functionIter) for(auto functionIter = forMatchingFunctions.begin(); functionIter != forMatchingFunctions.end(); ++functionIter)
{ {
(*functionIter)(); functionIter->second();
} }
} }
/*! /*!
\brief Remove all stored functions. \brief Remove all stored functions.
Also resets the index counter of stored functions to 0.
Example: Example:
\code{.cpp} \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, C0& component0, C1& component1) {
@ -514,8 +532,128 @@ namespace EC
void clearForMatchingFunctions() void clearForMatchingFunctions()
{ {
forMatchingFunctions.clear(); forMatchingFunctions.clear();
functionIndex = 0;
} }
/*!
\brief Removes all functions that do not have the index specified
in argument "list".
Note this function is slower than the variant that uses a set
argument as all items in the List are traversed during
traversal through all entities to check if they are in the list.
Thus the complexity of this function is n^2.
*/
template <typename List>
void clearSomeMatchingFunctions(List list)
{
bool willErase = true;
for(auto functionIter = forMatchingFunctions.begin();
functionIter != forMatchingFunctions.end();
++functionIter)
{
willErase = true;
for(auto listIter = list.begin();
listIter != list.end();
++listIter)
{
if(functionIter->first == *listIter)
{
willErase = false;
break;
}
}
if(willErase)
{
functionIter = --(forMatchingFunctions.erase(functionIter));
}
}
}
/*!
\brief Removes all functions that do not have the index specified
in argument "list".
Note this function is slower than the variant that uses a set
argument as all items in the List are traversed during
traversal through all entities to check if they are in the list.
Thus the complexity of this function is n^2.
*/
void clearSomeMatchingFunctions(std::initializer_list<unsigned long long> list)
{
clearSomeMatchingFunctions<decltype(list)>(list);
}
private:
template <typename Set>
void clearSomeMatchingFunctionsWithSet(Set set)
{
for(auto functionIter = forMatchingFunctions.begin();
functionIter != forMatchingFunctions.end();
++functionIter)
{
if(set.find(functionIter->first) == set.end())
{
functionIter = --(forMatchingFunctions.erase(functionIter));
}
}
}
public:
/*!
\brief Removes all functions that do not have the index specified
in argument "set".
Note this function is faster than the variant that uses a list
argument as the set's implementation as a tree allows for
log(n) time checking of an index to the set. Thus the complexity
of this function is n*log(n).
*/
void clearSomeMatchingFunctions(std::set<unsigned long long> set)
{
clearSomeMatchingFunctionsWithSet<decltype(set)>(set);
}
/*!
\brief Removes all functions that do not have the index specified
in argument "set".
Note this function is faster than the variant that uses a list
argument as the unordered_set's implementation as a hash table
allows for constant time checking of an index to the set. Thus the
complexity of this function is n.
*/
void clearSomeMatchingFunctions(std::unordered_set<unsigned long long> set)
{
clearSomeMatchingFunctionsWithSet<decltype(set)>(set);
}
/*!
\brief Deletes the specified function.
The index of a function is returned from addForMatchingFunction()
so there is no other way to get the index of a function.
*/
void deleteForMatchingFunction(unsigned long long index)
{
forMatchingFunctions.erase(index);
}
/*!
\brief Resets the Manager, removing all entities.
Some data may persist but will be overwritten when new entities
are added. Thus, do not depend on data to persist after a call to
reset().
*/
void reset()
{
clearForMatchingFunctions();
currentSize = 0;
currentCapacity = 0;
resize(EC_INIT_ENTITIES_SIZE);
}
}; };
} }

View file

@ -246,17 +246,29 @@ TEST(EC, FunctionStorage)
{ {
EC::Manager<ListComponentsAll, ListTagsAll> manager; EC::Manager<ListComponentsAll, ListTagsAll> manager;
auto eid = manager.addEntity(); auto eid = manager.addEntity();
manager.addComponent<C0>(eid); manager.addComponent<C0>(eid, 0, 1);
manager.addComponent<C1>(eid); manager.addComponent<C1>(eid);
manager.addComponent<C2>(eid);
manager.addComponent<C3>(eid);
manager.addForMatchingFunction<EC::Meta::TypeList<C0>>( [] (std::size_t eid, C0& c0) { auto f0index = manager.addForMatchingFunction<EC::Meta::TypeList<C0>>( [] (std::size_t eid, C0& c0) {
c0.x = 1; ++c0.x;
c0.y = 2; ++c0.y;
}); });
manager.addForMatchingFunction<EC::Meta::TypeList<C0, C1>>( [] (std::size_t eid, C0& c0, C1& c1) { auto f1index = manager.addForMatchingFunction<EC::Meta::TypeList<C0, C1>>( [] (std::size_t eid, C0& c0, C1& c1) {
c1.vx = c0.x + 10; c1.vx = c0.x + 10;
c1.vy = c1.vx + c0.y + 10; c1.vy = c1.vy + c1.vx + c0.y + 10;
});
manager.addForMatchingFunction<EC::Meta::TypeList<>>(
[] (std::size_t eid) {
//derp 0
});
manager.addForMatchingFunction<EC::Meta::TypeList<>>(
[] (std::size_t eid) {
//derp 1
}); });
manager.callForMatchingFunctions(); manager.callForMatchingFunctions();
@ -264,29 +276,57 @@ TEST(EC, FunctionStorage)
{ {
auto c0 = manager.getEntityData<C0>(eid); auto c0 = manager.getEntityData<C0>(eid);
EXPECT_EQ(c0.x, 1); EXPECT_EQ(1, c0.x);
EXPECT_EQ(c0.y, 2); EXPECT_EQ(2, c0.y);
auto c1 = manager.getEntityData<C1>(eid); auto c1 = manager.getEntityData<C1>(eid);
EXPECT_EQ(c1.vx, 11); EXPECT_EQ(11, c1.vx);
EXPECT_EQ(c1.vy, 23); EXPECT_EQ(23, c1.vy);
}
manager.clearSomeMatchingFunctions({f1index});
{
std::vector<unsigned long long> indices{f1index};
manager.clearSomeMatchingFunctions(indices);
}
{
std::set<unsigned long long> indices{f1index};
manager.clearSomeMatchingFunctions(indices);
}
{
std::unordered_set<unsigned long long> indices{f1index};
manager.clearSomeMatchingFunctions(indices);
}
manager.callForMatchingFunctions();
{
auto c0 = manager.getEntityData<C0>(eid);
EXPECT_EQ(1, c0.x);
EXPECT_EQ(2, c0.y);
auto c1 = manager.getEntityData<C1>(eid);
EXPECT_EQ(11, c1.vx);
EXPECT_EQ(46, c1.vy);
} }
manager.clearForMatchingFunctions(); manager.clearForMatchingFunctions();
manager.callForMatchingFunctions(); manager.callForMatchingFunctions();
{ {
auto c0 = manager.getEntityData<C0>(eid); auto c0 = manager.getEntityData<C0>(eid);
EXPECT_EQ(c0.x, 1); EXPECT_EQ(1, c0.x);
EXPECT_EQ(c0.y, 2); EXPECT_EQ(2, c0.y);
auto c1 = manager.getEntityData<C1>(eid); auto c1 = manager.getEntityData<C1>(eid);
EXPECT_EQ(c1.vx, 11); EXPECT_EQ(11, c1.vx);
EXPECT_EQ(c1.vy, 23); EXPECT_EQ(46, c1.vy);
} }
} }