All pastes #936893 Raw Edit

sdfsdfsdfsdfsd

public cpp v1 · immutable
·published 2008-03-10 18:40 UTC
rendered paste body
/*! @author jhc_ */#ifndef _POINTER_H#define _POINTER_H#include <QPair>#include "index.h"#include "mappable.h"#include "abstractpointer.h"/*! *  @brief On demand cached mapped object, acts as normal pointers * * Pointer is a key class to efficiency in our ORM implementation : * it has two main purposes : to provides a cache mechanism for mapped  * objects, to ensure unicity in memory of mapped objects and to  * instanciate mapped objects on demand. * * Why do we have to bother with such issues ?  * * To start, Caching is just a matter of performance, every modern  * software includes a such feature and not having it would severly hit  * reactivity in a gui using it, even if it runs asynchronously in a  * separated thread ( it would be smoother but still slow ). Concretely,  * caching mapped objects means that if we want to fetch an object that we * already fetched, we don't have to walk through the database anymore.  * But this introduces the requirement for the cache to know when database  * has been modified by someone else on the network.  * We use a watchdog for solving this ( @see DbConnection for more details. ). * * Next, Unicity. Why do we bother ourselves with unicity ?  * To avoid algorithmic crazyness when trying to resolve conflicts  * when the same objects instanciated twice has been modified differently  * on each instance. Let's take an example : we have User u, which is  * an attribute of Group g1 and g2. This means, without mapped object  * unicity, that u is instanciated twice, with same data fetched from * the database. Le'ts imagine, that someone modify u name is g1 and someone  * else modify u's phone and u's name in g2. How should we behave ?  * That is what was meant when using the term "algorithmic crazyness".  * * To solve this tedious problem, we reuse the caching system : instead * of just instanciating another object with data brought from the cache, * we simply return a reference to the current instance of this object. * * This make the cache acts at two levels : database data, mapped objects  * instances. * * Finally, what is "on demand instanciation" ? It's the answer to a  * big performance problem. Normally, if we basically instanciate every  * mapped attributes of each mapped class, we will end by transitive relations * in instanciating all objects possible using associations between objects.  * * If User u is referenced by Group g1, we have to instanciate g1. But g1 has  * many users in its references, so we have to instanciates them too. And it * goes like this for a while.  * * By instancianting objects only when they are explicitely needed  * we completely avoid the previous problem. Concretely, it means that at least * a mapped object, even if not instanciated has a database index to know  * where to fetch informations. If  you try to use an object through a Pointer, * if it isn't loaded and not cached, it will be loaded implicitely, added to * the cache for further usage. This mechanism is activated when accessing the  * pointer using any operator that access to object itself :  * * Pointer<User> u(Index(4,"User");  * Using indirection operators ( *u  and -> ) will trigger this mechanism.  * * @Warning Pointer is intended to manage heap allocated objects, and will act *          as a basic garbage collector. */ template <class T>class Pointer : public AbstractPointer {  private:    T * m_ptr;    Index m_index;  public:    DECLARE_EXCEPTION(NotMappable);    inline Pointer()       : m_ptr(NULL)    {    }        /*! Constructs a Pointer by using an already instanciated object */    inline Pointer(T * ptr)    {      m_ptr = ptr;            if(!m_ptr)        return;      m_index = m_ptr->_index();      if( AbstractPointer::sm_cache.contains(m_index) )      {        if( m_ptr != AbstractPointer::sm_cache.value(m_index))        {          qWarning() << "Pointer<T> : referenced objects aren't the same";          AbstractPointer::sm_cache[m_index] = m_ptr;        }      }      else      {        AbstractPointer::sm_cache.insert(m_index, m_ptr);      }    }         inline Pointer(Index index)      : m_ptr(NULL), m_index(index)    {      if(AbstractPointer::sm_cache.value(index) == NULL)      {        m_ptr = static_cast<T*>(sm_cache[index]);        AbstractPointer::sm_counter[index]++;      }      else         AbstractPointer::sm_counter.insert(index,1);      qDebug() << AbstractPointer::sm_counter.value(index);    }    inline Pointer(const Pointer<T> & pointer)    {      operator=(pointer);    }    inline T * ptr() const     {      if( ! m_index.isNull() )        return static_cast<T*>(AbstractPointer::sm_cache.value(m_index));       return m_ptr;    }    Index index() const    {      return m_index;    }    inline ~Pointer()     {      decreaseReference();    }        inline bool isNull()     {       return !m_ptr;     }         inline T * operator->()    {      return access();     }    inline T & operator*()    {      return *access();    }    inline operator T*()    {      return access();    }    inline const Pointer<T> & operator=(const Pointer<T> & pointer)    {      if(pointer.ptr() == NULL)      {        decreaseReference();        m_ptr = NULL;        m_index = pointer.index();        return *this;      }      if(m_ptr == pointer.ptr()          || m_index == pointer.ptr()->_index())          return *this;      decreaseReference();          /* Since it's already a Pointer, we do not have to check for existance */      m_ptr = pointer.ptr();      m_index = pointer.index();      sm_counter[m_ptr->_index()]++;      return *this;    }    inline const Pointer<T> & operator=(T * ptr)    {      decreaseReference();      if( ptr == NULL )      {        m_ptr = ptr;        m_index = Index();      }      else      {        m_ptr = ptr;        m_index = ptr->_index;      }      return *this;    }    void decreaseReference()     {      if(!m_ptr || m_index.isNull())        return;      int remaining = --AbstractPointer::sm_counter[m_index];        qDebug() << remaining;      if(remaining <= 0)      {        //delete m_ptr;        //AbstractPointer::sm_cache.remove(m_index);        qDebug() << "Pointer<T> : no more reference to" << m_index.toString();      }      else        qDebug() << "Pointer<T> : remaining" << sm_counter[m_index] << "references to" << m_index.toString();    }    T * access()    {      /* if we don't make reference to an object, do nothing */      if(m_index.isNull())      {        qDebug() << "Pointer<T> : refers to nothing, can't access";        return NULL;      }       /* if m_ptr is NULL, check for pointer state in hash*/      if(m_ptr == NULL)      {        /* if corresponding value in hash is still empty, fetch it from db         * this is a "cache miss" */        if( AbstractPointer::sm_cache.value(m_index) == NULL )        {          m_ptr = new T();          m_ptr->setId(m_index.id());          sm_hitCounter[m_ptr->_index()] = 0;          qDebug() << "Pointer<T> : cache miss, fetching" << m_ptr->_index().toString() << "from database";                     m_ptr->_fetch();          AbstractPointer::sm_cache[m_index] = m_ptr;        }        else          /* "cache hit" */          m_ptr = static_cast<T*>(AbstractPointer::sm_cache.value(m_index));      }            sm_hitCounter[m_ptr->_index()]++;      return m_ptr;    }        void setIndex(const Index & index)    {      decreaseReference();       m_ptr = NULL;      m_index = index;    }};#endif