From 4c0c225a1928316fca026da2269a42f1f7f43735 Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Mon, 18 Dec 2006 13:17:08 +0100 Subject: [PATCH 1/5] initial go at smart pointer implementation --- SConstruct | 2 + src/core/ptr.cc | 103 ++++++++++++++++++ src/core/ptr.h | 270 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 375 insertions(+) create mode 100644 src/core/ptr.cc create mode 100644 src/core/ptr.h diff --git a/SConstruct b/SConstruct index 090b6b764..dda9a9192 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' ]) diff --git a/src/core/ptr.cc b/src/core/ptr.cc new file mode 100644 index 000000000..1c2b240b0 --- /dev/null +++ b/src/core/ptr.cc @@ -0,0 +1,103 @@ +/* -*- 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 (); +private: + Callback m_cb; +}; +NoCount::NoCount (Callback cb) + : m_cb (cb) +{} +NoCount::~NoCount () +{ + m_cb (); +} + +class PtrTest : Test +{ +public: + PtrTest (); + virtual ~PtrTest (); + virtual bool RunTests (void); +private: + void DestroyNotify (void); + bool m_destroyed; +}; + +PtrTest::PtrTest () + : Test ("Ptr") +{} + +PtrTest::~PtrTest () +{} + +void +PtrTest::DestroyNotify (void) +{ + m_destroyed = true; +} + + +bool +PtrTest::RunTests (void) +{ + bool ok = true; + + Callback cb = MakeCallback (&PtrTest::DestroyNotify, this); + m_destroyed = false; + { + Ptr p = new NoCount (cb); + } + if (!m_destroyed) + { + ok = false; + } + m_destroyed = false; + { + Ptr p; + p = new NoCount (cb); + } + if (!m_destroyed) + { + 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..ba496bf0b --- /dev/null +++ b/src/core/ptr.h @@ -0,0 +1,270 @@ +/* -*- 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); +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 *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); + + /** + * \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 * +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_ptr.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; +} + +}; // namespace ns3 + +#endif /* PTR_H */ From 3a88bb5f3215976891e259babc7760a112274b7d Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Mon, 18 Dec 2006 13:28:29 +0100 Subject: [PATCH 2/5] smart pointer tests. no bugs for now --- src/core/ptr.cc | 117 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 110 insertions(+), 7 deletions(-) diff --git a/src/core/ptr.cc b/src/core/ptr.cc index 1c2b240b0..97e26f232 100644 --- a/src/core/ptr.cc +++ b/src/core/ptr.cc @@ -51,7 +51,8 @@ public: virtual bool RunTests (void); private: void DestroyNotify (void); - bool m_destroyed; + Ptr CallTest (Ptr p); + uint32_t m_nDestroyed; }; PtrTest::PtrTest () @@ -64,9 +65,13 @@ PtrTest::~PtrTest () void PtrTest::DestroyNotify (void) { - m_destroyed = true; + m_nDestroyed++; +} +Ptr +PtrTest::CallTest (Ptr p) +{ + return p; } - bool PtrTest::RunTests (void) @@ -74,20 +79,118 @@ PtrTest::RunTests (void) bool ok = true; Callback cb = MakeCallback (&PtrTest::DestroyNotify, this); - m_destroyed = false; + m_nDestroyed = false; { Ptr p = new NoCount (cb); } - if (!m_destroyed) + if (m_nDestroyed != 1) { ok = false; } - m_destroyed = false; + + m_nDestroyed = 0; { Ptr p; p = new NoCount (cb); } - if (!m_destroyed) + 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; } From 735a076eb32e4a581badb92628dc1f73729bed7e Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Mon, 18 Dec 2006 14:25:33 +0100 Subject: [PATCH 3/5] more tests, fix bugs uncovered by tests --- src/core/ptr.cc | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ src/core/ptr.h | 15 ++++++++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/core/ptr.cc b/src/core/ptr.cc index 97e26f232..47f2829d5 100644 --- a/src/core/ptr.cc +++ b/src/core/ptr.cc @@ -52,6 +52,7 @@ public: private: void DestroyNotify (void); Ptr CallTest (Ptr p); + Ptr const CallTestConst (Ptr const p); uint32_t m_nDestroyed; }; @@ -73,6 +74,12 @@ PtrTest::CallTest (Ptr p) return p; } +Ptr const +PtrTest::CallTestConst (Ptr const p) +{ + return p; +} + bool PtrTest::RunTests (void) { @@ -92,6 +99,7 @@ PtrTest::RunTests (void) { Ptr p; p = new NoCount (cb); + p = p; } if (m_nDestroyed != 1) { @@ -194,6 +202,48 @@ PtrTest::RunTests (void) { 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; + } return ok; diff --git a/src/core/ptr.h b/src/core/ptr.h index ba496bf0b..4b4c9c202 100644 --- a/src/core/ptr.h +++ b/src/core/ptr.h @@ -53,6 +53,7 @@ private: }; static uint32_t *AllocCount (void); static void DeallocCount (uint32_t *count); + friend class Ptr; public: /** * Create an empty smart pointer @@ -93,6 +94,10 @@ public: template inline friend bool operator != (T1 const *lhs, Ptr &rhs); + template + inline friend Ptr const_pointer_cast (Ptr const&p); + + /** * \returns raw pointer * @@ -233,7 +238,7 @@ template T * Ptr::Remove (void) { - assert (m_ptr.m_count == 1); + assert ((*m_count) == 1); T *retval = m_ptr; m_ptr = 0; return retval; @@ -265,6 +270,14 @@ 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 */ From b37a9d44e899f36f618f76912da6dc903466b7c3 Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Fri, 22 Dec 2006 09:03:09 +0100 Subject: [PATCH 4/5] add smart pointer sample code --- SConstruct | 6 ++++ samples/main-ptr.cc | 76 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 samples/main-ptr.cc diff --git a/SConstruct b/SConstruct index dda9a9192..af7467119 100644 --- a/SConstruct +++ b/SConstruct @@ -173,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; +} From 041fcd17527320495224371f8e06c1e107776320 Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Wed, 31 Jan 2007 20:14:23 +0100 Subject: [PATCH 5/5] add ns3::Ptr::operator * with a few tests --- src/core/ptr.cc | 18 ++++++++++++++++++ src/core/ptr.h | 8 ++++++++ 2 files changed, 26 insertions(+) diff --git a/src/core/ptr.cc b/src/core/ptr.cc index 47f2829d5..a9ba89970 100644 --- a/src/core/ptr.cc +++ b/src/core/ptr.cc @@ -32,6 +32,7 @@ class NoCount public: NoCount (Callback cb); ~NoCount (); + void Nothing () const; private: Callback m_cb; }; @@ -42,6 +43,9 @@ NoCount::~NoCount () { m_cb (); } +void +NoCount::Nothing () const +{} class PtrTest : Test { @@ -244,6 +248,20 @@ PtrTest::RunTests (void) } 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; diff --git a/src/core/ptr.h b/src/core/ptr.h index 4b4c9c202..06ea24c76 100644 --- a/src/core/ptr.h +++ b/src/core/ptr.h @@ -75,6 +75,7 @@ public: Ptr (Ptr const &o); ~Ptr () ; Ptr &operator = (Ptr const& o); + T const& operator * () const; T *operator -> () const; T *operator -> (); // allow if (!sp) @@ -202,6 +203,13 @@ Ptr::operator = (Ptr const& o) return *this; } +template +T const& +Ptr::operator * () const +{ + return *m_ptr; +} + template T * Ptr::operator -> ()