|
From: Teiniker E. <tei...@us...> - 2007-01-19 11:58:16
|
Update of /cvsroot/ccmtools/cpp-environment/utils/smartptr In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv1313/utils/smartptr Added Files: linkassert.h smartptr.h Confix2.dir smartptr.cc Log Message: Added utils package for the case that wx-utils are not installed. --- NEW FILE: linkassert.h --- // -*- mode: C++; c-basic-offset: 3 -*- // // $Id$ // #ifndef wx_utils_code_linkassert_h #define wx_utils_code_linkassert_h namespace wamas { namespace platform { namespace utils { /** \page page_utils_code_linkassert Link Time Assertions \see LTA_MEMDECL \see LTA_MEMDEF \see LTA_STATDEF \section utils_code_linkassert What is that? Best to explain the problem in C first because it is more explicit there. Suppose you have a header file containing the following declaration, plus a "member" function: \code struct foo { long l1; long l2; }; extern void foo_print_l2(const struct foo*); \endcode In the implementation (the .c file), suppose you define <tt>foo_print_l2()</tt> as follows: \code void foo_print_l2(const struct foo* f) { printf ("%ld\n", f->l2) ; } \endcode Now, you change the declaration of <tt>struct foo</tt> to read \code struct foo { long l1; long l1_5; long l2; }; \endcode (note the move of \c l2 by 1 long in the memory layout) and you do \em not recompile your .c file to tell the function <tt>foo_print_l2()</tt> to use the new offset of \c l2 from the base pointer, the function will print <tt>foo->l1_5</tt> instead of <tt>foo->l2</tt>, if it is linked with a fresh executable. (This whole thing applies to classes as well (which are structs basically)). A general precaution to this is to reference a variable from the .h file that is defined in the .c file. In C, before the change, this would read in the header file \code struct foo { long l1; long l2; }; extern int foo_version1; // you have to explicitly reference the foo_version1 so that the // compiler does not ignore the declaration and thus does not // reference it static int foo_version = foo_version1; \endcode and in the .c file \code int foo_version1; \endcode If you change the memory layout of the struct, you somehow have to force a recompile of the .c file. Do this through a change of the name of the extern variable. In the .h file write \code struct foo { long l1; long l1_5; long l2; }; extern int foo_version2; static int foo_version = foo_version2; \endcode In the .c file write \code int foo_version2; \endcode Thus, if you make the .h file publicly available, and an executable compiled with this new <tt>struct foo</tt> memory layout references the <tt>foo_print_l2()</tt> function, it must also reference the <tt>foo_version2</tt> extern variable. If the executable is linked with the old .o file, the linker won't be able to resolve the reference because the old .o file still contains the old variable <tt>foo_version1</tt>. We call this (most sophisticatedly) a <em>link time assertion</em>. In C++, this could be written as follows using static members. No matter how you view it, in C or C++, the principle is the same. The .h file: \code class Foo { public: // something ... public: static const char* version1; } ; static const char* Foo_version = Foo::version1; \endcode The .cc file: \code // we use a char* for the variable (formatted like this) because // this enables us to grep a binary for a particular class // version/revision const char* Foo::version1 = "Foo"; \endcode \section utils_code_linkassert_when When to increment the version number To summarize, the rules of thumb as to when to change the <em>link time assertion symbol</em> (e.g. increment <tt>version1</tt> to <tt>version2</tt> in the example) are: <ul> <li>You added and/or removed a member variable</li> <li>You added and/or removed a virtual method (this changes the layout of the vtable (or so))</li> <li>You added and/or removed a base class (a base class can be thought of as an implicit member)</li> <li>You added and/or removed the <tt>virtual</tt> keyword to/from a base class</li> <li>You need not increment the version if you added/removed a non-virtual method.</li> </ul> */ /** \def LTA_MEMDECL(num) \ingroup utils_code \brief Declare class member with version number \a num. You write this macro in the class definition, about where you would declare the member (in fact, the macro declares the member for you). See \ref page_utils_code_linkassert for a description. */ #define LTA_MEMDECL(num) static const char* version##num /** \def LTA_MEMDEF(class, num, rhs) \ingroup utils_code \brief Implement member of \a class with version number \a and the value \a rhs. \a rhs is a string. You write this macro in the .cc file, just where you would implement the static member you declared with LTA_MEMDECL. See \ref page_utils_code_linkassert for a description. */ #define LTA_MEMDEF(class, num, rhs) const char* class::version##num = rhs /** \def LTA_STATDEF(class, num) \ingroup utils_code \brief Reference the static member. Referencing the static member is necessary. It persuades the compiler to generate a reference to the member, and to not optimize it away. See \ref page_utils_code_linkassert for a description. */ #ifdef __PXWC__ #define LTA_STATDEF(class, num) #else #define LTA_STATDEF(class, num) static const char* class##_##version##num = class::version##num #endif } // /namespace } // /namespace } // /namespace #endif --- NEW FILE: smartptr.h --- #ifndef wx_utils_code_smartptr_h #define wx_utils_code_smartptr_h #include "atomic_count.h" #include "linkassert.h" #include <iostream> #ifdef WX_REFCOUNTED_COLLECT_NEW #include <map> #endif namespace wamas { namespace platform { namespace utils { /** \brief Base class for all classes which are supposed to be pointed to by a smart pointer. Actually doesn't do anything, but rather provides the interface. \ingroup utils_code \author Joerg Faschingbauer \b COPYING \b A \b REFERENCE \b COUNTED \b THING Copying a refcounted thing is not straightforward yet it is simple. In an ideal world you would pass a class object either by copy or by using a smart pointer. The former method does not care about anything, it just copies, whereas the latter method maintains a reference count inside the object that is being passed. Everything is fine if you don't intermix both methods (which you shouldn't unless there is need to AND you know what you're doing). It gets more complicated if, for example, \b COPY \b CONSTRUCTOR we take a copy of a smartpointed object and pass that copy around using a smart pointer: we get a smart pointer xp that points to some object, say *x. \code SmartPtr<X> xp = get_object(); \endcode Now assume that *x, after execution of that statement, has a refcount of 2 (x->refcount()==2). In other words, another smart pointer (other than xp) points to it somewhere outside our scope. We now copy that object *x (not the smart pointer xp) into another object, *y. \code X* y = new X(*xp.ptr()); \endcode The question is: what reference count must *y now have? Obviously, since the reference count reflects the number of smart pointers pointing at it (well, ideally; one can always dirtily ref() and unref() by hand), the reference count of *y must be ZERO. Otherwise, if we would pass *y around using smart pointers (which would then be responsible for cleanup), we would leak memory. \code XPtr yp = XPtr(y); pass_around(yp); // leak \endcode \b ASSIGNMENT \b OPERATOR Now for the assignment operator. Suppose we get an object *x which is pointed to by, say, 2 smart pointers (x->refcount()==2), one of them being xp. \code SmartPtr<X> xp = get_object(); \endcode Now, eagerly crying for subtle bugs, we want to replace the content of *x with that of some other object newx of class X. \code X newx(...); *xp.ptr() = newx; \endcode The question is: what must be the reference count of *x? Obviously, since the number of smart pointers pointing at it does not change, the reference count must not change, it must still be 2 as before the assignment. */ class RefCounted { public: RefCounted() : refcount_(0) {} virtual ~RefCounted() {} /** @name see the big note above for copy semantics */ //@{ RefCounted(const RefCounted&) : refcount_(0) {} RefCounted& operator=(const RefCounted&) { return *this; } //@} /// increment reference count virtual void ref(); /// decrement reference count virtual void unref(); /// return the reference count long refcount() const { return refcount_; } /** @name simple wrappers to accommodate null pointers */ //@{ static void ref(RefCounted* r) { if (r) r->ref(); } static void unref(RefCounted* r) { if (r) r->unref(); } //@} #ifdef WX_REFCOUNTED_COLLECT_NEW void* operator new (std::size_t); void operator delete (void*); typedef std::map<std::size_t, long> NewStatistics; static NewStatistics new_statistics_; #endif private: atomic_count refcount_; public: LTA_MEMDECL(20061107); }; LTA_STATDEF(RefCounted, 20061107); /** \class SmartPtr \ingroup utils_code \brief Template class which manages pointers to RefCounted objects. \author Joerg Faschingbauer Smart pointers are supposed to be used in cases where you do not want to care about memory ownerships. For example, when you pass a pointer to an object to a function, you and the function have to agree upon who is responsible for deleting the object. You use a smart pointer to automatically accomplish that. You wrap a smart pointer around the raw pointer, and pass the smart pointer instead (of course, the function must be prepared to expect a smart pointer). If you are not interested in the object after the call, you simply let the smart pointer go out of scope. The smart pointer's destructor will then decrement the reference count of the pointed-to object, and, if the reference count has become zero, delete it. If you are interested in keeping the object, you store the smart pointer somewhere. This prevents the object's reference count from becoming zero. The function, on the other hand, may decide to store the smart pointer somewhere (and keep a reference on it), or to only temporarily use it and then forget about it (let it go out of scope). */ template <class T> class SmartPtr { public: /** Default constructor. Initially a smart pointer points to nothing (i.e., has the value 0). */ SmartPtr(): ptr_(0) {} /** "Eating" constructor. The object the argument points to is now managed by the smart pointer object. Hence it must not be deleted explicitly afterwards. */ explicit SmartPtr(T* p): ptr_(p) { RefCounted::ref(ptr_); } /** Copy constructor, template version (to be able to assign derived pointees). Increments the reference count on the object by one. */ template<class RHS> SmartPtr(const SmartPtr<RHS>& p): ptr_(0) { operator=(p); } /** Copy constructor, non-template version. Increments the reference count on the object by one. Implementor's note: one would think that the emplate version of the copy ctor should be used by the compiler to generate the default copy ctor. But that's not how C++ is defined - if we do not define an explicit non-template copy ctor, we would get a real default copy ctor which only copies the pointer and does no refcounting. */ SmartPtr(const SmartPtr& p): ptr_(0) { operator=(p); } /** Destructor. Decrements the reference count by one. Deletes the object (better to say, the object deletes itself) if the reference count becomes zero. */ ~SmartPtr() { RefCounted::unref(ptr_); } /** Assignment operator, template version. Same semantics as template copy constructor. */ template<class RHS> SmartPtr& operator=( const SmartPtr<RHS>& p) { if (this->ptr() != p.ptr()) { RefCounted::ref(p.ptr()); RefCounted::unref(this->ptr()); ptr_ = p.ptr(); } return *this; } /** Assignment operator, non-template version. Same semantics as copy constructor. Implementor's note: we have to define a non-template version for the same reason why we have to define a non-template copy ctor. */ SmartPtr& operator=( const SmartPtr& p) { if (this->ptr() != p.ptr()) { RefCounted::ref(p.ptr()); RefCounted::unref(this->ptr()); ptr_ = p.ptr(); } return *this; } /** Acquire responsibility for the object (and increment its reference count). An eventual existing responsibility for a different object is canceled, and that reference is decremented. */ template<class RHS> void eat(RHS* p) { if (p == ptr_) return; RefCounted::unref(ptr_); ptr_ = p; RefCounted::ref(p); } /** Become a NULL pointer. Unreference an object that we are pointing to. */ void forget() { if (ptr_) { RefCounted::unref(ptr_); ptr_ = NULL; } } /** \name Smart pointer comparison */ //@{ operator bool() const { // Doze bullshit yells "Performance warning" if I don't cast // explicitly return static_cast<bool>(ptr_); } bool operator !() const { return !ptr_; } bool operator< (const SmartPtr& that) const { return ptr_< that.ptr_; } bool operator==(const SmartPtr& that) const { return ptr_==that.ptr_; } bool operator> (const SmartPtr& that) const { return ptr_> that.ptr_; } bool operator<=(const SmartPtr& that) const { return ptr_<=that.ptr_; } bool operator>=(const SmartPtr& that) const { return ptr_>=that.ptr_; } //@} /** A pointer to the object, modifiable. */ T* ptr() const { return ptr_; } /** A pointer to the object, modifiable, trading off for readability. */ T* operator->() { return ptr_; } /** A pointer to the object, non-modifiable. */ const T* cptr() const { return ptr_; } /** A pointer to the object, non-modifiable, trading off for readability. */ const T* operator->() const { return ptr_; } private: T* ptr_; }; } // /namespace } // /namespace } // /namespace #endif --- NEW FILE: Confix2.dir --- --- NEW FILE: smartptr.cc --- #include "smartptr.h" namespace wamas { namespace platform { namespace utils { LTA_MEMDEF(RefCounted, 20061107, "$Id$"); void RefCounted::ref() { ++refcount_; } void RefCounted::unref() { if (!--refcount_) delete this; } #ifdef WX_REFCOUNTED_COLLECT_NEW RefCounted::NewStatistics RefCounted::new_statistics_; void* RefCounted::operator new (std::size_t s) { NewStatistics::iterator pos = new_statistics_.find(s); if(pos==new_statistics_.end()) new_statistics_[s] = 1; else ++(pos->second); return malloc(s); } void RefCounted::operator delete (void* p) { free(p); } #endif // WX_REFCOUNTED_COLLECT_NEW } // /namespace } // /namespace } // /namespace |