When instantiating new object instances, our object factory implementation (see Core/Object.h) does not automatically initialize any Attributes to the default values that we declared via URHO3D_ATTRIBUTE macro. Professional programming standards mandate that we ought to initialize everything ourselves, yet it is a very common mistake to forget to do that, which leads to all kinds of problems that can be difficult to trace, such as the application crashing randomly.
I thought it would be nice if our factory checked for registered attributes, and if found, applied them for us.
I propose the following extension to ObjectFactoryImpl::CreateObject( )
/// Create an object of the specific type.
SharedPtr<Object> CreateObject() override {
T* rawptr = new T(context_);
/// Check if the object derives from Serializable
Serializable* casted = dynamic_cast<Serializable*>(rawptr);
if(casted!=nullptr)
{
/// Initialize local attributes to default values
const Vector<AttributeInfo>* attribs = context_->GetAttributes(GetType());
if(attribs!=nullptr){
for(int i=0;i<attribs->Size();i++)
{
const AttributeInfo& info = attribs->At(i);
info.accessor_->Set(casted, info.defaultValue_);
}
}
/// Initialize network attributes to default values
attribs = context_->GetNetworkAttributes(GetType());
if(attribs!=nullptr){
for(int i=0;i<attribs->Size();i++)
{
const AttributeInfo& info = attribs->At(i);
info.accessor_->Set(casted, info.defaultValue_);
}
}
}
return SharedPtr<Object>(rawptr);
}
Note that you’ll also need to include Attribute.h at the top of the Object.h file
I’ve tested this code in my current project, it’s stable there, but I have not tested more widely.
You’ll still have to initialize any unserialized members yourself, but at least you can rest assured that any serialized attributes will be initialized “automagically”.
Oh, and worth mentioning that since this is a templated method, appearing in a header file, the changes can be tested without needing to rebuild the engine.