Proper string handling without nasty memory leaks. PHEW.
This commit is contained in:
@@ -32,11 +32,19 @@ ComponentPoolForwardIterator& ComponentPoolForwardIterator::operator++()
|
||||
|
||||
ComponentPool::ComponentPool(const ComponentPool& other)
|
||||
: m_ComponentInfo(other.m_ComponentInfo)
|
||||
, m_Pool(other.m_Pool)
|
||||
, m_Pool(other.m_Pool)
|
||||
, m_EntityToComponent()
|
||||
{
|
||||
// Update EntityToComponent pointers
|
||||
for (char& ptr : m_Pool) {
|
||||
EntityID entity = *reinterpret_cast<EntityID*>(&ptr);
|
||||
m_EntityToComponent[entity] = &ptr;
|
||||
}
|
||||
|
||||
// Duplicate strings
|
||||
for (auto& name : m_ComponentInfo.StringFields) {
|
||||
for (auto& c : *this) {
|
||||
std::string& val = c[name];
|
||||
ComponentWrapper::SolidifyStrings(c);
|
||||
}
|
||||
}
|
||||
@@ -44,11 +52,9 @@ ComponentPool::ComponentPool(const ComponentPool& other)
|
||||
|
||||
ComponentPool::~ComponentPool()
|
||||
{
|
||||
// Call std::string destructors
|
||||
for (auto& name : m_ComponentInfo.StringFields) {
|
||||
for (auto& c : *this) {
|
||||
c.Field<std::string>(name).~basic_string();
|
||||
}
|
||||
// Destroy component data
|
||||
for (auto& c : *this) {
|
||||
ComponentWrapper::Destroy(c.Info, c.Data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +92,7 @@ bool ComponentPool::KnowsEntity(EntityID ent)
|
||||
|
||||
void ComponentPool::Delete(ComponentWrapper& wrapper)
|
||||
{
|
||||
ComponentWrapper::Destroy(wrapper.Info, wrapper.Data);
|
||||
m_EntityToComponent.erase(wrapper.EntityID);
|
||||
m_Pool.Free(wrapper.Data - sizeof(EntityID));
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ void EntityFilePreprocessor::parseDefaults()
|
||||
|
||||
for (auto& ci : m_ComponentInfo) {
|
||||
// Allocate memory for default values
|
||||
ci.second.Defaults = std::shared_ptr<char>(new char[ci.second.Stride]);
|
||||
ci.second.Defaults = boost::shared_array<char>(new char[ci.second.Stride], std::bind(&ComponentWrapper::Destroy, ci.second, std::placeholders::_1));
|
||||
memset(ci.second.Defaults.get(), 0, ci.second.Stride);
|
||||
|
||||
std::string componentName = ci.first;
|
||||
|
||||
@@ -11,14 +11,18 @@ World::~World()
|
||||
|
||||
World::World(const World& other)
|
||||
: m_EventBroker(other.m_EventBroker)
|
||||
, m_CurrentEntityID(other.m_CurrentEntityID)
|
||||
, m_EntityParents(other.m_EntityParents)
|
||||
, m_EntityChildren(other.m_EntityChildren)
|
||||
, m_EntityNames(other.m_EntityNames)
|
||||
{
|
||||
// Deep copy component pools
|
||||
for (auto& kv : m_ComponentPools) {
|
||||
for (auto& kv : other.m_ComponentPools) {
|
||||
m_ComponentPools[kv.first] = new ComponentPool(*kv.second);
|
||||
}
|
||||
}
|
||||
|
||||
EntityID World::CreateEntity(EntityID parent /*= 0*/)
|
||||
EntityID World::CreateEntity(EntityID parent /*= EntityID_Invalid*/)
|
||||
{
|
||||
EntityID newEntity = generateEntityID();
|
||||
if (newEntity == parent) {
|
||||
@@ -53,11 +57,8 @@ ComponentWrapper World::AttachComponent(EntityID entity, const std::string& comp
|
||||
ComponentPool* pool = m_ComponentPools.at(componentType);
|
||||
const ComponentInfo& ci = pool->ComponentInfo();
|
||||
|
||||
// Allocate space for the component
|
||||
// Allocate component with default values
|
||||
ComponentWrapper c = pool->Allocate(entity);
|
||||
// Write default values
|
||||
memcpy(c.Data, ci.Defaults.get(), ci.Stride);
|
||||
ComponentWrapper::SolidifyStrings(c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -69,3 +69,47 @@ BOOST_AUTO_TEST_CASE(WorldTestMultipleAllocations, * utf::tolerance(0.00001))
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(WorldCopy, *utf::tolerance(0.00001))
|
||||
{
|
||||
World w1;
|
||||
|
||||
// Create a test component
|
||||
auto testComponent = ComponentWrapperFactory("Test", 2);
|
||||
testComponent.AddProperty("TestInteger", 1337);
|
||||
testComponent.AddProperty("TestDouble", 13.37);
|
||||
testComponent.AddProperty("TestString", std::string("DefaultString"));
|
||||
testComponent.AddProperty("TestVec3", glm::vec3(1.f, 2.f, 3.f));
|
||||
w1.RegisterComponent(testComponent);
|
||||
|
||||
// Create a test entity
|
||||
EntityID w1_e1 = w1.CreateEntity();
|
||||
auto w1_c1 = w1.AttachComponent(w1_e1, "Test");
|
||||
|
||||
// Create a child
|
||||
EntityID w1_e2 = w1.CreateEntity(w1_e1);
|
||||
auto w1_c2 = w1.AttachComponent(w1_e2, "Test");
|
||||
w1_c2["TestString"] = "NonDefaultString";
|
||||
|
||||
// Copy the world!
|
||||
World w2 = w1;
|
||||
|
||||
// Fetch the components
|
||||
auto w2_c1 = w2.GetComponent(w1_e1, "Test");
|
||||
auto w2_c2 = w2.GetComponent(w1_e2, "Test");
|
||||
|
||||
// Check that built-in types are copied but don't reside in the same memory
|
||||
BOOST_CHECK((int)w1_c1["TestInteger"] == (int)w2_c1["TestInteger"]);
|
||||
BOOST_CHECK(&(int&)w1_c1["TestInteger"] != &(int&)w2_c1["TestInteger"]);
|
||||
BOOST_CHECK((double)w1_c1["TestDouble"] == (double)w2_c1["TestDouble"]);
|
||||
BOOST_CHECK(&(int&)w1_c1["TestDouble"] != &(int&)w2_c1["TestDouble"]);
|
||||
BOOST_CHECK((int)w1_c2["TestInteger"] == (int)w2_c2["TestInteger"]);
|
||||
BOOST_CHECK(&(int&)w1_c2["TestInteger"] != &(int&)w2_c2["TestInteger"]);
|
||||
BOOST_CHECK((double)w1_c2["TestDouble"] == (double)w2_c2["TestDouble"]);
|
||||
BOOST_CHECK(&(int&)w1_c2["TestDouble"] != &(int&)w2_c2["TestDouble"]);
|
||||
// Check that specially handled strings are fine
|
||||
BOOST_CHECK((std::string)w1_c1["TestString"] == (std::string)w2_c1["TestString"]);
|
||||
BOOST_CHECK(&(std::string&)w1_c1["TestString"] != &(std::string&)w2_c1["TestString"]);
|
||||
BOOST_CHECK((std::string)w1_c2["TestString"] == (std::string)w2_c2["TestString"]);
|
||||
BOOST_CHECK(&(std::string&)w1_c2["TestString"] != &(std::string&)w2_c2["TestString"]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user