diff --git a/doc/manual/source/attributes.rst b/doc/manual/source/attributes.rst index d05876484..c7749e4e9 100644 --- a/doc/manual/source/attributes.rst +++ b/doc/manual/source/attributes.rst @@ -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() { diff --git a/src/core/model/object-base.cc b/src/core/model/object-base.cc index 85ced0952..1ef5a67ae 100644 --- a/src/core/model/object-base.cc +++ b/src/core/model/object-base.cc @@ -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 diff --git a/src/internet/model/rtt-estimator.cc b/src/internet/model/rtt-estimator.cc index 79747c8d8..abc79d0b3 100644 --- a/src/internet/model/rtt-estimator.cc +++ b/src/internet/model/rtt-estimator.cc @@ -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."); } diff --git a/src/internet/model/rtt-estimator.h b/src/internet/model/rtt-estimator.h index 1922c68e6..fdfc9d5fe 100644 --- a/src/internet/model/rtt-estimator.h +++ b/src/internet/model/rtt-estimator.h @@ -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 diff --git a/src/lte/helper/emu-epc-helper.cc b/src/lte/helper/emu-epc-helper.cc index 7bbf4f190..ac119afd9 100644 --- a/src/lte/helper/emu-epc-helper.cc +++ b/src/lte/helper/emu-epc-helper.cc @@ -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; diff --git a/src/lte/helper/emu-epc-helper.h b/src/lte/helper/emu-epc-helper.h index d03c09827..5c3225bbf 100644 --- a/src/lte/helper/emu-epc-helper.h +++ b/src/lte/helper/emu-epc-helper.h @@ -55,6 +55,9 @@ class EmuEpcHelper : public NoBackhaulEpcHelper std::vector cellIds) override; void AddX2Interface(Ptr enbNode1, Ptr enbNode2) override; + protected: + void NotifyConstructionCompleted() override; + private: /** * helper to assign addresses to S1-U NetDevices diff --git a/src/lte/helper/no-backhaul-epc-helper.cc b/src/lte/helper/no-backhaul-epc-helper.cc index 514cea5af..25e33b739 100644 --- a/src/lte/helper/no-backhaul-epc-helper.cc +++ b/src/lte/helper/no-backhaul-epc-helper.cc @@ -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; diff --git a/src/lte/helper/no-backhaul-epc-helper.h b/src/lte/helper/no-backhaul-epc-helper.h index ea98639dc..c1d29f468 100644 --- a/src/lte/helper/no-backhaul-epc-helper.h +++ b/src/lte/helper/no-backhaul-epc-helper.h @@ -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 * diff --git a/src/lte/helper/point-to-point-epc-helper.cc b/src/lte/helper/point-to-point-epc-helper.cc index 8e45a5814..68e070940 100644 --- a/src/lte/helper/point-to-point-epc-helper.cc +++ b/src/lte/helper/point-to-point-epc-helper.cc @@ -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 diff --git a/src/lte/helper/point-to-point-epc-helper.h b/src/lte/helper/point-to-point-epc-helper.h index 8bffc79fc..c254236d9 100644 --- a/src/lte/helper/point-to-point-epc-helper.h +++ b/src/lte/helper/point-to-point-epc-helper.h @@ -51,6 +51,9 @@ class PointToPointEpcHelper : public NoBackhaulEpcHelper Ptr lteEnbNetDevice, std::vector cellIds) override; + protected: + void NotifyConstructionCompleted() override; + private: /** * S1-U interfaces