core: allow multiple aggregation of an object
This commit is contained in:
@@ -18,6 +18,8 @@ Changes from ns-3.41 to ns-3-dev
|
|||||||
|
|
||||||
### New API
|
### 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
|
### Changes to existing API
|
||||||
|
|
||||||
* `InetSocketAddress::SetTos()` and `InetSocketAddress::GetTos()` have been removed.
|
* `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
|
### Changed behavior
|
||||||
|
|
||||||
* Fixed the corner rebound direction in `RandomWalk2d[Outdoor]MobilityModel` and the
|
* Fixed the corner rebound direction in `RandomWalk2d[Outdoor]MobilityModel` and the initial direction in case of node starting from a border or corner.
|
||||||
initial direction in case of node starting from a border or corner.
|
|
||||||
|
|
||||||
Changes from ns-3.40 to ns-3.41
|
Changes from ns-3.40 to ns-3.41
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|||||||
@@ -59,16 +59,27 @@ bool
|
|||||||
Object::AggregateIterator::HasNext() const
|
Object::AggregateIterator::HasNext() const
|
||||||
{
|
{
|
||||||
NS_LOG_FUNCTION(this);
|
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<const Object>
|
Ptr<const Object>
|
||||||
Object::AggregateIterator::Next()
|
Object::AggregateIterator::Next()
|
||||||
{
|
{
|
||||||
NS_LOG_FUNCTION(this);
|
NS_LOG_FUNCTION(this);
|
||||||
Object* object = m_object->m_aggregates->buffer[m_current];
|
if (m_current < m_object->m_aggregates->n)
|
||||||
m_current++;
|
{
|
||||||
return object;
|
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<const Object> object)
|
Object::AggregateIterator::AggregateIterator(Ptr<const Object> object)
|
||||||
@@ -76,6 +87,7 @@ Object::AggregateIterator::AggregateIterator(Ptr<const Object> object)
|
|||||||
m_current(0)
|
m_current(0)
|
||||||
{
|
{
|
||||||
NS_LOG_FUNCTION(this << object);
|
NS_LOG_FUNCTION(this << object);
|
||||||
|
m_uniAggrIter = object->m_unidirectionalAggregates.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId
|
TypeId
|
||||||
@@ -127,6 +139,7 @@ Object::~Object()
|
|||||||
std::free(m_aggregates);
|
std::free(m_aggregates);
|
||||||
}
|
}
|
||||||
m_aggregates = nullptr;
|
m_aggregates = nullptr;
|
||||||
|
m_unidirectionalAggregates.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
Object::Object(const Object& o)
|
Object::Object(const Object& o)
|
||||||
@@ -153,6 +166,7 @@ Object::DoGetObject(TypeId tid) const
|
|||||||
NS_LOG_FUNCTION(this << tid);
|
NS_LOG_FUNCTION(this << tid);
|
||||||
NS_ASSERT(CheckLoose());
|
NS_ASSERT(CheckLoose());
|
||||||
|
|
||||||
|
// First check if the object is in the normal aggregates.
|
||||||
uint32_t n = m_aggregates->n;
|
uint32_t n = m_aggregates->n;
|
||||||
TypeId objectTid = Object::GetTypeId();
|
TypeId objectTid = Object::GetTypeId();
|
||||||
for (uint32_t i = 0; i < n; i++)
|
for (uint32_t i = 0; i < n; i++)
|
||||||
@@ -179,6 +193,20 @@ Object::DoGetObject(TypeId tid) const
|
|||||||
return const_cast<Object*>(current);
|
return const_cast<Object*>(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;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,6 +234,17 @@ restart:
|
|||||||
goto 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
|
bool
|
||||||
@@ -239,6 +278,17 @@ restart:
|
|||||||
goto 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
|
void
|
||||||
@@ -280,11 +330,13 @@ Object::AggregateObject(Ptr<Object> o)
|
|||||||
{
|
{
|
||||||
aggregates->buffer[m_aggregates->n + i] = other->m_aggregates->buffer[i];
|
aggregates->buffer[m_aggregates->n + i] = other->m_aggregates->buffer[i];
|
||||||
const TypeId typeId = other->m_aggregates->buffer[i]->GetInstanceTypeId();
|
const TypeId typeId = other->m_aggregates->buffer[i]->GetInstanceTypeId();
|
||||||
|
// note: DoGetObject scans also the unidirectional aggregates
|
||||||
if (DoGetObject(typeId))
|
if (DoGetObject(typeId))
|
||||||
{
|
{
|
||||||
NS_FATAL_ERROR("Object::AggregateObject(): "
|
NS_FATAL_ERROR("Object::AggregateObject(): "
|
||||||
"Multiple aggregation of objects of type "
|
"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);
|
UpdateSortedArray(aggregates, m_aggregates->n + i);
|
||||||
}
|
}
|
||||||
@@ -323,7 +375,47 @@ Object::AggregateObject(Ptr<Object> o)
|
|||||||
std::free(b);
|
std::free(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void
|
||||||
|
Object::UnidirectionalAggregateObject(Ptr<Object> 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<Object*> 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
|
* This function must be implemented in the stack that needs to notify
|
||||||
* other stacks connected to the node of their presence in the node.
|
* other stacks connected to the node of their presence in the node.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -137,6 +137,8 @@ class Object : public SimpleRefCount<Object, ObjectBase, ObjectDeleter>
|
|||||||
AggregateIterator(Ptr<const Object> object);
|
AggregateIterator(Ptr<const Object> object);
|
||||||
Ptr<const Object> m_object; //!< Parent Object.
|
Ptr<const Object> m_object; //!< Parent Object.
|
||||||
uint32_t m_current; //!< Current position in parent's aggregates.
|
uint32_t m_current; //!< Current position in parent's aggregates.
|
||||||
|
/// Iterator to the unidirectional aggregates.
|
||||||
|
std::vector<Ptr<Object>>::const_iterator m_uniAggrIter;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Constructor. */
|
/** Constructor. */
|
||||||
@@ -199,6 +201,44 @@ class Object : public SimpleRefCount<Object, ObjectBase, ObjectDeleter>
|
|||||||
*/
|
*/
|
||||||
void AggregateObject(Ptr<Object> other);
|
void AggregateObject(Ptr<Object> 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<Object> other);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an iterator to the Objects aggregated to this one.
|
* Get an iterator to the Objects aggregated to this one.
|
||||||
*
|
*
|
||||||
@@ -436,6 +476,17 @@ class Object : public SimpleRefCount<Object, ObjectBase, ObjectDeleter>
|
|||||||
* so the size of the array is indirectly a reference count.
|
* so the size of the array is indirectly a reference count.
|
||||||
*/
|
*/
|
||||||
Aggregates* m_aggregates;
|
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<Ptr<Object>> m_unidirectionalAggregates;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of times the Object was accessed with a
|
* The number of times the Object was accessed with a
|
||||||
* call to GetObject().
|
* call to GetObject().
|
||||||
|
|||||||
@@ -464,6 +464,74 @@ AggregateObjectTestCase::DoRun()
|
|||||||
NS_TEST_ASSERT_MSG_NE(baseA, nullptr, "Unable to GetObject on released object");
|
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<BaseA> baseAOne = CreateObject<BaseA>();
|
||||||
|
NS_TEST_ASSERT_MSG_NE(baseAOne, nullptr, "Unable to CreateObject<BaseA>");
|
||||||
|
Ptr<BaseA> baseATwo = CreateObject<BaseA>();
|
||||||
|
NS_TEST_ASSERT_MSG_NE(baseATwo, nullptr, "Unable to CreateObject<BaseA>");
|
||||||
|
|
||||||
|
Ptr<BaseB> baseB = CreateObject<BaseB>();
|
||||||
|
NS_TEST_ASSERT_MSG_NE(baseB, nullptr, "Unable to CreateObject<BaseB>");
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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<BaseB>(),
|
||||||
|
nullptr,
|
||||||
|
"Cannot GetObject (through baseAOne) for BaseB Object");
|
||||||
|
|
||||||
|
NS_TEST_ASSERT_MSG_NE(baseATwo->GetObject<BaseB>(),
|
||||||
|
nullptr,
|
||||||
|
"Cannot GetObject (through baseATwo) for BaseB Object");
|
||||||
|
|
||||||
|
NS_TEST_ASSERT_MSG_EQ(
|
||||||
|
baseAOne->GetObject<BaseB>(),
|
||||||
|
baseATwo->GetObject<BaseB>(),
|
||||||
|
"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<BaseA>(),
|
||||||
|
0,
|
||||||
|
"Can GetObject (through baseB) for BaseA Object");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \ingroup object-tests
|
* \ingroup object-tests
|
||||||
* Test an Object factory can create Objects
|
* Test an Object factory can create Objects
|
||||||
@@ -565,6 +633,7 @@ ObjectTestSuite::ObjectTestSuite()
|
|||||||
{
|
{
|
||||||
AddTestCase(new CreateObjectTestCase);
|
AddTestCase(new CreateObjectTestCase);
|
||||||
AddTestCase(new AggregateObjectTestCase);
|
AddTestCase(new AggregateObjectTestCase);
|
||||||
|
AddTestCase(new UnidirectionalAggregateObjectTestCase);
|
||||||
AddTestCase(new ObjectFactoryTestCase);
|
AddTestCase(new ObjectFactoryTestCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user