Hello,
I recently experienced a very strange crash, which after inspecting further turned out to be caused by the destructor of a class being called one time too often in a certain vector creation. I havn't been able to fully determine the excact reason this is happening, so I hope some of you guys has a better idea of what could be going on. I'm using VS2013. This is the code that crashes:
const ecs::ComponentDeclaration& Sprite::GenerateDeclaration(void)
{
const ecs::ComponentDeclaration::AttributeVector vAttributes =
{
{ L"Texture", core::VariableType::STRING, offsetof(Sprite, stTexture), onSetTexture },
{ L"Blending", core::VariableType::INT, offsetof(Sprite, blend) },
}; // this line here
static const ecs::ComponentDeclaration declaration(L"Sprite", vAttributes);
return declaration;
}
The crash happens in the commented line, so appearently in the vector construction. Let me show you all related code:
struct ACCLIMATE_API ComponentDeclaration
{
public:
typedef std::vector<AttributeDeclaration> AttributeVector;
//...
}
This is the declaration of the vector.
struct ACCLIMATE_API AttributeDeclaration
{
typedef void(*SetCallback)(BaseComponent& component);
AttributeDeclaration(const std::wstring& stName, core::VariableType type, size_t offset, SetCallback pCallback = nullptr);
std::wstring stName;
size_t offset;
SetCallback pCallback;
ICustomComponentAttributes* pCustom;
private:
core::Variable m_default;
};
AttributeDeclaration::AttributeDeclaration(const std::wstring& stName, core::VariableType type, size_t offset, SetCallback pCallback) :
stName(stName), pCallback(pCallback), m_default(type, false), pCustom(nullptr)
{
}
Thats the class that is being stored, with all related variables/methods. It stores a core::Variable class, which acts like a semi-variant data-container, which is defined as follows:
class ACCLIMATE_API Variable
{
public:
static const unsigned int NO_OBJECT = -1;
Variable(VariableType type, bool isArray);
Variable(const Variable& variable);
Variable(Variable&& variable);
~Variable(void);
Variable& operator=(const Variable& variable);
Variable& operator=(Variable&& variable);
private:
void* m_pData;
VariableType m_type;
bool m_isArray;
unsigned int m_objectType;
};
Variable::Variable(VariableType type, bool isArray) :
m_type(type), m_isArray(isArray), m_objectType(NO_OBJECT),
m_pData(nullptr)
{
ACL_ASSERT(m_type != VariableType::OBJECT);
if(type != VariableType::UNKNOWN)
m_pData = CallByTypeNoObject<newData, void*>();
}
Variable::Variable(const Variable& variable) :
m_type(variable.m_type), m_isArray(variable.m_isArray), m_objectType(variable.m_objectType)
{
m_pData = CopyData(variable.m_pData);
}
Variable::Variable(Variable&& variable) :
m_type(variable.m_type), m_isArray(variable.m_isArray), m_objectType(variable.m_objectType)
{
m_pData = variable.m_pData;
variable.m_pData = nullptr;
variable.m_type = VariableType::UNKNOWN;
variable.m_objectType = NO_OBJECT;
}
Variable::~Variable(void)
{
// just for simplicities sake - the actual deletion of the correct type is normally handled
delete m_pData;
}
Variable& Variable::operator=(const Variable& variable)
{
if(this != &variable)
{
m_type = variable.m_type;
m_isArray = variable.m_isArray;
m_objectType = variable.m_objectType;
DeleteData();
m_pData = CopyData(variable.m_pData);
}
return *this;
}
Variable& Variable::operator=(Variable&& variable)
{
if(this != &variable)
{
m_type = variable.m_type;
m_isArray = variable.m_isArray;
m_objectType = variable.m_objectType;
m_pData = variable.m_pData;
variable.m_pData = nullptr;
variable.m_type = VariableType::UNKNOWN;
variable.m_objectType = NO_OBJECT;
}
return *this;
}
Now the crash happens inside this classes ctor. Obviously, m_pData is destroyed more then once. The call hierachy looks as follows:
- ctor of first line is being called (line in the first code snipped)
- ctor for second line is being called
- copy-ctor is called from the object of the first line
- copy-ctor is called from the object of the second line
- Destructor is called on the first object
- Destructor is called on the second object
- Destructor is called on the second object again - crash!
Now the reason I know that the destructor is called again, and not m_pData is somehow deleted twice is that by setting m_pData to nullptr in the destructor, the crash resolves. Also note that it is probably not an issue with the Variable-class, as the void* m_pData has already been part of the AttributeDeclaration-class before, as well as part of some other class.
_____________________________________________________
So, thats about all the information I can give. I can't make any sense of the vector-code, so debugging it further is rather out of the question. My question is now: Does someone see something obviously wrong, or knows/has some idea what is going on? Appearently I *could* fix it by simply putting in the nullptr-set in the ctor, but thats way to hackish and obviously just hides the problem. One thing I find interesting is that the move-ctor isn't called, as it is almost always when dealing with this kind of vector-emplacement, maybe thats related to the problem?