#ifndef __NamedReference_h_ #define __NamedReference_h_ #include #include #include /* A NamedReference is like a pointer in Java. It has automatic garbage * collection. (Unlike Java it's done through reference counts.) The name is * like a reference in Java. The name will not keep the object alive. Using * the name you can ask for a NamedReference. That will return NULL if the * underlying object has been deleted. * * A NamedReference will always point to an object of type HasNamedReference. * HasNamedReference is a rather dull object. Its main purpose is to have a * virtual destructor. Our previous smart pointers were templates. The way * we plan to use this, you want one object that can hold any number of types * of objects. * * This was originally aimed at our scripting. When you have a variable in * the scripting language, it can contain several different types of objects. * * NamedReference can contain a NULL. All methods of NamedReference will work * just fine on a NULL. * * NamedReference is explicitly thread safe. This follows the normal posix * conventions. Two threads should not access a NamedReference object at the * same time. However, it is safe to have two NamedReference objects pointing * to the same data (i.e. a = b;) then having one thread work with one of those * object, while another thread works on the other object. * * Presumably the classes derived from HasNamedReference will also be thread * safe. However, that's not required. This file does not make or use any * threads. This implementation should be fast even in a single threaded * program. There would be no reason to make similar functionality in a non- * thread-safe class. * * Performance: There is a small penalty the first time you wrap a pointer * in a smart pointer, and when you release the last smart pointer and delete * the original object. Otherwise performance should be the same as other * smart pointers. Those two slow operations each include a global mutex and a * lookup in an O(log(N)) data structure. The other operations use atomic- * increment, and use a lot of inline code. * * It wasn't exactly clear how much inline code was required. Most other smart * pointers are templates so they were all inline. I kept most of this code * inline just to compete. */ class HasNamedReference { private: static uint64_t _nextId; const std::string _name; static std::string createName(); void *const _threadId; protected: HasNamedReference(bool onlyThisThread = false); public: std::string const &getName() const { return _name; } // Mostly aimed at debugging. Use the as() function, below, to see if // something is the expected type. virtual std::string getTypeName() const; virtual ~HasNamedReference() { } bool correctThread() const; }; class NamedReference { private: struct Container { uint64_t count; // basePointer is never NULL. Instead make _container NULL. HasNamedReference *basePointer; }; static std::map< std::string, Container * > _allNames; static std::string s_NULL; Container *_container; void initializeNotNull(HasNamedReference *basePointer); void destroy(); void decrement() { if (!_container) return; const uint64_t before = __sync_fetch_and_sub(&_container->count, 1); if (before == 1) destroy(); } void increment() const { if (_container) __sync_fetch_and_add(&_container->count, 1); } public: // Returns null if not found. static NamedReference find(std::string const &name); // The inverse of find(). This should be safe to display to a user. This // won't make a lot of sense to a user. This is just a magic cookie. You // should not try to parse or otherwise manipulate these. std::string getName() const { return _container?_container->basePointer->getName():s_NULL; } NamedReference(HasNamedReference *basePointer = NULL) { if (basePointer) initializeNotNull(basePointer); else _container = NULL; } NamedReference(NamedReference const &other) : _container(other._container) { increment(); } // Standard pointer operations. operator bool() const { return _container; } bool operator !() const { return !_container; } bool operator ==(NamedReference const &other) const { return _container == other._container; } bool operator !=(NamedReference const &other) const { return _container != other._container; } bool operator <(NamedReference const &other) const { return _container < other._container; } void operator =(NamedReference const &other) { other.increment(); decrement(); _container = other._container; } void clear() { decrement(); _container = NULL; } // Small optimization. If we didn't have this, NULL would result in a call // to the NamedReference constructor. The result would be the same. These // are all the same as operations that already exist above. It's tempting // just to tell people to use those operations. But in practice people // forget and use these operations. bool operator ==(std::nullptr_t) const { return _container;} bool operator !=(std::nullptr_t) const { return !_container;} void operator =(std::nullptr_t) { clear(); } // This is a wrapper around dynamic_cast(). This will return NULL if the // value was already NULL, or if the value cannot be cast to the requested // type. // // Warning: Don't save this pointer unless you are also saving an unmodified // copy of the NamedReference object. template< typename T > T *as() const { return _container?dynamic_cast< T * >(_container->basePointer):NULL; } ~NamedReference() { decrement(); } // Aimed at debugging. Same as getAllNames.size(), but much faster. static uint64_t getObjectCount(); // Aimed at debugging. Every name that maps to an object. static std::vector< std::string > getAllNames(); }; #endif