Click here to Skip to main content
15,885,546 members
Articles / Programming Languages / C++

DynObj - C++ Cross Platform Plugin Objects

Rate me:
Please Sign up or sign in to vote.
4.95/5 (34 votes)
27 Sep 200727 min read 142.1K   3.4K   132  
DynObj is an open source library that provides a C++ application with run-time class loading facilities
#ifndef NOTIFIABLE_HPP
#define NOTIFIABLE_HPP

// define NOTIF_SILENT to suppress warnings below

#include "NotifiableI.hpp"
#include "TemplUtils.hpp"


// For compatibility with DynObj.h
#ifndef docall
	#ifndef NOTIF_SILENT
		#warning docall not defined, defining to nothing
	#endif
	#define docall
#endif

// If threaded implementation, NOTIF_LOCK locks a global recursive mutex
// so that NotifierI and Notifiable can decouple in an ordered way.
#ifndef  NOTIF_LOCK
    #include "pi/CritSect.h"
    // Use a shared global in Notifiable.cpp
    extern CritSect g_not_cs;
	#define NOTIF_LOCK   g_not_cs.Enter()
	#define NOTIF_UNLOCK g_not_cs.Leave()
#endif 

#ifndef NOTIF_TRACE
	#ifndef NOTIF_SILENT
		#warning NOTIF_TRACE not defined, defining to nothing
	#endif 
	#define NOTIF_TRACE(str)
#endif 


// Reader-write lock for WeakRef
#include "pi/RWLock.h"


class NotifLocker {
public:
	NotifLocker(){ NOTIF_LOCK; }
	~NotifLocker(){ NOTIF_UNLOCK; }
};

// Helper struct, represents one subscriber to a specific notification.
struct NotTarget {
    NotTarget(NotifiableI* pni, int evt) : m_pni(pni), m_evt(evt) { }
    NotTarget(const NotTarget& nt) : m_pni(nt.m_pni), m_evt(nt.m_evt) { }
    NotifiableI *m_pni;
    int m_evt;
    bool operator == (const NotTarget& nt) { return nt.m_pni==m_pni && nt.m_evt==m_evt; }
};

/** @ingroup VObj 
 * Base class to include support for notfications.
 * A class that derives from Notifier send off destructor notifications
 * so we can have weak references to them. For thread safety, it uses a lock 
 * that is shared between all instances of Notifier.
 */
class Notifier : public NotifierI {
public:
	Notifier() : m_fst(NULL) { }
	~Notifier() {
		Notify(NOT_EVT_DESTRUCTOR,NULL);
		NotifLocker lock;
		delete m_fst; 
		m_fst=NULL; 
	}
    /** Adds a notifcation target to this object */
	virtual bool docall AddNotifiable(NotifiableI* pnot, int evt){
        if( !pnot ) return false;
		NOTIF_TRACE("Adding notifiable");
		NotifLocker lock; 
        // Only add once
        if( TLLFind(NotTarget(pnot,evt),m_fst) ) return false;
		m_fst=new TLLNode<NotTarget>(NotTarget(pnot,evt),m_fst); 
        return true;
	}
    /** Remove a notifcation target to this object */
	virtual bool docall RemoveNotifiable(NotifiableI* pnot, int evt){ 
		NOTIF_TRACE("Removing notifiable");
		NotifLocker lock; 
		return TLLErase<NotTarget>(NotTarget(pnot,evt),&m_fst); 
	}
    /** Notifies all notifcation targets registered with this object */
	virtual int docall Notify( int evt, NotDataI *pnd ){
		NotifLocker lock; 
		// We send pointer to ourseleves as first arg
        int cnt=0;
		for( TLLNode<NotTarget> *pnode=m_fst; pnode; pnode=pnode->m_nxt )
            if( pnode->m_t.m_evt==evt || pnode->m_t.m_evt==NOT_EVT_ALL )
                cnt++, pnode->m_t.m_pni->OnNotify( this, evt, pnd );
        return cnt;
	}
	
protected:
	TLLNode<NotTarget> *m_fst;
};

// The point with the below class is that it can convert back to the main class pointer
// However, then we loose the type (NotifierI). The receiver can resolve type
/*
template<class T>
class Notifier : public NotifierBaseImpl {
public:
	~Notifier(){ Notify(NOT_DESTRUCTOR,NULL); } 
	virtual void docall Notify( int msg, NotDataI *pnd ){
		NotifLocker lock; 
 		// NOTE: Conversion to T* will cast back to whole object (if multiple bases)
 		// T and Notifier will be in the same object and on the same SDO side
		T* pt = static_cast<T*>(this);
		for( TLLNode<NotifiableI*> *pnode=m_fst; pnode; pnode=pnode->m_nxt )
			pnode->m_t->OnNotify( pt, msg, pnd );
	}
};
*/


/** @ingroup VObj 
 * Weak reference to another object of type T.
 * The other object must support NotifierI, otherwise we cannot 
 * make a reference. To generate a NotifierI from T, there is 
 * GenericCaster which by default uses static_cast<NotfierI*>(T*).
 * DynObj will specialize this to provide the interface in a 
 * different way (using do_cast).
 * A WeakRef can be used in a thread safe way, using HoldGet() and 
 * HoldPut(). It has a reader-writer lock object per instance, so
 * the reference cannot be overwritten nor destroyed while we do 
 * HoldGet(). */
