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!
COM原理与实现之二: 聚合的更多相关文章
- SSH原理与运用(二):远程操作与端口转发
SSH原理与运用(二):远程操作与端口转发 作者:阮一峰 (Image credit: Tony Narlock) 七.远程操作 SSH不仅可以用于远程主机登录,还可以直接在远程主机上执行操作. 上一 ...
- JVM工作原理和特点(一些二逼的逼神面试官会问的问题)
作为一种阅读的方式了解下jvm的工作原理 ps:(一些二逼的逼神面试官会问的问题) JVM工作原理和特点主要是指操作系统装入JVM是通过jdk中Java.exe来完毕,通过以下4步来完毕JVM环境. ...
- kafka原理和实践(二)spring-kafka简单实践
系列目录 kafka原理和实践(一)原理:10分钟入门 kafka原理和实践(二)spring-kafka简单实践 kafka原理和实践(三)spring-kafka生产者源码 kafka原理和实践( ...
- 从底层谈WebGIS 原理设计与实现(二):探究本质,WebGIS前端地图显示之地图比例尺换算原理
从底层谈WebGIS 原理设计与实现(二):探究本质,WebGIS前端地图显示之地图比例尺换算原理 作者:naaoveGI… 文章来源:http://www.cnblogs.com/naaove ...
- [转帖]SSH原理与运用(二):远程操作与端口转发
SSH原理与运用(二):远程操作与端口转发 http://www.ruanyifeng.com/blog/2011/12/ssh_port_forwarding.html 接着前一次的文章,继续介绍S ...
- RabbitMQ原理与相关操作(二)
接着 上篇随笔 增加几个概念: RabbitMQ是一个在AMQP(高级消息队列协议)标准基础上完整的,可服用的企业消息系统. AMQP模型的功能组件图(上图摘自 Sophia_tj 的 第2章 AMQ ...
- Spring Boot自动配置原理与实践(二)
前言 在之前的博文(Spring Boot自动配置原理与实践(一))中,已经介绍了Spring boot的自动配置的相关原理与概念,本篇主要是对自动配置的实践,即自定义Starter,对原理与概念加深 ...
- angr原理与实践(二)—— 各类图的生成(CFG CG ACFG DDG等)
本文系原创,转载请说明出处 Please Subscribe Wechat Official Account:信安科研人,获取更多的原创安全资讯 上一篇文章介绍了angr的原理,自此篇文章开始, ...
- Web程序的运行原理及流程(二)
其实WEB服务器和WEB应用服务器这两个概念特别容易混淆 可以理解为装了不同软件(服务)的两台计算机(服务器)吧 先对两个概念做一个简单介绍 了解了基本的概念 我们再用两个典型的例子做一下比较(建立 ...
随机推荐
- Evensgn 的债务
问题 A: Evensgn 的债务 大致题意:a欠b5元,b欠c5元,那么最小债务总额为a欠c5元,给你关系,求最小债务总额! 不想说话...一句超级大水题,我居然没读懂!!差点想到网络流了...其实 ...
- 【BZOJ1483】【HNOI2009】梦幻布丁
题意:n个连续的点,有若干种颜色,每个颜色会因为某些操作变为另一种颜色,动态查询颜色段数. 解题思路:对每个颜色开一棵平衡树启发式合并应该是最裸的想法,但是我们有更优的! 考虑对每个颜色利用链表储存它 ...
- [SPOJ962]Intergalactic Map 拆点+最大流
Jedi knights, Qui-Gon Jinn and his young apprentice Obi-Wan Kenobi, are entrusted by Queen Padmé Ami ...
- hdu 5437Alisha’s Party(优先队列)
题意:邀请k个朋友,每个朋友带有礼物价值不一,m次开门,每次开门让一定人数p(如果门外人数少于p,全都进去)进来,当所有人到时会再开一次,每次都是礼物价值高的人先进. /*小伙伴最开始gg了,结果发现 ...
- bzoj2839: 集合计数 容斥+组合
2839: 集合计数 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 523 Solved: 287[Submit][Status][Discuss] ...
- C语言程序注释风格
良好编程习惯的养成对于一个程序员的发展非常重要,而注释对于一份程序来讲又是一个必不可少的组成部分,今天来研究一下C语言程序的注释风格. 注释是源码程序中非常重要的一部分,一般情况下,源程序有效注释量必 ...
- sklearn.model_selection 的 train_test_split作用
train_test_split函数用于将数据划分为训练数据和测试数据. train_test_split是交叉验证中常用的函数,功能是从样本中随机的按比例选取train_data和test_data ...
- Feign报错Caused by: com.netflix.client.ClientException: Load balancer does not have available server for client
问题描述 使用Feign调用微服务接口报错,如下: java.lang.RuntimeException: com.netflix.client.ClientException: Load balan ...
- Java 8 的时间日期 API
上一篇文章『Java 的时间日期 API』中,我们学习了由 Date.Calendar,DateFormat 等组成的「传统时间日期 API」,但是传统的处理接口设计并不是很友好,不易使用.终于,Ja ...
- 浅谈@RequestMapping @ResponseBody 和 @RequestBody 注解的用法与区别
浅谈@RequestMapping @ResponseBody 和 @RequestBody 注解的用法与区别 Spring 2.5 版本新增了注解功能, 通过注解,代码编写简化了很多:但熟悉注解的使 ...