COM原理与实现之一

时间:2024-09-06 08:03:07

COM原理与实现之一

COM组件其实是一种特殊的对象体系,遵循一个统一的标准,使到各个软件都可以通过某种方法访问这个对象的方法,也就可以做到组件调用。COM就是统一的标准——通过接口来调用COM组件。接口是COM组件能被外界所感知的唯一的东西。

所有接口的接口都继承自IUnknown,实现了“接口查询”和“引用计数”。包含3个方法:

  interface   IUnknown
  {
          virtual   HRESULT   _stdcall   QueryInterface([in]REFIID iid, [out] void **ppv)=0;
          virtual   ULONG   _stdcall   AddRef(void)=0;
          virtual   ULONG   _stdcall   Release(void)=0;
  }

QueryInterface负责得到该组件的其他接口的指针。
AddRef/Release负责管理该组件的生存期——引用计数+1/-1。

我实现的跨平台COM,也定义了类似IUnknown的接口,我称之为:IUniversal,定义如下:

/**
* IUniversal.h
*    IUniversal is a variant from IUnknown
*
* Refer:
*   <<Inside COM>>
*
* Init Created: 2016-06-10
* Last Updated: 2016-06-10
*/
#ifndef IUNIVERSAL_H
#define IUNIVERSAL_H

#include "Platform.h"

#define iid_IUniversal  ((iid_t)(-1))

interface IUniversal
{
    static const iid_t IID = ((iid_t) (-1));

    virtual lresult_t query(iid_t iid, void **ppvOut) = 0;
    virtual unsigned long retain(void) = 0;
    virtual unsigned long release(void) = 0;
};

class UniversalImpl
{
private:

    refcount_t  _refc;

public:
    UniversalImpl() : _refc(1) {
    }

    virtual ~UniversalImpl() {
    }

    //
    // IUniversal
    //
    virtual lresult_t query(iid_t iid, void **ppv) = 0;

    virtual unsigned long retain(void) {
        return __interlock_inc(&_refc);
    }

    virtual unsigned long release(void) {
        if (__interlock_dec(&_refc) == 0) {
            delete this;
            return 0;
        }
        return _refc;
    }
};

#endif /* IUNIVERSAL_H */

其中Platform.h 定义如下:

/**
* Platform.h
*
*
* Init Created: 2016-06-10
* Last Updated: 2016-06-10
*/
#ifndef PLATFORM_H
#define PLATFORM_H

#if defined _MSC_VER || WIN32
    #ifndef OS_PLATFORM_WIN
        #define OS_PLATFORM_WIN
    #endif
#endif

#ifdef OS_PLATFORM_WIN
    #include <windows.h>
    #include <process.h>
#else
    #include <pthread.h>
    #include <unistd.h>
#endif

#ifndef interface
#define interface struct
#endif

/**
* interface iid
*/
typedef unsigned int iid_t;

/**
* long result
*/
typedef long lresult_t;

#define lres_success             0
#define lres_error             (-1)
#define lres_e_outmemory       (-4)
#define lres_e_nointerface     (-11)

/**
 * ref count type
 */
#ifdef OS_PLATFORM_WIN
    typedef volatile unsigned long refcount_t;
    #define __interlock_inc(add)  InterlockedIncrement(add)
    #define __interlock_dec(sub)  InterlockedDecrement(sub)
#else
    typedef volatile size_t refcount_t;
    #define __interlock_inc(add)  __sync_add_and_fetch(add, 1)
    #define __interlock_dec(sub)  __sync_sub_and_fetch(sub, 1)
#endif

#endif /* PLATFORM_H */

任何一个C++对象想要成为COM,必须从IUniversal继承,并且由于 UniversalImpl 实现了一个默认的 IUniversal,所以可以直接从UniversalImpl继承过来。

例如我们定义下面一个组件Entity,其接口定义如下:

interface EntityInterface : IUniversal
{
    static const iid_t IID = ((iid_t) 0x00F001);

    virtual void addComponent(AbstractComponent * component) = 0;