template<class T>	
class WeakRef : public NotifiableI {
public:
	WeakRef(T* pobj=NULL) : m_pobj(NULL), m_pni(NULL) { Reset(pobj); }
	~WeakRef(){ Reset(NULL); NOTIF_TRACE("WeakRef Dtor"); }

    /** Set the reference to another object */
	WeakRef& Reset( T* pobj ){ 
        piWriteLocker<RWLock> locker( m_lock );
        if( m_pobj==pobj ) return *this;
        
        assert( locker.Locked() );
        if( m_pni || m_pobj ){
            assert( m_pni && m_pobj );
            m_pni->RemoveNotifiable(this,NOT_EVT_DESTRUCTOR);
            m_pni = NULL; m_pobj = NULL;
        }
        if( !pobj ) return *this;
        
        // This will use a static_cast if available and if not, it can be 
        // specialized (with value 1)
		m_pni = GenericCaster<NotifierI*,T*,
                            1-IsConvertible<NotifierI*,T*>::v>::Cast(pobj);
        if( !m_pni ){
			NOTIF_TRACE("WeakRef::Reset - Could not cast to object to NotifierI. Resetting object.");
            m_pobj = NULL;
            return *this;
        }
        m_pobj = pobj;
		m_pni->AddNotifiable(this,NOT_EVT_DESTRUCTOR);
        return *this;
	}
	
    /** Operator support */
	WeakRef&  operator = (T *pobj){ return Reset(pobj); }
    bool operator ==(T *pobj){ return m_pobj==pobj; }

	/** These are non-synced methods. Only use from the same thread as where 
     * the reference owner. */
	operator T*(){ return m_pobj; }
    /** Non-synced */
	T* operator ->(){ return m_pobj; }
    /** Non-synced */
	T* Get(){ return m_pobj; }
    /** Return true if the reference is empty (we have NULL pointer) */
    bool operator!(){ return !m_pobj; }
    
    /** Synchronized access. 
     * This increases read counter (and must be compensated with HoldPut) */
    T* HoldGet(){ 
        if( !m_pobj ) return NULL;
        if( !m_lock.Lock() ) 
            return NULL;
        return m_pobj;
    }
	
    /** Synchronized end-of-access. 
     * This decreases read counter. On zero, the reference can be modified. */
    void HoldPut( T *pt ){ 
        if( !pt ) return;
        assert( pt==m_pobj );
        bool b = m_lock.Unlock();
        assert( b );
    }
    
protected:
	virtual bool docall OnNotify( NotifierI *psrc, int evt, NotDataI *pnd ){
		if( evt==NOT_EVT_DESTRUCTOR ){
            piWriteLocker<RWLock> locker(m_lock);
            if( psrc!=m_pni ){
				NOTIF_TRACE("WeakRef::OnNotify: Unknown source, keeping object"); 
                return false;
            }
            m_pobj = NULL;
            m_pni = NULL;
            return true;
		}
		else{
			NOTIF_TRACE("WeakRef::OnNotify: Unknown event, doing nothing"); 
			return false;
		}
	}
	
	T* m_pobj;          // 16 bytes all in all
    NotifierI *m_pni;
    RWLock m_lock;
};



/*
// The difference compared to WeakRef is that here we don't keep a
// pointer to the compound object, instead we have a pointer to 
// a sub object. We cannot do the 'inside' test, but have to 
// explicitely store a pointer to the NotifierI. 
template<class T>	
class WeakRefSubObject : public WeakRef<T> {
public:
	WeakRefSubObject(T* pobj=NULL) : WeakRef<T>(pobj), m_pni(NULL) { }
	~WeakRefSubObject(){  }
	
	void Reset(T* pobj ){
		NotifLocker lock;	// # Recursive lock!
		WeakRef<T>::Reset( pobj );
		if( this->m_pobj )
			m_pni = Cast<NotifierI*>(this->m_pobj,NULL);
	}
	
	void operator = (T *pobj){ Reset(pobj); }
	
	virtual bool docall OnNotify( NotifierI *psrc, int evt, NotDataI *pnd ){
		NotifLocker lock;
		if( evt==NOT_DESTRUCTOR ){
			if( psrc==m_pni ){
				this->m_pobj = NULL; 
				m_pni=NULL;
				NOTIF_TRACE("WeakRefSubObject::OnNotify: Releasing"); 
				return true;
			}
			else{
				NOTIF_TRACE("WeakRefSubObject::OnNotify: Unknown source, releasing reference anyway"); 
				this->m_pobj = NULL; 
				m_pni=NULL;
				return false;
			}
		}
		else{
			NOTIF_TRACE("WeakRefSubObject::OnNotify: Unknown event, doing nothing"); 
			return false;
		}
	}
	
protected:
	NotifierI *m_pni;
};
*/

#endif // NOTIFIABLE_HPP

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions