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,\
iid_t iid,\
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 */
最后是测试代码:
//
// 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, 0, 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;
}
没有内存泄露。OK!