COM原理与实现之二: 聚合
C++没有同聚合等价的特性。聚合实际上是继承性的一种动态形式。而C++的继承总是静态的,是实现继承。COM是接口继承,通过聚合接口,可以做成动态配置。
研究COM,主要是利用接口继承的灵活性构筑强大的系统:可配置、可插拔、可脚本化。本文不讲太多理论,详细原理参考[COM技术内幕]这本书。关于[COM技术内幕],很多内容过时了,比如注册表,类厂之类的。我更关心COM思想所蕴含的哲学。我实现了跨平台COM,支持聚合。
GameStencil这个组件聚合了SystemMngmt这个组件。最初采用IGameStencil::getSystemMngmt()这样的形式返回一个ISystemMngmt接口指针,显然这不是聚合。通过A对象的方法得到B对象,显然A仅仅是B的一种类厂。于是我下决心解决组件聚合问题。就有了本文。
core目录下面就3个文件,是我的跨平台COM的基础。
1) 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_initdata (-2)
- #define lres_e_outmemory (-4)
- #define lres_e_nointerface (-11)
- #define lres_e_noaggregation (-12)
- /**
- * thread_ctx
- */
- #define thread_ctx_single 0
- #define thread_ctx_multiple 1
- /**
- * 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 */
2) Universal.h
- /**
- * Universal.h
- *
- * Refer:
- * <<Inside COM>>
- *
- * Init Created: 2016-06-10
- * Last Updated: 2016-06-13
- */
- #ifndef UNIVERSAL_H
- #define UNIVERSAL_H
- #include "Platform.h"
- // IUniversal is a variant from IUnknown
- //
- interface IUniversal
- {
- static const iid_t IID = ((iid_t) (0));
- virtual lresult_t query(iid_t iid,void **ppvOut) = 0;
- virtual unsigned long retain(void) = 0;
- virtual unsigned long release(void) = 0;
- };
- #define iid_IUniversal (IUniversal::IID)
- // Nondelegating IUniversal interface
- // - Nondelegating version of IUniversal
- //
- interface INondelegatingUniversal
- {
- static const iid_t IID = ((iid_t) (-1));
- virtual lresult_t NondelegatingQuery(iid_t iid,void **ppvOut) = 0;
- virtual unsigned long NondelegatingRetain(void) = 0;
- virtual unsigned long NondelegatingRelease(void) = 0;
- };
- #define iid_INondelegatingUniversal (INondelegatingUniversal::IID)
- class UniversalImpl
- {
- private:
- unsigned thread_ctx;
- refcount_t ref_count;
- public:
- UniversalImpl() :
- ref_count(1),thread_ctx(thread_ctx_multiple) {
- printf("UniversalImpl\n");
- }
- virtual ~UniversalImpl() {
- printf("~UniversalImpl\n");
- }
- void init(unsigned threadctx) {
- thread_ctx = threadctx;
- }
- // Notification to derived classes that we are releasing
- void finalRelease() {
- // Increment reference count for final release
- ref_count = 1;
- }
- unsigned getThreadCtx() const {
- return thread_ctx;
- }
- // IUniversal
- //
- virtual lresult_t query(iid_t iid,void **ppv) = 0;
- virtual unsigned long retain(void) {
- if (thread_ctx == thread_ctx_multiple) {
- return __interlock_inc(&ref_count);
- } else {
- return ++ref_count;
- }
- }
- virtual unsigned long release(void) {
- if (thread_ctx == thread_ctx_multiple) {
- if (__interlock_dec(&ref_count) == 0) {
- delete this;
- return 0;
- }
- } else {
- if (--ref_count == 0) {
- delete this;
- return 0;
- }
- }
- return ref_count;
- }
- };
- // Declaration of NondelegatingUniversalImpl
- // - Base class for implementing INondelegatingUniversal
- //
- class NondelegatingUniversalImpl : public INondelegatingUniversal
- {
- public:
- virtual lresult_t NondelegatingQuery(iid_t iid,void **ppv) = 0;
- virtual unsigned long NondelegatingRetain(void) {
- if (thread_ctx == thread_ctx_multiple) {
- return __interlock_inc(&ref_count);
- } else {
- return ++ref_count;
- }
- }
- virtual unsigned long NondelegatingRelease(void) {
- if (thread_ctx == thread_ctx_multiple) {
- if (__interlock_dec(&ref_count) == 0) {
- delete this;
- return 0;
- }
- } else {
- if (--ref_count == 0) {
- delete this;
- return 0;
- }
- }
- return ref_count;
- }
- // Constructor
- NondelegatingUniversalImpl(IUniversal* pUniversalOuter) :
- ref_count(1) {
- // Set outer_universal pointer
- if (! pUniversalOuter) {
- // Not aggregating; delegate to nondelegating IUniversal
- m_pUniversalOuter = reinterpret_cast<IUniversal*>
- (static_cast<INondelegatingUniversal*>
- (this));
- } else {
- // Aggregating; delegate to outer IUniversal
- m_pUniversalOuter = pUniversalOuter;
- }
- }
- // Destructor
- virtual ~NondelegatingUniversalImpl() {
- }
- // Initialization (especially for aggregates)
- void init(unsigned threadctx) {
- thread_ctx = threadctx;
- }
- // Notification to derived classes that we are releasing
- virtual void finalRelease() {
- // Increment reference count for final release
- ref_count = 1;
- }
- protected:
- // Support for delegation
- IUniversal* getUniversalOuter() const {
- return m_pUniversalOuter;
- }
- private:
- // thread context
- unsigned thread_ctx;
- // Reference count for this object
- refcount_t ref_count;
- // Pointer to (external) outer IUniversal
- IUniversal* m_pUniversalOuter;
- };
- ///////////////////////////////////////////////////////////
- //
- // Delegating IUniversal
- // - Delegates to the nondelegating IUniversal,or to the
- // outer IUniversal if the component is aggregated.
- //
- #define DECLARE_UNIVERSAL_INTERFACE \
- virtual lresult_t query(iid_t iid,void **ppv) { \
- return getUniversalOuter()->query(iid,ppv); \
- } \
- virtual unsigned long retain(void) { \
- return getUniversalOuter()->retain(); \
- } \
- virtual unsigned long release(void) { \
- return getUniversalOuter()->release(); \
- }
- #define CREATE_INSTANCE_NO_AGGREGATION(className) \
- static lresult_t createInstance(\
- unsigned threadctx,\
- IUniversal *pUniversalOuter,\
- iid_t iid,\
- void **ppv) {\
- /* cannot be aggregated */ \
- if (pUniversalOuter) {\
- return lres_e_noaggregation; \
- } \
- className * p = new className();\
- if ( ! p) {\
- return lres_e_outmemory;\
- }\
- lresult_t hr = p->init(threadctx);\
- if (hr != lres_success) {\
- p->NondelegatingRelease();\
- return hr;\
- }\
- hr = p->NondelegatingQuery(iid,ppv);\
- p->NondelegatingRelease();\
- return hr;\
- }
- #define CREATE_INSTANCE_WITH_AGGREGATION(className) \
- static lresult_t createInstance(\
- unsigned threadctx,\
- IUniversal* pUniversalOuter,\
- void **ppv) {\
- className * p = new className(pUniversalOuter);\
- if ( ! p) {\
- return lres_e_outmemory;\
- }\
- lresult_t hr = p->init(threadctx);\
- if (hr != lres_success) {\
- p->release();\
- return hr;\
- }\
- hr = p->NondelegatingQuery(iid,ppv);\
- p->NondelegatingRelease();\
- return hr;\
- }
- #endif /* UNIVERSAL_H */
3)SIPtr.h
- /**
- * SIPtr.h
- * Smart Interface Pointer
- *
- * Use: SIPtr<IX> spIX;
- * Do not use with IUniversal; SIPtr<IUniversal>
- * will not compile. Instead,use IUniversalPtr.
- *
- * Refer:
- * <<Inside COM>>
- *
- * Init Created: 2016-06-10
- * Last Updated: 2016-06-10
- */
- #ifndef SIPTR_H
- #define SIPTR_H
- #include "Universal.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;
- }
- T* get() {
- return m_pI;
- }
- // 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 */
组件 GameStencil的代码:
- /**
- * IGameStencil.h
- *
- * Author: master@pepstack.com
- *
- * 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-06-13
- * Last Updated: 2016-06-13
- */
- #ifndef IGAME_STENCIL_H
- #define IGAME_STENCIL_H
- #include "core/SIPtr.h"
- namespace ecs {
- interface IGameStencil : IUniversal {
- static const iid_t IID = ((iid_t) 0x00F000);
- virtual void update(float dt) = 0;
- };
- }; /* namespace ecs */
- #endif /* IGAME_STENCIL_H */
- /**
- * GameStencil.h
- * The GameStencil class is the central point for creating
- * and managing your game state.
- *
- * 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-06-12
- * Last Updated: 2016-06-12
- */
- #ifndef GAME_STENCIL_H
- #define GAME_STENCIL_H
- #include "IGameStencil.h"
- #include "SystemMngmt.h"
- #include <memory>
- #include <vector>
- using namespace std;
- namespace ecs {
- class GameStencil :
- public IGameStencil,public NondelegatingUniversalImpl
- {
- public:
- // Creation
- //
- CREATE_INSTANCE_NO_AGGREGATION(GameStencil)
- private:
- // Constructor
- GameStencil() :
- NondelegatingUniversalImpl(0),updating(false),m_pUniversalInner(0) {
- printf("GameStencil\n");
- m_pISystemMngmt = 0;
- }
- // Destructor
- virtual ~GameStencil() {
- finalRelease();
- printf("~GameStencil\n");
- }
- // Initialization
- //
- virtual lresult_t init(unsigned threadctx) {
- NondelegatingUniversalImpl::init(threadctx);
- IUniversal * pUniversalOuter = this;
- lresult_t hr = SystemMngmt::createInstance(threadctx,pUniversalOuter,IUniversal::IID,(void**) &m_pUniversalInner);
- if (hr != lres_success) {
- return lres_error;
- }
- hr = m_pUniversalInner->query(ISystemMngmt::IID,(void**) &m_pISystemMngmt);
- if (hr != lres_success) {
- m_pUniversalInner->release();
- return lres_error;
- }
- pUniversalOuter->release();
- return lres_success;
- }
- virtual void finalRelease() {
- NondelegatingUniversalImpl::finalRelease();
- getUniversalOuter()->retain();
- m_pISystemMngmt->release();
- if (m_pUniversalInner) {
- m_pUniversalInner->release();
- }
- }
- // IUniversal
- //
- DECLARE_UNIVERSAL_INTERFACE
- // INondelegatingUniversal
- //
- virtual lresult_t NondelegatingQuery(iid_t iid,void** ppv) {
- if (iid == IUniversal::IID) {
- *ppv = static_cast<IGameStencil*> (this);
- } else if (iid == IGameStencil::IID) {
- *ppv = static_cast<IGameStencil*> (this);
- } else if (iid == ISystemMngmt::IID) {
- // contained component
- *ppv = m_pISystemMngmt;
- } else {
- *ppv = 0;
- return lres_e_nointerface;
- }
- reinterpret_cast<IUniversal*> (*ppv)->retain();
- return lres_success;
- }
- // IGameStencil
- //
- virtual void update(float dt) {
- updating = true;
- // TODO:
- updating = false;
- }
- private:
- bool updating;
- IUniversal * m_pUniversalInner;
- ISystemMngmt * m_pISystemMngmt;
- };
- }; /* namespace ecs */
- #endif /* GAME_STENCIL_H */
被聚合的组件 SystemMngmt的代码:
- /**
- * ISystemMngmt.h
- *
- * Author: master@pepstack.com
- *
- * 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-06-10
- * Last Updated: 2016-06-12
- */
- #ifndef ISYSTEM_MNGMT_H
- #define ISYSTEM_MNGMT_H
- #include "core/SIPtr.h"
- namespace ecs {
- interface ISystemMngmt : IUniversal
- {
- static const iid_t IID = ((iid_t) 0x10F001);
- virtual void update(float dt) = 0;
- virtual void pause() = 0;
- virtual void resume() = 0;
- };
- }; /* namespace ecs */
- #endif /* ISYSTEM_MNGMT_H */
- /**
- * SystemMngmt.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-06-13
- * Last Updated: 2016-06-13
- */
- #ifndef SYSTEM_MNGMT_H
- #define SYSTEM_MNGMT_H
- #include "ISystemMngmt.h"
- #include <memory>
- #include <vector>
- using namespace std;
- namespace ecs {
- class SystemMngmt :
- public ISystemMngmt,public NondelegatingUniversalImpl
- {
- public:
- // Creation
- //
- CREATE_INSTANCE_WITH_AGGREGATION(SystemMngmt)
- private:
- // Constructor
- SystemMngmt(IUniversal * pUniversalOuter) :
- NondelegatingUniversalImpl(pUniversalOuter) {
- paused = false;
- printf("SystemMngmt\n");
- }
- // Destructor
- virtual ~SystemMngmt() {
- finalRelease();
- printf("~SystemMngmt\n");
- }
- virtual lresult_t init(unsigned threadctx) {
- NondelegatingUniversalImpl::init(threadctx);
- return lres_success;
- }
- public:
- // IUniversal
- //
- DECLARE_UNIVERSAL_INTERFACE
- // INondelegatingUniversal
- //
- virtual lresult_t NondelegatingQuery(iid_t iid,void** ppv) {
- if (iid == IUniversal::IID) {
- *ppv = static_cast<INondelegatingUniversal*> (this);
- } else if (iid == ISystemMngmt::IID) {
- *ppv = static_cast<ISystemMngmt*> (this);
- } else {
- *ppv = 0;
- return lres_e_nointerface;
- }
- reinterpret_cast<IUniversal*> (*ppv)->retain();
- return lres_success;
- }
- // ISystemMngmt
- //
- // Update all the system
- void update(float dt) {
- }
- // Pause all the systems
- void pause() {
- paused = true;
- }
- // Resume all the systems
- void resume() {
- paused = false;
- }
- private:
- bool paused;
- };
- }; /* namespace ecs */
- #endif /* SYSTEM_MNGMT_H */
最后是测试代码:
没有内存泄露。OK!
- //
- // main.cpp
- //
- #ifdef WIN32
- // Refer:
- // http ://www.cnblogs.com/skynet/archive/2011/02/20/1959162.html
- #define _CRTDBG_MAP_ALLOC
- #include <stdlib.h>
- #include <crtdbg.h>
- #else
- #include <stdlib.h>
- #endif
- #include <assert.h>
- #include <stdio.h>
- #include <string.h>
- #include "model/GameStencil.h"
- using namespace ecs;
- void usage()
- {
- SIPtr<IGameStencil> spGame;
- GameStencil::createInstance(thread_ctx_single,spGame.iid(),(void**) &spGame);
- SIPtr<ISystemMngmt> spSysMngmt;
- spGame->query(spSysMngmt.iid(),(void**) &spSysMngmt);
- SIPtr<IGameStencil> spGame2;
- spSysMngmt->query(spGame2.iid(),(void**) &spGame2);
- assert(spGame2.get() == spGame.get());
- spSysMngmt->update(0.1f);
- }
- int main()
- {
- #ifdef _CRTDBG_MAP_ALLOC
- _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
- #endif
- printf("main.cpp start\n");
- usage();
- printf("main.cpp exit.\n");
- return 0;
- }