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.csdn.net/i_dovelemon/article/details/30250049
*   http://blog.csdn.net/zhao_92221/article/details/46629553
*   http://blog.csdn.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(...);
}

COM原理与实现之一的更多相关文章

  1. 奇异值分解(SVD)原理与在降维中的应用

    奇异值分解(Singular Value Decomposition,以下简称SVD)是在机器学习领域广泛应用的算法,它不光可以用于降维算法中的特征分解,还可以用于推荐系统,以及自然语言处理等领域.是 ...

  2. node.js学习(三)简单的node程序&&模块简单使用&&commonJS规范&&深入理解模块原理

    一.一个简单的node程序 1.新建一个txt文件 2.修改后缀 修改之后会弹出这个,点击"是" 3.运行test.js 源文件 使用node.js运行之后的. 如果该路径下没有该 ...

  3. 线性判别分析LDA原理总结

    在主成分分析(PCA)原理总结中,我们对降维算法PCA做了总结.这里我们就对另外一种经典的降维方法线性判别分析(Linear Discriminant Analysis, 以下简称LDA)做一个总结. ...

  4. [原] KVM 虚拟化原理探究(1)— overview

    KVM 虚拟化原理探究- overview 标签(空格分隔): KVM 写在前面的话 本文不介绍kvm和qemu的基本安装操作,希望读者具有一定的KVM实践经验.同时希望借此系列博客,能够对KVM底层 ...

  5. H5单页面手势滑屏切换原理

    H5单页面手势滑屏切换是采用HTML5 触摸事件(Touch) 和 CSS3动画(Transform,Transition)来实现的,效果图如下所示,本文简单说一下其实现原理和主要思路. 1.实现原理 ...

  6. .NET Core中间件的注册和管道的构建(1)---- 注册和构建原理

    .NET Core中间件的注册和管道的构建(1)---- 注册和构建原理 0x00 问题的产生 管道是.NET Core中非常关键的一个概念,很多重要的组件都以中间件的形式存在,包括权限管理.会话管理 ...

  7. python自动化测试(2)-自动化基本技术原理

    python自动化测试(2) 自动化基本技术原理 1   概述 在之前的文章里面提到过:做自动化的首要本领就是要会 透过现象看本质 ,落实到实际的IT工作中就是 透过界面看数据. 掌握上面的这样的本领 ...

  8. CRC、反码求和校验 原理分析

    3月份开始从客户端转后台,算是幸运的进入全栈工程师的修炼阶段.这段时间一边是老项目的客户端加服务器两边的维护和交接,一边是新项目加加加班赶工,期间最长经历了连续工作三天只睡了四五个小时的煎熬,人生也算 ...

  9. 菜鸟学Struts2——Struts工作原理

    在完成Struts2的HelloWorld后,对Struts2的工作原理进行学习.Struts2框架可以按照模块来划分为Servlet Filters,Struts核心模块,拦截器和用户实现部分,其中 ...

  10. Objective-C中block的底层原理

    先出2个考题: 1. 上面打印的是几,captureNum2 出去作用域后是否被销毁?为什么? 同样类型的题目: 问:打印的数字为多少? 有人会回答:mutArray是captureObject方法的 ...

随机推荐

  1. no zuo no die

    #include <iostream> #include <cstring> #include <cstdio> using namespace std; name ...

  2. ●洛谷P2495 [SDOI2011]消耗战

    题链: https://www.luogu.org/problemnew/show/P2495题解: 虚树入门,树形dp 推荐博客:http://blog.csdn.net/lych_cys/arti ...

  3. [Codeforces]852A - Digits

    题目大意:给一个10^200000以内的数字,支持一种操作:在数字之间加若干个加号,把原数字变为加法运算后的结果,要求在三次操作内把数字变成个位数,输出方案. 做法:直观的想法是每两位之间都塞加号,事 ...

  4. IO复用

    IO复用:使得程序能同时监听多个文件描述符 select: select在一段指定的时间内,监听用户感兴趣的文件描述符的 读.写.异常事件. select(int nfds,fd_set* readf ...

  5. java9学习之模块化

    截止到目前JDK的版本已经更新到10了,虽然java9的生命周期才半年,但是我认为这个版本带来的变革是不可磨灭的,它是第一次深层次的针对架构以及依赖上的革新.下面我们就来学习一下. 一.模块化项目构建 ...

  6. matplotlib中subplot的各参数的作用

    subplot(a,b,c)中a代表所画图形的行数 b代表所画图形的列数 c代表所画图形的序号. plt.figure(facecolor='w', figsize=(9, 10)) plt.subp ...

  7. PTA 旅游规划(25 分)

    7-10 旅游规划(25 分) 有了一张自驾旅游路线图,你会知道城市间的高速公路长度.以及该公路要收取的过路费.现在需要你写一个程序,帮助前来咨询的游客找一条出发地和目的地之间的最短路径.如果有若干条 ...

  8. JVM程序计数器

    一.先来看看概念 多线程的Java应用程序:为了让每个线程正常工作就提出了程序计数器(Programe Counter Register),每个线程都有自己的程序计数器这样当线程执行切换的时候就可以在 ...

  9. idea和androidstudio的首次git配置一些问题

    网上都有很清楚的步骤 但是 都是教怎么使用 但是对第一次应用idea内部vcs的git 则很少有详细说明 首先要在网上创建个项目 然后本地git clone下来 不建议内部vcs的fetch from ...

  10. Servlet-----response.getWriter().write()与out.print()的区别

    50313  1.首先介绍write()和print()方法的区别:  (1).write():仅支持输出字符类型数据,字符.字符数组.字符串等  (2).print():可以将各种类型(包括Obje ...