/*************************************************************************** $RCSfile: ctpointer.h,v $ ------------------- cvs : $Id: ctpointer.h,v 1.3 2003/01/10 20:02:16 aquamaniac Exp $ begin : Tue Dec 13 2001 copyright : (C) 2001 by Martin Preuss email : martin@aquamaniac.de *************************************************************************** * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * * MA 02111-1307 USA * * * ***************************************************************************/ #ifndef CTPOINTER_H #define CTPOINTER_H #include /* DEBUG */ #include #include class CTPointerBase; /* template class CHIPCARD_API CTPointer; */ class CTPointerObject; template class CTPointerCastBase; template class CTPointerCast; #ifndef DOXYGEN /** * This internal class is created by CTPointer. It holds the real pointer * and an usage counter. You can neither create nor detroy such an object. * @author Martin Preuss */ class CHIPCARD_API CTPointerObject { friend class CTPointerBase; private: void *_object; int _counter; bool _delete; string _descr; CTPointerObject(void *obj, string descr=""): _object(obj),_counter(0) ,_delete(true) ,_descr(descr){ }; ~CTPointerObject(){ }; void setDescription(string descr) { /* if (_descr.empty()) */ _descr=descr; }; const string &description() const { return _descr; }; int counter() const { return _counter;}; public: }; #endif /* DOXYGEN */ /** * @short Base class for the smart pointer template class. * * This is the base class to be inherited by a template class. This * cannot be used directly. * * @author Martin Preuss * @ingroup misc */ class CHIPCARD_API CTPointerBase { #ifndef DOXYGEN private: CTPointerObject *_ptr; string _descr; protected: void _attach(CTPointerObject &p) { _ptr=&p; if (_ptr) { _ptr->_counter++; if (_descr.empty()) _descr=_ptr->_descr; } else throw CTError("CTCTPointer::_attach(&)", k_CTERROR_POINTER,0,0, "No object for "+_descr); }; void _attach(CTPointerObject *p) { _ptr=p; if (_ptr) { _ptr->_counter++; if (_descr.empty()) _descr=_ptr->_descr; } else throw CTError("CTCTPointer::_attach(*)", k_CTERROR_POINTER,0,0, "No object for "+_descr); }; void _detach() { if (_ptr) { if (_ptr->_counter>0) { _ptr->_counter--; if (_ptr->_counter<1) { if (_ptr->_delete) _deleteObject(_ptr->_object); delete _ptr; } } } _ptr=0; }; /** * This method actually deletes the object. Since the base class * does not know the type of this object, we have to make this method * virtual. The template class MUST override this. */ virtual void _deleteObject(void *p) { }; CTPointerBase(CTPointerBase &p): _ptr(0) { if (p._ptr) _attach(p._ptr); }; CTPointerBase(const CTPointerBase &p) : _ptr(0) { if (p._ptr) _attach(p._ptr); }; /** * This operator handles the case where you give another pointer as argument. * (like pointer1=pointer2). * @author Martin Preuss */ void operator=(CTPointerBase &p) { _detach(); if (_descr.empty()) _descr=p._descr; if (p._ptr) _attach(p._ptr); }; void operator=(const CTPointerBase &p) { _detach(); if (_descr.empty()) _descr=p._descr; if (p._ptr) _attach(p._ptr); }; /** * This operator handles the case where you do something like this:
* pointer=new structXYZ;
* @author Martin Preuss */ void operator=(void* obj) { CTPointerObject *p; if (_ptr) _detach(); _ptr=0; if (obj==0) return; p=new CTPointerObject(obj,_descr); _attach(p); }; /** * Constructor. */ CTPointerBase(): _ptr(0) {}; CTPointerBase(void *obj): _ptr(0) { CTPointerObject *p; p=new CTPointerObject(obj,_descr); _attach(p); }; #endif public: /** * Destructor. * If this one gets called, it automagically decrements the usage * counter of the object pointed to. If it reaches zero, then no other * pointer points to the object and the object faces deletion. * @author Martin Preuss */ virtual ~CTPointerBase() { }; /** * Set the description of this pointer. * * Useful for debugging purposes. * @author Martin Preuss */ void setDescription(string descr) { _descr=descr; }; /** * Get the description of this pointer. * * Useful for debugging purposes. * @author Martin Preuss */ const string &description() const { return _descr; }; /** * Set the description of the object this pointer points to. * * Useful for debugging purposes. * @author Martin Preuss */ void setObjectDescription(string descr) { if (!descr.empty()) if (_ptr) _ptr->setDescription(descr); }; /** * Returns the description of the object. * * Useful for debugging purposes. * @author Martin Preuss */ string objectDescription() const { if (_ptr) return _ptr->description(); else return ""; }; /** * Returns the reference counter of the object. * * Useful for debugging purposes. * @author Martin Preuss */ int referenceCount() const { if (_ptr) return _ptr->counter(); else return -1; }; /** * Equality operator for the object pointed to. * * This operator checks whether another pointer and this one are * pointing to the same data. * * @author Martin Preuss */ bool operator==(const CTPointerBase &p) const { if (_ptr && p._ptr) return _ptr->_object==p._ptr->_object; else return false; }; /** * Checks whether both pointers share their data object. * * @author Martin Preuss */ bool sharingData(const CTPointerBase &p) const { return (_ptr==p._ptr); }; /** * Inequality operator for the object pointed to. * * This operator checks whether another pointer and this one are * not pointing to the same data. * * @author Martin Preuss */ bool operator!=(const CTPointerBase &p) const { if (_ptr && p._ptr) return _ptr->_object!=p._ptr->_object; else return true; }; /** * Returns a raw pointer to the stored data. * * You should not really use this, but if you do so please NEVER * delete the object the pointer points to ! AND you should make * sure that as long as you are using the pointer returned there * is still a CTPointer pointing to it (because if the last CTPointer * stops pointing to an object that object gets deleted) !! * * @author Martin Preuss */ virtual void* voidptr() const { if (!_ptr) return 0; if (!(_ptr->_object)) return 0; return _ptr->_object; }; /** * Set the auto-deletion behaviour. * * Set the auto-deletion behaviour of the CTPointerObject (the * wrapper object around the "real" object pointed to) that is * pointed to by this CTPointer. * * By default, this is set to setAutoDeletion(true), i.e. the * object will automatically be deleted when its last * CTPointer gets deleted. On the other hand, when you call * this with b=false, then the object this pointer points to will * not be deleted by the last CTPointer. * * This might be useful if you are pointing to constant objects, * or if you need to continue using this object through raw * pointers elsewhere. * * This flag is a property of the CTPointerObject, i.e. even for * multiple CTPointer's pointing to the same object there is * only *one* autoDelete flag per object. Changes to this flag * affect all of the CTPointer's at the same time. * * This CTPointer MUST already point to an object (a NULL * pointer is not allowed at this point) since the autodelete flag * is a property of the class CTPointerObject. If called on an * invalid CTPointer, this method will throw an CTError. * * @param b True to set automatic deletion to be enabled, * false to disable it. * @author Martin Preuss */ void setAutoDelete(bool b) { if (_ptr) { if (_ptr->_object) _ptr->_delete=b; } else throw CTError("CTCTPointer::setAutoDelete()", k_CTERROR_POINTER,0,0, "No object for "+description()); }; /** * Returns true if this CTPointer is valid. * * This tells you if this pointer is pointing to accessible data. * @author Martin Preuss * @return true if data is accessible, false if no data */ bool isValid() const { if (_ptr) if (_ptr->_object) return true; return false; }; }; /** * @short A smart pointer template class. * * This class serves as a smart pointer class that is used in OpenHBCI * to avoid memory leaks. It does automatic reference counting for the * objects pointed to, like so: Each time a new CTPointer to the same * object is created, the reference counter is incremented. Each time * a CTPointer to an object is deleted, the reference counter is * decremented. When the reference counter reaches zero, the object is * deleted. * * Use it instead of normal pointers, for example: * instead of * * structXYZ *pointer;
* pointer = new structXYZ;
* * use this one: * * CTPointer pointer;
* pointer = new structXYZ;
* * You can access the data easily by using the "*" operator, e.g: * * structXYZ xyz = *pointer; * * To access members of the object, either use the "*" operator or the * ref() method: * * a = (*pointer).a; or
* b = pointer.ref().a; * * @author Martin Preuss * @ingroup misc */ template class CHIPCARD_API CTPointer: public CTPointerBase { friend class CTPointerCastBase; private: protected: /** * This method actually deletes the object. Since the base class * does not know the type of this object, we have to make this method * virtual. The template class MUST override this. */ virtual void _deleteObject(void *p) { delete (T*) p; }; CTPointer(const CTPointerBase &p): CTPointerBase(p) { }; public: /** * Empty Constructor. */ CTPointer(): CTPointerBase(){}; /** * Constructor with object to be pointing to. */ CTPointer(T *obj): CTPointerBase(obj) { }; /** Copy constructor */ CTPointer(const CTPointer &p) : CTPointerBase(p) { }; /** * Destructor. * * If this one gets called, it automagically decrements the usage * counter of the object pointed to. If it reaches zero, then no other * pointer points to the object and the object will be deleted. * @author Martin Preuss */ virtual ~CTPointer() { _detach(); }; /** @name Copy Operators */ //@{ /** * Copy operator with object pointed to. * * This operator handles the case where you do something like this:
* pointer=new structXYZ;
* @author Martin Preuss */ void operator=(T* obj) { CTPointerBase::operator=(obj); }; /** * Copy operator with another CTPointer. * * This operator handles the case where you give another pointer * as argument. (like pointer1=pointer2). * * @author Martin Preuss */ void operator=(CTPointer &p) { CTPointerBase::operator=(p); }; /** * Copy operator with another const CTPointer. * * This operator handles the case where you give another pointer * as argument. (like pointer1=pointer2). * * @author Martin Preuss */ void operator=(const CTPointer &p) { CTPointerBase::operator=(p); }; //@} /** @name Object Access */ //@{ /** * Returns a reference to the object pointed to. * * If the CTPointer is invalid, this throws a CTError. */ T& ref() const { T* p; p=ptr(); if (!p) throw CTError("CTCTPointer::ref()", k_CTERROR_POINTER,0,0, "No object for "+description()); return *p; }; /** * Returns a reference to the object pointed to. * * If the CTPointer is invalid, this throws a CTError. */ T& operator*() const { return ref(); }; /** Returns a raw pointer to the stored data. * * If you can continue using only CTPointer's, you should not * really need to use this. This method is necessary if and only * if you need to use a "raw C pointer" of the object pointed to. * * So if you need to use this method while there is still a * CTPointer pointing to it, please never delete the object * returned. The last remaining CTPointer's will take care of * deletion. * * On the other hand, if you need to use this pointer longer than * the last CTPointer would exist, then either try to keep a CTPointer * around long enough, or you need to consider setting * CTPointerBase::setAutoDelete appropriately. (Because if the last * CTPointer stops pointing to an object, then that object will get * deleted unless CTPointerBase::setAutoDelete was changed.) * * @author Martin Preuss */ virtual T* ptr() const { return (T*)CTPointerBase::voidptr(); }; //@} /** @name Type cast */ //@{ /** * @short Returns a type-safe casted CTPointer of the given type. * * This method returns a type-safe casted CTPointer of the given * type. This obeys the same rules as a * dynamic_cast, and in fact internally * a dynamic_cast is used. * * Use it like this: *
     * class type_X;
     * class type_Y : public type_X;
     *
     * CTPointer pX;
     * CTPointer pY = new type_Y;
     * pX = pY.cast();
     * 
* * The casting fails if it is impossibe to safely cast the * "type_Y" to "type_X". In that case, an CTError is * thrown. Also, if you call this method on an invalid * CTPointer, a CTError is thrown. * * @author Martin Preuss */ template CTPointer cast() const { return CTPointerCast::cast(*this); /* return CTPointer(*this); */ }; //@} /** @name Equality */ //@{ /** * Equality operator for the object pointed to. * * This operator checks whether another pointer and this one are * pointing to the same data. * * @author Martin Preuss */ bool operator==(const CTPointer &p) const { return CTPointerBase::operator==(p); }; /** * Inequality operator for the object pointed to. * * This operator checks whether another pointer and this one are * not pointing to the same data. * * @author Martin Preuss */ bool operator!=(const CTPointer &p) const { return CTPointerBase::operator!=(p); }; /** * Checks whether both pointers share their data object. * * This method checks whether another pointer and this one share * the same internal data object and thus also the same data * pointed to. This is a stronger condition than * operator==. This method returns true only if one * CTPointer has been copied to other CTPointer's. But as soon as some * "raw C pointers" have been assigned to different CTPointer, this * method would return false. In that latter case, the * operator== would still return true, so that is why * the operator== is more likely to be useful. * * @author Martin Preuss */ bool sharingData(const CTPointer &p) const { return CTPointerBase::sharingData(p); }; //@} }; #ifndef DOXYGEN /** * */ template class CHIPCARD_API CTPointerCastBase { protected: CTPointerCastBase(); ~CTPointerCastBase(); static CTPointer makeCTPointer(const CTPointerBase &p) { return CTPointer(p); }; }; /** * This class lets you safely cast one CTPointer to another one. * It will automatically perform type checking. If this class is unable to * cast then it throws an CTError. * You can use this if you have a CTPointer for an object but * you need a pointer for a base object. For example: *
 * class BaseClass {
 *   BaseClass();
 *   ~BaseClass();
 * };
 *
 * class InheritingClass: public BaseClass {
 *   InheritingClass();
 *   ~InheritingClass();
 * };
 *
 * CTPointer pInheriting;
 * CTPointer pBase;
 *
 * pInheriting=new InheritingClass();
 * pBase==CTPointerCast::cast(pInheriting);
 * 
* @author Martin Preuss */ template class CHIPCARD_API CTPointerCast :public CTPointerCastBase { public: /** * If the first template parameter is the base class to the second * template parameter then this method will cast a pointer to an * inheriting class into a pointer to the base class (downcast). * If you want just the opposite then you only need to exchange the order * of the template parameters (upcast). * @author Martin Preuss */ static CHIPCARD_API CTPointer cast(const CTPointer &u) { U *uo; T *t; /* check if given pointer is valid */ if (!u.isValid()) throw CTError("CTPointerCast::cast()", k_CTERROR_POINTER,0,0, "No object for "+u.description()); /* then try to cast the pointer */ uo=u.ptr(); t=dynamic_cast(uo); /* could we cast it ? */ if (t==0) /* no, throw */ throw CTError("CTCTPointerCast::cast()", k_CTERROR_POINTER,0,0, "Bad cast "+u.description()); /* otherwise create a new pointer */ return makeCTPointer(u); }; }; #endif /* DOXYGEN */ #endif /* CTPOINTER_H */