ObjectBase::ConstructSelf does not initialize attributes
Since ns-3.45, the method Object::GetInstanceTypeId has been set final.
The consequence of this change is that when calling in a constructor ObjectBase::ConstructSelf(AttributeConstructionList());, the attributes defined in the associated class are not set.
Here is a quick example to reproduce this:
#include "ns3/core-module.h"
NS_LOG_COMPONENT_DEFINE("TidTester");
namespace ns3
{
class TidTester : public Object {
public:
TidTester();
static TypeId GetTypeId(void);
uint32_t GetMyAttribute();
private:
uint32_t m_myAttribute;
};
NS_OBJECT_ENSURE_REGISTERED(TidTester);
TypeId
TidTester::GetTypeId(void)
{
static TypeId tid = TypeId("ns3::TidTester")
.SetParent<Object>()
.AddConstructor<TidTester>()
.AddAttribute("MyAttribute",
"Set MyAttribute",
UintegerValue(0),
MakeIntegerAccessor(&TidTester::m_myAttribute),
MakeIntegerChecker<uint32_t>());
return tid;
}
uint32_t
TidTester::GetMyAttribute() {
return m_myAttribute;
}
TidTester::TidTester()
: m_myAttribute(0)
{
std::cout << "### Before ConstructSelf: " << m_myAttribute << std::endl;
ObjectBase::ConstructSelf(AttributeConstructionList());
std::cout << "### After ConstructSelf: " << m_myAttribute << std::endl;
}
}
using namespace ns3;
int
main(int argc, char* argv[])
{
Config::SetDefault("ns3::TidTester::MyAttribute", StringValue("10"));
Ptr<TidTester> obj = CreateObject<TidTester>();
std::cout << "### After constructor: " << obj->GetMyAttribute() << std::endl;
return 0;
}
In this example, I was expecting m_myAttribute to be set to 10 after ConstructSelf is called in the constructor of TidTester, but I still got 0. m_myAttribute is set to 10 only after leaving the constructor scope, when I get back in the main function.
After investigation, I saw that ObjectBase::ConstructSelf has this instruction: TypeId tid = GetInstanceTypeId();. As GetInstanceTypeId() is now final in Objet, it returns to ConstructSelf the tid associated to the Object class and not the one from TidTester (it was possible before because GetInstanceTypeId was not final and could be overloaded in TidTester).
This behavior can also be reproduced with RttEstimator, which also call ConstructSelf in its constructor.
Do I make something wrong, or is it an unwanted behavior ?
And is it possible to find a workaround without modifying any existing class (Object or ObjectBase for example) ? I tried to replace ObjectBase::ConstructSelf(AttributeConstructionList()); with CompleteConstruct(this); since it eventually calls ConstructSelf but set the proper TypeId just before. It works in the constructor and I’m able to access m_myAttribute with the expected value 10. But when it is called a second time at the end of CreateObject, the TypeId tid = GetInstanceTypeId(); in ConstructSelf end in a segfault.