core,internet,lte: Prevent calling ConstructSelf in constructors of Object-derived classes

This commit is contained in:
Stefano Avallone
2025-08-21 17:51:47 +02:00
committed by Tom Henderson
parent 736aeddc7f
commit 9eee8a345a
10 changed files with 64 additions and 16 deletions

View File

@@ -788,12 +788,20 @@ consistently to allow correct operation. To this end we do allow for consistency
checking *when the attribute is used* (*cf*. ``NS_ASSERT_MSG``
or ``NS_ABORT_MSG``).
In general, the attribute code to assign values to the underlying class member
variables is executed after an object is constructed. But what if you need the
values assigned before the constructor body executes, because you need them in
the logic of the constructor? There is a way to do this, used for example in the
class :cpp:class:`ConfigStore`: call :cpp:func:`ObjectBase::ConstructSelf()` as
follows::
For classes deriving from the :cpp:class:`Object` class, the attribute code to assign initial
values to the underlying class member variables is executed after an object is constructed.
Therefore, you cannot access the values assigned to the attributes of an object from within
its constructor. Instead, you can override :cpp:func:`ObjectBase::NotifyConstructionCompleted()`
and access the values assigned to the attributes of the object from within that function.
A practical example of how this is used can be found in the ns-3 class :cpp:class:`RttEstimator`
in the `internet` module.
For classes deriving directly from :cpp:class:`ObjectBase`, it is instead needed to explicitly
call :cpp:func:`ObjectBase::ConstructSelf()` to assign initial values to the class attributes.
Such a call can be made in the class constructor, in which case the values assigned to the
attributes are accessible from within its constructor. There are not many examples of this usage
in the ns-3 codebase because most classes with attributes typically derive from :cpp:class:`Object`.
One example is the :cpp:class:`ConfigStore` class, whose constructor is shown as follows::
ConfigStore::ConfigStore()
{

View File

@@ -83,6 +83,19 @@ ObjectBase::ConstructSelf(const AttributeConstructionList& attributes)
// loop over the inheritance tree back to the Object base class.
NS_LOG_FUNCTION(this << &attributes);
TypeId tid = GetInstanceTypeId();
TypeId objectTid;
// the TypeId of a class derived from Object is initialized to "ns3::Object"; for a class
// deriving from Object, check that this function is called after that the correct TypeId is set
// to ensure that the attributes of the class are initialized
NS_ABORT_MSG_IF(TypeId::LookupByNameFailSafe("ns3::Object", &objectTid) && tid == objectTid,
"ObjectBase::ConstructSelf() has been called on an object of a class derived "
"from the Object class, but the TypeId is still set to ns3::Object.\n"
"This is known to happen in two cases:\n"
"- ObjectBase::ConstructSelf() is called in the class constructor; in this "
"case, override ObjectBase::NotifyConstructionCompleted() to access the "
"initial values of the object attributes as soon as object construction is "
"completed (see issue #1249)\n"
"- the class deriving from Object does not define a static GetTypeId() method");
do // Do this tid and all parents
{
// loop over all attributes in object type

View File

@@ -63,12 +63,13 @@ RttEstimator::RttEstimator()
: m_nSamples(0)
{
NS_LOG_FUNCTION(this);
}
// We need attributes initialized here, not later, so use the
// ConstructSelf() technique documented in the manual
ObjectBase::ConstructSelf(AttributeConstructionList());
void
RttEstimator::NotifyConstructionCompleted()
{
NS_LOG_FUNCTION(this);
m_estimatedRtt = m_initialEstimatedRtt;
m_estimatedVariation = Time(0);
NS_LOG_DEBUG("Initialize m_estimatedRtt to " << m_estimatedRtt.GetSeconds() << " sec.");
}

View File

@@ -89,6 +89,8 @@ class RttEstimator : public Object
Time m_initialEstimatedRtt; //!< Initial RTT estimation
protected:
void NotifyConstructionCompleted() override;
Time m_estimatedRtt; //!< Current estimate
Time m_estimatedVariation; //!< Current estimate variation
uint32_t m_nSamples; //!< Number of samples

View File

@@ -31,8 +31,14 @@ EmuEpcHelper::EmuEpcHelper()
: NoBackhaulEpcHelper()
{
NS_LOG_FUNCTION(this);
// To access the attribute value within the constructor
ObjectBase::ConstructSelf(AttributeConstructionList());
}
void
EmuEpcHelper::NotifyConstructionCompleted()
{
NoBackhaulEpcHelper::NotifyConstructionCompleted();
NS_LOG_FUNCTION(this);
// Create EmuFdNetDevice for SGW
EmuFdNetDeviceHelper emu;

View File

@@ -55,6 +55,9 @@ class EmuEpcHelper : public NoBackhaulEpcHelper
std::vector<uint16_t> cellIds) override;
void AddX2Interface(Ptr<Node> enbNode1, Ptr<Node> enbNode2) override;
protected:
void NotifyConstructionCompleted() override;
private:
/**
* helper to assign addresses to S1-U NetDevices

View File

@@ -45,8 +45,12 @@ NoBackhaulEpcHelper::NoBackhaulEpcHelper()
m_s5LinkMtu(3000)
{
NS_LOG_FUNCTION(this);
// To access the attribute value within the constructor
ObjectBase::ConstructSelf(AttributeConstructionList());
}
void
NoBackhaulEpcHelper::NotifyConstructionCompleted()
{
EpcHelper::NotifyConstructionCompleted();
int retval;

View File

@@ -76,6 +76,8 @@ class NoBackhaulEpcHelper : public EpcHelper
int64_t AssignStreams(int64_t stream) override;
protected:
void NotifyConstructionCompleted() override;
/**
* @brief DoAddX2Interface: Call AddX2Interface on top of the Enb device pointers
*

View File

@@ -28,8 +28,14 @@ PointToPointEpcHelper::PointToPointEpcHelper()
: NoBackhaulEpcHelper()
{
NS_LOG_FUNCTION(this);
// To access the attribute value within the constructor
ObjectBase::ConstructSelf(AttributeConstructionList());
}
void
PointToPointEpcHelper::NotifyConstructionCompleted()
{
NoBackhaulEpcHelper::NotifyConstructionCompleted();
NS_LOG_FUNCTION(this);
// since we use point-to-point links for the backhaul links,
// we use a /30 subnet which can hold exactly two addresses

View File

@@ -51,6 +51,9 @@ class PointToPointEpcHelper : public NoBackhaulEpcHelper
Ptr<NetDevice> lteEnbNetDevice,
std::vector<uint16_t> cellIds) override;
protected:
void NotifyConstructionCompleted() override;
private:
/**
* S1-U interfaces