diff --git a/CHANGES.md b/CHANGES.md index ce36c1173..0098ccd4b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -18,6 +18,8 @@ Changes from ns-3.41 to ns-3-dev ### New API +* Objects now can be aggregated to multiple objects though the `Object::UnidirectionalAggregateObject` function. Objects aggregated in such a way can not use `GetObject` to access the objects they are aggregated to. + ### Changes to existing API * `InetSocketAddress::SetTos()` and `InetSocketAddress::GetTos()` have been removed. @@ -30,8 +32,7 @@ Applications have a new Attribute to set the IPv4 ToS field. ### Changed behavior -* Fixed the corner rebound direction in `RandomWalk2d[Outdoor]MobilityModel` and the -initial direction in case of node starting from a border or corner. +* Fixed the corner rebound direction in `RandomWalk2d[Outdoor]MobilityModel` and the initial direction in case of node starting from a border or corner. Changes from ns-3.40 to ns-3.41 ------------------------------- diff --git a/src/core/model/object.cc b/src/core/model/object.cc index 176fed3a5..2f3af0427 100644 --- a/src/core/model/object.cc +++ b/src/core/model/object.cc @@ -59,16 +59,27 @@ bool Object::AggregateIterator::HasNext() const { NS_LOG_FUNCTION(this); - return m_current < m_object->m_aggregates->n; + return (m_current < m_object->m_aggregates->n) || + (m_uniAggrIter != m_object->m_unidirectionalAggregates.end()); } Ptr Object::AggregateIterator::Next() { NS_LOG_FUNCTION(this); - Object* object = m_object->m_aggregates->buffer[m_current]; - m_current++; - return object; + if (m_current < m_object->m_aggregates->n) + { + Object* object = m_object->m_aggregates->buffer[m_current]; + m_current++; + return object; + } + else if (m_uniAggrIter != m_object->m_unidirectionalAggregates.end()) + { + auto object = *m_uniAggrIter; + m_uniAggrIter++; + return object; + } + return nullptr; } Object::AggregateIterator::AggregateIterator(Ptr object) @@ -76,6 +87,7 @@ Object::AggregateIterator::AggregateIterator(Ptr object) m_current(0) { NS_LOG_FUNCTION(this << object); + m_uniAggrIter = object->m_unidirectionalAggregates.begin(); } TypeId @@ -127,6 +139,7 @@ Object::~Object() std::free(m_aggregates); } m_aggregates = nullptr; + m_unidirectionalAggregates.clear(); } Object::Object(const Object& o) @@ -153,6 +166,7 @@ Object::DoGetObject(TypeId tid) const NS_LOG_FUNCTION(this << tid); NS_ASSERT(CheckLoose()); + // First check if the object is in the normal aggregates. uint32_t n = m_aggregates->n; TypeId objectTid = Object::GetTypeId(); for (uint32_t i = 0; i < n; i++) @@ -179,6 +193,20 @@ Object::DoGetObject(TypeId tid) const return const_cast(current); } } + + // Next check if it's a unidirectional aggregate + for (auto& uniItem : m_unidirectionalAggregates) + { + TypeId cur = uniItem->GetInstanceTypeId(); + while (cur != tid && cur != objectTid) + { + cur = cur.GetParent(); + } + if (cur == tid) + { + return uniItem; + } + } return nullptr; } @@ -206,6 +234,17 @@ restart: goto restart; } } + + // note: no need to restart because unidirectionally aggregated objects + // can not change the status of the actual object. + for (auto& uniItem : m_unidirectionalAggregates) + { + if (!uniItem->m_initialized) + { + uniItem->DoInitialize(); + uniItem->m_initialized = true; + } + } } bool @@ -239,6 +278,17 @@ restart: goto restart; } } + + // note: no need to restart because unidirectionally aggregated objects + // can not change the status of the actual object. + for (auto& uniItem : m_unidirectionalAggregates) + { + if (!uniItem->m_disposed && uniItem->GetReferenceCount() == 1) + { + uniItem->DoDispose(); + uniItem->m_disposed = true; + } + } } void @@ -280,11 +330,13 @@ Object::AggregateObject(Ptr o) { aggregates->buffer[m_aggregates->n + i] = other->m_aggregates->buffer[i]; const TypeId typeId = other->m_aggregates->buffer[i]->GetInstanceTypeId(); + // note: DoGetObject scans also the unidirectional aggregates if (DoGetObject(typeId)) { NS_FATAL_ERROR("Object::AggregateObject(): " "Multiple aggregation of objects of type " - << other->GetInstanceTypeId() << " on objects of type " << typeId); + << other->GetInstanceTypeId() << " on objects of type " + << GetInstanceTypeId()); } UpdateSortedArray(aggregates, m_aggregates->n + i); } @@ -323,7 +375,47 @@ Object::AggregateObject(Ptr o) std::free(b); } -/** +void +Object::UnidirectionalAggregateObject(Ptr o) +{ + NS_LOG_FUNCTION(this << o); + NS_ASSERT(!m_disposed); + NS_ASSERT(!o->m_disposed); + NS_ASSERT(CheckLoose()); + NS_ASSERT(o->CheckLoose()); + + Object* other = PeekPointer(o); + + const TypeId typeId = other->GetInstanceTypeId(); + // note: DoGetObject scans also the unidirectional aggregates + if (DoGetObject(typeId)) + { + NS_FATAL_ERROR("Object::UnidirectionalAggregateObject(): " + "Multiple aggregation of objects of type " + << other->GetInstanceTypeId() << " on objects of type " + << GetInstanceTypeId()); + } + + m_unidirectionalAggregates.emplace_back(other); + + // Finally, call NotifyNewAggregate on all the objects aggregates by this object. + // We skip the aggregated Object and its aggregates because they are not + // mutually aggregated to the others. + // Unfortunately, we have to make a copy of the aggregated objects, because + // NotifyNewAggregate might change it... + + std::list aggregates; + for (uint32_t i = 0; i < m_aggregates->n; i++) + { + aggregates.emplace_back(m_aggregates->buffer[i]); + } + for (auto& item : aggregates) + { + item->NotifyNewAggregate(); + } +} + +/* * This function must be implemented in the stack that needs to notify * other stacks connected to the node of their presence in the node. */ diff --git a/src/core/model/object.h b/src/core/model/object.h index 09bf09357..b5f46a01c 100644 --- a/src/core/model/object.h +++ b/src/core/model/object.h @@ -137,6 +137,8 @@ class Object : public SimpleRefCount AggregateIterator(Ptr object); Ptr m_object; //!< Parent Object. uint32_t m_current; //!< Current position in parent's aggregates. + /// Iterator to the unidirectional aggregates. + std::vector>::const_iterator m_uniAggrIter; }; /** Constructor. */ @@ -199,6 +201,44 @@ class Object : public SimpleRefCount */ void AggregateObject(Ptr other); + /** + * Aggregate an Object to another Object. + * + * \param [in] other The other Object pointer + * + * This method aggregates the an object to another Object: + * after this method returns, it becomes possible to call GetObject() + * on the aggregating Object to get the other, but not vice-versa. + * + * This method calls the virtual method NotifyNewAggregates() to + * notify all aggregated Objects that they have been aggregated + * together. + * + * This method is useful only if there is the need to aggregate an + * object to more than one object at the same time, and should be avoided + * if not strictly necessary. + * In particular, objects aggregated with this method should be destroyed + * only after making sure that the objects they are aggregated to are + * destroyed as well. However, the destruction of the aggregating objects + * will take care of the unidirectional aggregated objects gracefully. + * + * Beware that an object aggregated to another with this function + * behaves differently than other aggregates in the following ways. + * Suppose that Object B is aggregated unidirectionally: + * - It can be aggregated unidirectionally to more than one objects + * (e.g., A1 and A2). + * - It is not possible to call GetObject on B to find an aggregate of + * object A1 or A2. + * - When A1 or A2 are initialized, B is initialized, whichever happens first. + * - When A1 or A2 are destroyed, B is destroyed, whichever happens last. + * - If B is initialized, A1 and A2 are unaffected. + * - If B is forcefully destroyed, A1 and A2 are unaffected. + * + * + * \sa AggregateObject() + */ + void UnidirectionalAggregateObject(Ptr other); + /** * Get an iterator to the Objects aggregated to this one. * @@ -436,6 +476,17 @@ class Object : public SimpleRefCount * so the size of the array is indirectly a reference count. */ Aggregates* m_aggregates; + + /** + * An array of unidirectional aggregates, i.e., objects that are + * aggregated to the current object, but not vice-versa. + * + * This is useful (and suggested) only for Objects that should + * be aggregated to multiple other Objects, where the normal + * Aggregation would create an issue. + */ + std::vector> m_unidirectionalAggregates; + /** * The number of times the Object was accessed with a * call to GetObject(). diff --git a/src/core/test/object-test-suite.cc b/src/core/test/object-test-suite.cc index 76032ab68..f8bfe0cc0 100644 --- a/src/core/test/object-test-suite.cc +++ b/src/core/test/object-test-suite.cc @@ -464,6 +464,74 @@ AggregateObjectTestCase::DoRun() NS_TEST_ASSERT_MSG_NE(baseA, nullptr, "Unable to GetObject on released object"); } +/** + * \ingroup object-tests + * Test we can aggregate Objects. + */ +class UnidirectionalAggregateObjectTestCase : public TestCase +{ + public: + /** Constructor. */ + UnidirectionalAggregateObjectTestCase(); + /** Destructor. */ + ~UnidirectionalAggregateObjectTestCase() override; + + private: + void DoRun() override; +}; + +UnidirectionalAggregateObjectTestCase::UnidirectionalAggregateObjectTestCase() + : TestCase("Check Object unidirectional aggregation functionality") +{ +} + +UnidirectionalAggregateObjectTestCase::~UnidirectionalAggregateObjectTestCase() +{ +} + +void +UnidirectionalAggregateObjectTestCase::DoRun() +{ + Ptr baseAOne = CreateObject(); + NS_TEST_ASSERT_MSG_NE(baseAOne, nullptr, "Unable to CreateObject"); + Ptr baseATwo = CreateObject(); + NS_TEST_ASSERT_MSG_NE(baseATwo, nullptr, "Unable to CreateObject"); + + Ptr baseB = CreateObject(); + NS_TEST_ASSERT_MSG_NE(baseB, nullptr, "Unable to CreateObject"); + + // + // Make an unidirectional aggregation of a BaseA object and a BaseB object. + // + baseAOne->UnidirectionalAggregateObject(baseB); + baseATwo->UnidirectionalAggregateObject(baseB); + + // + // We should be able to ask the aggregation (through baseA) for the BaseB part + // on either BaseA objects + // + NS_TEST_ASSERT_MSG_NE(baseAOne->GetObject(), + nullptr, + "Cannot GetObject (through baseAOne) for BaseB Object"); + + NS_TEST_ASSERT_MSG_NE(baseATwo->GetObject(), + nullptr, + "Cannot GetObject (through baseATwo) for BaseB Object"); + + NS_TEST_ASSERT_MSG_EQ( + baseAOne->GetObject(), + baseATwo->GetObject(), + "GetObject (through baseAOne and baseATwo) for BaseB Object are not equal"); + + // + // We should not be able to ask the aggregation (through baseB) for the BaseA part + // of the aggregation. + // + NS_TEST_ASSERT_MSG_NE(!baseB->GetObject(), + 0, + "Can GetObject (through baseB) for BaseA Object"); +} + /** * \ingroup object-tests * Test an Object factory can create Objects @@ -565,6 +633,7 @@ ObjectTestSuite::ObjectTestSuite() { AddTestCase(new CreateObjectTestCase); AddTestCase(new AggregateObjectTestCase); + AddTestCase(new UnidirectionalAggregateObjectTestCase); AddTestCase(new ObjectFactoryTestCase); }