diff --git a/SConstruct b/SConstruct index 090b6b764..af7467119 100644 --- a/SConstruct +++ b/SConstruct @@ -18,6 +18,7 @@ ns3.add(core) core.add_sources([ 'reference-list-test.cc', 'callback-test.cc', + 'ptr.cc', 'test.cc' ]) env = Environment() @@ -37,6 +38,7 @@ core.add_inst_headers([ 'system-wall-clock-ms.h', 'reference-list.h', 'callback.h', + 'ptr.h', 'test.h' ]) @@ -171,6 +173,12 @@ ns3.add(sample_callback) sample_callback.add_dep('core') sample_callback.add_source('main-callback.cc') +sample_ptr = build.Ns3Module('sample-ptr', 'samples') +sample_ptr.set_executable() +ns3.add(sample_ptr) +sample_ptr.add_dep('core') +sample_ptr.add_source('main-ptr.cc') + sample_trace = build.Ns3Module('sample-trace', 'samples') ns3.add(sample_trace) sample_trace.add_dep('common') diff --git a/samples/main-ptr.cc b/samples/main-ptr.cc new file mode 100644 index 000000000..3fea325ba --- /dev/null +++ b/samples/main-ptr.cc @@ -0,0 +1,76 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +#include "ns3/ptr.h" +#include + +using namespace ns3; + +class A +{ +public: + A (); + ~A (); + void Method (void); +}; +A::A () +{ + std::cout << "A constructor" << std::endl; +} +A::~A() +{ + std::cout << "A destructor" << std::endl; +} +void +A::Method (void) +{ + std::cout << "A method" << std::endl; +} + +static Ptr g_a = 0; + +static Ptr +StoreA (Ptr a) +{ + Ptr prev = g_a; + g_a = a; + return prev; +} + +static void +ClearA (void) +{ + g_a = 0; +} + + + +int main (int argc, char *argv[]) +{ + { + // Create a new object of type A, store it in global + // variable g_a + Ptr a = new A (); + a->Method (); + Ptr prev = StoreA (a); + assert (prev == 0); + } + + { + // Create a new object of type A, store it in global + // variable g_a, get a hold on the previous A object. + Ptr a = new A (); + Ptr prev = StoreA (a); + // call method on object + prev->Method (); + // Clear the currently-stored object + ClearA (); + // remove the raw pointer from its smart pointer. + // we can do this because the refcount is exactly one + // here + A *raw = prev.Remove (); + raw->Method (); + delete raw; + } + + + return 0; +} diff --git a/src/core/ptr.cc b/src/core/ptr.cc new file mode 100644 index 000000000..a9ba89970 --- /dev/null +++ b/src/core/ptr.cc @@ -0,0 +1,274 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2005,2006 INRIA + * All rights reserved. + * + * 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 + * + * Author: Mathieu Lacage + */ +#include "ptr.h" + +#ifdef RUN_SELF_TESTS + +#include "test.h" +#include "callback.h" + +namespace ns3 { + +class NoCount +{ +public: + NoCount (Callback cb); + ~NoCount (); + void Nothing () const; +private: + Callback m_cb; +}; +NoCount::NoCount (Callback cb) + : m_cb (cb) +{} +NoCount::~NoCount () +{ + m_cb (); +} +void +NoCount::Nothing () const +{} + +class PtrTest : Test +{ +public: + PtrTest (); + virtual ~PtrTest (); + virtual bool RunTests (void); +private: + void DestroyNotify (void); + Ptr CallTest (Ptr p); + Ptr const CallTestConst (Ptr const p); + uint32_t m_nDestroyed; +}; + +PtrTest::PtrTest () + : Test ("Ptr") +{} + +PtrTest::~PtrTest () +{} + +void +PtrTest::DestroyNotify (void) +{ + m_nDestroyed++; +} +Ptr +PtrTest::CallTest (Ptr p) +{ + return p; +} + +Ptr const +PtrTest::CallTestConst (Ptr const p) +{ + return p; +} + +bool +PtrTest::RunTests (void) +{ + bool ok = true; + + Callback cb = MakeCallback (&PtrTest::DestroyNotify, this); + m_nDestroyed = false; + { + Ptr p = new NoCount (cb); + } + if (m_nDestroyed != 1) + { + ok = false; + } + + m_nDestroyed = 0; + { + Ptr p; + p = new NoCount (cb); + p = p; + } + if (m_nDestroyed != 1) + { + ok = false; + } + + m_nDestroyed = 0; + { + Ptr p1; + p1 = new NoCount (cb); + Ptr p2 = p1; + } + if (m_nDestroyed != 1) + { + ok = false; + } + + m_nDestroyed = 0; + { + Ptr p1; + p1 = new NoCount (cb); + Ptr p2; + p2 = p1; + } + if (m_nDestroyed != 1) + { + ok = false; + } + + m_nDestroyed = 0; + { + Ptr p1; + p1 = new NoCount (cb); + Ptr p2 = new NoCount (cb); + p2 = p1; + } + if (m_nDestroyed != 2) + { + ok = false; + } + + m_nDestroyed = 0; + { + Ptr p1; + p1 = new NoCount (cb); + Ptr p2; + p2 = new NoCount (cb); + p2 = p1; + } + if (m_nDestroyed != 2) + { + ok = false; + } + + m_nDestroyed = 0; + { + Ptr p1; + p1 = new NoCount (cb); + p1 = new NoCount (cb); + } + if (m_nDestroyed != 2) + { + ok = false; + } + + m_nDestroyed = 0; + { + Ptr p1; + { + Ptr p2; + p1 = new NoCount (cb); + p2 = new NoCount (cb); + p2 = p1; + } + if (m_nDestroyed != 1) + { + ok = false; + } + } + if (m_nDestroyed != 2) + { + ok = false; + } + + m_nDestroyed = 0; + { + Ptr p1; + { + Ptr p2; + p1 = new NoCount (cb); + p2 = new NoCount (cb); + p2 = CallTest (p1); + } + if (m_nDestroyed != 1) + { + ok = false; + } + } + if (m_nDestroyed != 2) + { + ok = false; + } + + { + Ptr p1; + Ptr const p2 = CallTest (p1); + Ptr const p3 = CallTestConst (p1); + Ptr p4 = CallTestConst (p1); + Ptr p5 = p4; + //p4 = p5; You cannot make a const pointer be a non-const pointer. + // but if you use const_pointer_cast, you can. + p4 = const_pointer_cast (p5); + p5 = p1; + Ptr p; + if (p == 0) + {} + if (p != 0) + {} + if (0 == p) + {} + if (0 != p) + {} + if (p) + {} + if (!p) + {} + } + + m_nDestroyed = 0; + { + NoCount *raw; + { + Ptr p = new NoCount (cb); + { + Ptr p1 = p; + } + raw = p.Remove (); + } + if (m_nDestroyed != 0) + { + ok = false; + } + delete raw; + } + + + m_nDestroyed = 0; + { + Ptr p = new NoCount (cb); + NoCount const&v1 = *p; + NoCount v2 = *p; + v1.Nothing (); + v2.Nothing (); + } + if (m_nDestroyed != 2) + { + ok = false; + } + + + return ok; +} + +PtrTest g_ptr_test; + +}; // namespace ns3 + +#endif /* RUN_SELF_TESTS */ diff --git a/src/core/ptr.h b/src/core/ptr.h new file mode 100644 index 000000000..06ea24c76 --- /dev/null +++ b/src/core/ptr.h @@ -0,0 +1,291 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2005,2006 INRIA + * All rights reserved. + * + * 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 + * + * Author: Mathieu Lacage + */ + +#ifndef PTR_H +#define PTR_H + +#include +#include + +namespace ns3 { + +/** + * \brief smart pointer class similar to boost::shared_ptr + * + * This smart-pointer class is supposed to be used to manage + * heap-allocated objects: when it decides it does not need + * the object it references, it invokes operator delete on it. + * This implementation allows you to manipulate the smart pointer + * as if it was a normal pointer: you can compare it with zero, + * compare it against other pointers, etc. However, the only + * operation we are careful to avoid is the conversion back to + * raw pointers: if you need to convert back, you need to invoke + * the Ptr::Remove method which returns a raw pointer and + * makes the smart pointer forget about the raw pointer. + */ +template +class Ptr +{ +private: + T *m_ptr; + uint32_t *m_count; + class Tester { + private: + void operator delete (void *); + }; + static uint32_t *AllocCount (void); + static void DeallocCount (uint32_t *count); + friend class Ptr; +public: + /** + * Create an empty smart pointer + */ + Ptr (); + /** + * \param ptr raw pointer to manage + * + * Create a smart pointer which points to the + * input raw pointer. This method takes ownershipt + * of the input raw pointer. That is, the smart pointer + * becomes responsible for calling delete on the + * raw pointer when needed. + */ + Ptr (T *ptr); + Ptr (Ptr const&o); + // allow conversions from T to T const. + template + Ptr (Ptr const &o); + ~Ptr () ; + Ptr &operator = (Ptr const& o); + T const& operator * () const; + T *operator -> () const; + T *operator -> (); + // allow if (!sp) + bool operator! (); + // allow if (sp) + operator Tester * () const; + // allow if (sp == 0) + template + inline friend bool operator == (Ptr const &lhs, T2 const *rhs); + // allow if (0 == sp) + template + inline friend bool operator == (T1 const *lhs, Ptr &rhs); + // allow if (sp != 0) + template + inline friend bool operator != (Ptr const &lhs, T2 const *rhs); + // allow if (0 != sp) + template + inline friend bool operator != (T1 const *lhs, Ptr &rhs); + + template + inline friend Ptr const_pointer_cast (Ptr const&p); + + + /** + * \returns raw pointer + * + * It is a programming error to invoke this method when + * the reference count of the smart pointer is not one. + * If you try to do it anyway, an assert will be triggered. + * If asserts are disabled, bad things will happen. + * Once you have successfully called Ptr::Remove on + * a smart pointer, the smart pointer will forget + * about the raw pointer and will stop managing it. As such, + * you, as the caller, become responsible for invoking + * operator delete on the returned raw pointer. + */ + T *Remove (void); +}; + +template +uint32_t * +Ptr::AllocCount (void) +{ + return new uint32_t [1] (); +} +template +void +Ptr::DeallocCount (uint32_t *count) +{ + delete [] count; +} + +template +Ptr::Ptr () + : m_ptr (0), + m_count (Ptr::AllocCount ()) +{} + +template +Ptr::Ptr (T *ptr) + : m_ptr (ptr), + m_count (Ptr::AllocCount ()) +{ + if (m_ptr != 0) + { + *m_count = 1; + } +} + +template +Ptr::Ptr (Ptr const&o) + : m_ptr (o.m_ptr), + m_count (o.m_count) +{ + if (m_ptr != 0) + { + (*m_count)++; + } +} +template +template +Ptr::Ptr (Ptr const &o) + : m_ptr (o.m_ptr), + m_count (o.m_count) +{ + if (m_ptr != 0) + { + (*m_count)++; + } +} + +template +Ptr::~Ptr () +{ + if (m_ptr != 0) + { + (*m_count)--; + if ((*m_count) == 0) + { + delete m_ptr; + Ptr::DeallocCount (m_count); + } + } +} + +template +Ptr & +Ptr::operator = (Ptr const& o) +{ + if (o.m_ptr != 0) + { + (*(o.m_count))++; + } + if (m_ptr != 0) + { + (*m_count)--; + if ((*m_count) == 0) + { + delete m_ptr; + Ptr::DeallocCount (m_count); + } + } + m_ptr = o.m_ptr; + m_count = o.m_count; + return *this; +} + +template +T const& +Ptr::operator * () const +{ + return *m_ptr; +} + +template +T * +Ptr::operator -> () +{ + return m_ptr; +} + +template +T * +Ptr::operator -> () const +{ + return m_ptr; +} + +template +bool +Ptr::operator! () +{ + return m_ptr == 0; +} + +template +Ptr::operator Tester * () const +{ + if (m_ptr == 0) + { + return 0; + } + static Tester test; + return &test; +} + +template +T * +Ptr::Remove (void) +{ + assert ((*m_count) == 1); + T *retval = m_ptr; + m_ptr = 0; + return retval; +} + +// non-member friend functions. +template +bool +operator == (Ptr const &lhs, T2 const *rhs) +{ + return lhs.m_ptr == rhs; +} +template +bool +operator == (T1 const *lhs, Ptr &rhs) +{ + return lhs == rhs.m_ptr; +} +template +bool +operator != (Ptr const &lhs, T2 const *rhs) +{ + return lhs.m_ptr != rhs; +} +template +bool +operator != (T1 const *lhs, Ptr &rhs) +{ + return lhs != rhs.m_ptr; +} + +template +Ptr +const_pointer_cast (Ptr const&p) +{ + return Ptr (const_cast (p.m_ptr)); +} + + +}; // namespace ns3 + +#endif /* PTR_H */