/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* * Copyright (c) 2007 INRIA, Gustavo Carneiro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation; * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Authors: Gustavo Carneiro , * Mathieu Lacage */ #ifndef OBJECT_H #define OBJECT_H #include #include #include #include "ptr.h" #include "attribute.h" #include "object-base.h" #include "attribute-construction-list.h" #include "simple-ref-count.h" namespace ns3 { class Object; class AttributeAccessor; class AttributeValue; class TraceSourceAccessor; struct ObjectDeleter { inline static void Delete (Object *object); }; /** * \ingroup core * \defgroup object Object */ /** * \ingroup object * \brief a base class which provides memory management and object aggregation * * The memory management scheme is based on reference-counting with dispose-like * functionality to break the reference cycles. The reference count is increamented * and decremented with the methods Object::Ref and Object::Unref. If a reference cycle is * present, the user is responsible for breaking it by calling Object::Dispose in * a single location. This will eventually trigger the invocation of Object::DoDispose * on itself and all its aggregates. The Object::DoDispose method is always automatically * invoked from the Object::Unref method before destroying the object, even if the user * did not call Object::Dispose directly. */ class Object : public SimpleRefCount { public: static TypeId GetTypeId (void); /** * \brief Iterate over the objects aggregated to an ns3::Object. * * This iterator does not allow you to iterate over the initial * object used to call Object::GetAggregateIterator. * * Note: this is a java-style iterator. */ class AggregateIterator { public: AggregateIterator (); /** * \returns true if HasNext can be called and return a non-null * pointer, false otherwise. */ bool HasNext (void) const; /** * \returns the next aggregated object. */ Ptr Next (void); private: friend class Object; AggregateIterator (Ptr object); Ptr m_object; uint32_t m_current; }; Object (); virtual ~Object (); /* * Implement the GetInstanceTypeId method defined in ObjectBase. */ virtual TypeId GetInstanceTypeId (void) const; /** * \returns a pointer to the requested interface or zero if it could not be found. */ template inline Ptr GetObject (void) const; /** * \param tid the interface id of the requested interface * \returns a pointer to the requested interface or zero if it could not be found. */ template Ptr GetObject (TypeId tid) const; /** * Run the DoDispose methods of this object and all the * objects aggregated to it. * After calling this method, the object is expected to be * totally unusable except for the Ref and Unref methods. * * Note that you can call Dispose many times on the same object or * different objects aggregated together, and DoDispose will be * called only once for each aggregated object. * * This method is typically used to break reference cycles. */ void Dispose (void); /** * \param other another object pointer * * This method aggregates the two objects together: after this * method returns, it becomes possible to call GetObject * on one to get the other, and vice-versa. * * This method calls the virtual method NotifyNewAggregates to * notify all aggregated objects that they have been aggregated * together. * * \sa NotifyNewAggregate */ void AggregateObject (Ptr other); /** * \returns an iterator to the first object aggregated to this * object. * * If no objects are aggregated to this object, then, the returned * iterator will be empty and AggregateIterator::HasNext will * always return false. */ AggregateIterator GetAggregateIterator (void) const; /** * This method calls the virtual DoInitialize method on all the objects * aggregated to this object. DoInitialize will be called only once over * the lifetime of an object, just like DoDispose is called only * once. * * \sa DoInitialize */ void Initialize (void); protected: /** * This method is invoked whenever two sets of objects are aggregated together. * It is invoked exactly once for each object in both sets. * This method can be overriden by subclasses who wish to be notified of aggregation * events. These subclasses must chain up to their base class NotifyNewAggregate method. * It is safe to call GetObject and AggregateObject from within this method. */ virtual void NotifyNewAggregate (void); /** * This method is called only once by Object::Initialize. If the user * calls Object::Initialize multiple times, DoInitialize is called only the * first time. * * Subclasses are expected to override this method and _chain up_ * to their parent's implementation once they are done. It is * safe to call GetObject and AggregateObject from within this method. */ virtual void DoInitialize (void); /** * This method is called by Object::Dispose or by the object's * destructor, whichever comes first. * * Subclasses are expected to implement their real destruction * code in an overriden version of this method and chain * up to their parent's implementation once they are done. * i.e., for simplicity, the destructor of every subclass should * be empty and its content should be moved to the associated * DoDispose method. * * It is safe to call GetObject from within this method. */ virtual void DoDispose (void); /** * \param o the object to copy. * * Allow subclasses to implement a copy constructor. * While it is technically possible to implement a copy * constructor in a subclass, we strongly discourage you * to do so. If you really want to do it anyway, you have * to understand that this copy constructor will _not_ * copy aggregated objects. i.e., if your object instance * is already aggregated to another object and if you invoke * this copy constructor, the new object instance will be * a pristine standalone object instance not aggregated to * any other object. It is thus _your_ responsability * as a caller of this method to do what needs to be done * (if it is needed) to ensure that the object stays in a * valid state. */ Object (const Object &o); private: template friend Ptr CopyObject (Ptr object); template friend Ptr CopyObject (Ptr object); template friend Ptr CompleteConstruct (T *object); friend class ObjectFactory; friend class AggregateIterator; friend struct ObjectDeleter; /** * This data structure uses a classic C-style trick to * hold an array of variable size without performing * two memory allocations: the declaration of the structure * declares a one-element array but when we allocate * memory for this struct, we effectively allocate a larger * chunk of memory than the struct to allow space for a larger * variable sized buffer whose size is indicated by the element * 'n' */ struct Aggregates { uint32_t n; Object *buffer[1]; }; Ptr DoGetObject (TypeId tid) const; bool Check (void) const; bool CheckLoose (void) const; /** * \param tid an TypeId * * Invoked from ns3::CreateObject only. * Initialize the m_tid member variable to * keep track of the type of this object instance. */ void SetTypeId (TypeId tid); /** * \param attributes the attribute values used to initialize * the member variables of this object's instance. * * Invoked from ns3::ObjectFactory::Create and ns3::CreateObject only. * Initialize all the member variables which were * registered with the associated TypeId. */ void Construct (const AttributeConstructionList &attributes); void UpdateSortedArray (struct Aggregates *aggregates, uint32_t i) const; /** * Attempt to delete this object. This method iterates * over all aggregated objects to check if they all * have a zero refcount. If yes, the object and all * its aggregates are deleted. If not, nothing is done. */ void DoDelete (void); /** * Identifies the type of this object instance. */ TypeId m_tid; /** * Set to true when the DoDispose method of the object * has run, false otherwise. */ bool m_disposed; /** * Set to true once the DoInitialize method has run, * false otherwise */ bool m_initialized; /** * a pointer to an array of 'aggregates'. i.e., a pointer to * each object aggregated to this object is stored in this * array. The array is shared by all aggregated objects * so the size of the array is indirectly a reference count. */ struct Aggregates * m_aggregates; /** * Indicates the number of times the object was accessed with a * call to GetObject. This integer is used to implement a * heuristic to sort the array of aggregates to put at the start * of the array the most-frequently accessed elements. */ uint32_t m_getObjectCount; }; /** * \param object a pointer to the object to copy. * \returns a copy of the input object. * * This method invoke the copy constructor of the input object * and returns the new instance. */ template Ptr CopyObject (Ptr object); template Ptr CopyObject (Ptr object); } // namespace ns3 namespace ns3 { void ObjectDeleter::Delete (Object *object) { object->DoDelete (); } /************************************************************************* * The Object implementation which depends on templates *************************************************************************/ template Ptr Object::GetObject () const { // This is an optimization: if the cast works (which is likely), // things will be pretty fast. T *result = dynamic_cast (m_aggregates->buffer[0]); if (result != 0) { return Ptr (result); } // if the cast does not work, we try to do a full type check. Ptr found = DoGetObject (T::GetTypeId ()); if (found != 0) { return Ptr (static_cast (PeekPointer (found))); } return 0; } template Ptr Object::GetObject (TypeId tid) const { Ptr found = DoGetObject (tid); if (found != 0) { return Ptr (static_cast (PeekPointer (found))); } return 0; } /************************************************************************* * The helper functions which need templates. *************************************************************************/ template Ptr CopyObject (Ptr object) { Ptr p = Ptr (new T (*PeekPointer (object)), false); NS_ASSERT (p->GetInstanceTypeId () == object->GetInstanceTypeId ()); return p; } template Ptr CopyObject (Ptr object) { Ptr p = Ptr (new T (*PeekPointer (object)), false); NS_ASSERT (p->GetInstanceTypeId () == object->GetInstanceTypeId ()); return p; } template Ptr CompleteConstruct (T *p) { p->SetTypeId (T::GetTypeId ()); p->Object::Construct (AttributeConstructionList ()); return Ptr (p, false); } template Ptr CreateObject (void) { return CompleteConstruct (new T ()); } template Ptr CreateObject (T1 a1) { return CompleteConstruct (new T (a1)); } template Ptr CreateObject (T1 a1, T2 a2) { return CompleteConstruct (new T (a1,a2)); } template Ptr CreateObject (T1 a1, T2 a2, T3 a3) { return CompleteConstruct (new T (a1,a2,a3)); } template Ptr CreateObject (T1 a1, T2 a2, T3 a3, T4 a4) { return CompleteConstruct (new T (a1,a2,a3,a4)); } template Ptr CreateObject (T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) { return CompleteConstruct (new T (a1,a2,a3,a4,a5)); } template Ptr CreateObject (T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6) { return CompleteConstruct (new T (a1,a2,a3,a4,a5,a6)); } template Ptr CreateObject (T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7) { return CompleteConstruct (new T (a1,a2,a3,a4,a5,a6,a7)); } } // namespace ns3 #endif /* OBJECT_H */