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(...); }