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应用服务器这两个概念特别容易混淆 可以理解为装了不同软件(服务)的两台计算机(服务器)吧 先对两个概念做一个简单介绍 了解了基本的概念 我们再用两个典型的例子做一下比较(建立 ...
随机推荐
- Go学习——go+channel实战(转)
转载:http://studygolang.com/articles/2423 背景 在最近开发的项目中,后端需要编写许多提供HTTP接口的API,另外技术选型相对宽松,因此选择Golang + Be ...
- UVALive - 3942:Remember the Word
发现字典里面的单词数目多且长度短,可以用字典树保存 f[i]表示s[i~L]的分割方式,则有f[i]=∑f[i+len(word[j])] 其中word[j]为s[i~L]的前缀 注意字典树又叫前 ...
- 计蒜客NOIP2017提高组模拟赛(三)day1
火山喷发 火山喷发对所有附近的生物具有毁灭性的影响.在本题中,我们希望用数值来模拟这一过程. 在环境里有 n 个生物分别具有 A1,A2,⋯,An点生命值,一次火山喷发总计 MM 轮 ...
- java实现生产者/消费者的三种方式
package com.wenki.thread; import java.util.LinkedList; import java.util.concurrent.LinkedBlockingQue ...
- 解决win10 VC++6.0 应用程序无法正常运行 0xc0000142
废话不多说,无法正常运行原因就是win10不兼容中文版的vc,解决方法就是一句话,用英文版的msdev.exe替换中文版的msdev.exe,msdev.exe是vc的启动程序.直接上来教你怎么做.废 ...
- C语言程序设计第六次作业--循环结构(2)
(一)改错题 序列求和:输入一个正实数eps,计算序列部分和 1 - 1/4 + 1/7 - 1/10 + ... ,精确到最后一项的绝对值小于eps(保留6位小数). 输入输出样例: Input e ...
- Ambari2.6.0 安装HDP2.6.3(离线安装)
一.下载安装包 因为使用在线安装特别慢,所有的安装包加起来有9个G左右,所以本教程是通过迅雷下载包,然后上传到服务器,通过配置本地源的方式来实现的离线安装.通过ambari安装需要下载下面的三个主要包 ...
- 值得珍藏的HTTP协议详解
转自:http://www.cnblogs.com/li0803/archive/2008/11/03/1324746.html 引言 HTTP是一个属于应用层的面向对象的协议,由于其简捷.快速的方式 ...
- UI相关
前端 UI 框架 https://github.com/twbs/bootstrap https://github.com/google/material-design-lite https://gi ...
- ACM 畅通工程
Problem Description 某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇.省政府"畅通工程"的目标是使全省任何两个城镇间都可以实现交通 ...