    virtual void removeComponent(AbstractComponent * component) = 0;

    virtual void removeAllComponents() = 0;
};

组件 Entity 的实现如下:

/**
* Entity.h
*
* Refer:
*   http://www.richardlord.net/blog/what-is-an-entity-framework
*   http://blog.****.net/i_dovelemon/article/details/30250049
*   http://blog.****.net/zhao_92221/article/details/46629553
*   http://blog.****.net/ubuntu64fan/article/details/8839778
*
* Init Created: 2016-05-30
* Last Updated: 2016-06-08
*/
#ifndef ENTITY_H
#define ENTITY_H

#include "../EntityInterface.h"

#include "../ComponentManager.h"

#include <stdio.h>
#include <assert.h>

#include "../../../common/hashmap.h"
#include "../../../common/dhlist.h"

#include <memory>
#include <vector>
using namespace std;

namespace ecs {

class EntityManager;

class Entity :
    public UniversalImpl,
    public EntityInterface
{
    friend class EntityManager;

    static const int COMPS_HASHSIZE = 0x10;

    Entity() {
        printf("Entity\n");

        INIT_LIST_HEAD(&list1_comps);

        for (int hash = 0; hash <= COMPS_HASHSIZE; hash++) {
            INIT_HLIST_HEAD(&hlist_comps[hash]);
        }
    }

public:

    virtual ~Entity() {
        printf("~Entity\n");

        removeAllComponents();
    }

public:

    // Create function
    //
    static lresult_t createInstance(EntityInterface **ppv) {
        EntityInterface * p = (EntityInterface *) new Entity();
        if (p) {
            *ppv = p;
            return lres_success;
        } else {
            *ppv = 0;
            return lres_e_outmemory;
        }
    }

    // IUniversal
    //
    virtual lresult_t query(iid_t iid, void **ppv) {
        if (iid == IUniversal::IID || iid == EntityInterface::IID) {
            *ppv = static_cast<EntityInterface *> (this);
        } else {
            *ppv = 0;
            return lres_e_nointerface;
        }
        reinterpret_cast<IUniversal*>(*ppv)->retain();
        return lres_success;
    }

    virtual unsigned long retain(void) {
        return UniversalImpl::retain();
    }

    virtual unsigned long release(void) {
        return UniversalImpl::release();
    }

    // EntityInterface
    //
    virtual void addComponent(AbstractComponent * component) {
        COMPONENT_TYPE compType = component->getComponentType();

        list_add(&component->i_list, &list1_comps);
    }

    virtual void removeComponent(AbstractComponent * component) {

    }

    virtual void removeAllComponents() {
        struct list_head *list, *node;

        list_for_each_safe(list, node, &list1_comps) {
            class AbstractComponent * p = list_entry(list, class AbstractComponent, i_list);
            list_del(list);
            delete (p);
        }
    }

private:

    struct list_head  i_list;
    struct hlist_node i_hash;

    /*
    * components list and hlist
    */
    struct list_head list1_comps;
    struct hlist_head hlist_comps[COMPS_HASHSIZE + 1];
};

}; /* namespace ecs */

#endif /* ENTITY_H */

这样一个COM系统就初具雏形。使用的时候我们还需要一个智能指针对象,辅助我们使用组件对象。我定义这个智能接口指针如下:

/**
* SIPtr.h
*    Smart Interface Pointer
*
* Use: SIPtr<IX, iidIX> spIX;
*     Do not use with IUniversal; SIPtr<IUniversal, iidIUniversal>
*       will not compile.  Instead, use IUniversalSPtr.
*
* Refer:
*   <<Inside COM>>
*
* Init Created: 2016-06-10
* Last Updated: 2016-06-10
*/
#ifndef SIPTR_H
#define SIPTR_H

#include "IUniversal.h"

#include <assert.h>

