COM原理与实现之二: 聚合

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!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

车斗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值