template <class T> class SIPtr
{
public:

    // Constructors
    SIPtr() {
        m_pI = 0;
    }

    SIPtr(T* lp) {
        m_pI = lp;
        if ( m_pI ) {
            m_pI->retain();
        }
    }

    SIPtr(IUniversal* pI) {
        m_pI = 0;
        if ( pI ) {
            pI->query(T::IID, (void **) & m_pI);
        }
    }

    // Destructor
    ~SIPtr() {
        release();
    }

    // Reset
    void release() {
        if ( m_pI ) {
            T* pOld = m_pI;
            m_pI = 0;
            pOld->release();
        }
    }

    // Attach to an existing interface (does not retain)
    void attach(T * pI) {
        if (m_pI != pI) {
            IUniversal* pOld = m_pI;

            m_pI = pI;

            if (pOld) {
                // Release the old interface
                pOld->release();
            }
        }
    }

    // Detach the interface (does not release)
    T* detach() {
        T* pOld = m_pI;
        m_pI = 0;
        return pOld;
    }

    // Conversion
    operator T*() {
        return m_pI;
    }

    // Pointer operations
    T& operator*() {
        assert(m_pI);
        return * m_pI;
    }

    T** operator&() {
        assert(!m_pI);
        return &m_pI;
    }

    T* operator->() {
        assert(m_pI);
        return m_pI;
    }

    // Assignment from the same interface
    T* operator=(T* pI) {
        if (m_pI != pI) {
            // Save current value
            IUniversal* pOld = (IUniversal *) m_pI;

            // Assign new value
            m_pI = pI;

            if (m_pI) {
                m_pI->retain();
            }

            if (pOld) {
                // Release the old interface
                pOld->release();
            }
        }
        return m_pI;
    }

    // Assignment from another interface
    T* operator=(IUniversal* pI) {
        // Save current value
        IUniversal* pOld = m_pI;
        m_pI = 0;

        // Query for correct interface
        if ( pI ) {
            lresult_t hr = pI->query(T::iid_interface, (void**) & m_pI);
            assert(hr == lres_success && m_pI);
        }

        if ( pOld ) {
            // Release old pointer
            pOld->release();
        }
        return m_pI ;
    } 

    // bool functions
    bool operator!() {
        return m_pI ? false : true;
    }

    // Requires a compiler that supports BOOL
    operator bool() const {
        return m_pI ? true : false;
    }

    // Interface ID
    iid_t iid() {
        return T::IID;
    } 

private:
    // Pointer variable
    T* m_pI;
};

/**
* IUniversalPtr is a smart interface for IUniversal
*/
class IUniversalPtr
{
public:
    // Constructors
    IUniversalPtr() {
        m_pI = 0;
    }

    IUniversalPtr(IUniversal* lp) {
        m_pI = lp;
        if ( m_pI ) {
            m_pI->retain();
        }
    }

    // Destructor
    ~IUniversalPtr() {
        release();
    }

    // Reset
    void release() {
        if (m_pI) {
            IUniversal* pOld = m_pI;
            m_pI = 0;
            pOld->release();
        }
    }

    // Conversion
    operator IUniversal*() {
        return (IUniversal*) m_pI;
    }

    // Pointer operations
    IUniversal& operator*() {
        assert(m_pI);
        return *m_pI;
    }

    IUniversal** operator&() {
        assert(!m_pI);
        return &m_pI;
    }

    IUniversal* operator->() {
        assert(m_pI);
        return m_pI;
    }

    // Assignment
    IUniversal* operator=(IUniversal* pI) {
        if (m_pI != pI) {
            // Save current value
            IUniversal* pOld = m_pI;

            // Assign new value
            m_pI = pI;

            if ( m_pI ) {
                m_pI->retain();
            }

            if ( pOld ) {
                // Release the old interface
                pOld->release();
            }
        }
        return m_pI;
    }

    // Boolean functions
    bool operator!() {
        return m_pI ? false : true;
    }

    operator bool() const {
        return m_pI ? true : false;
    }

private:
    // Pointer variable
    IUniversal* m_pI;
}; 

#endif /* SIPTR_H */

使用 Entity 很简单:

void testEntityInterface()
{
    SIPtr<EntityInterface>spEntity;

    Entity::createInstance(&spEntity);

    spEntity->addComponent(...);
}