转:自己动手写插件框架(3)

转自:http://www.devbean.net/2012/03/building-your-own-plugin-framework-3/

编程语言的支持

在前面的章节中,我们已经了解到,如果你能够使用编译器令应用程序和插件的虚表匹配,那么就可以保持 C++ 虚表级别的兼容性;或者你可以使用 C 级别的兼容性,然后就能使用不用的编译器去构建系统,但其限制在于你只能编写纯 C 应用。这样的话,你就不能够使用前面我们在例子 IActor 中看到的那种优雅的 C++ 接口。

纯 C 实现

在纯 C 的编程模型中,你只能使用 C 开发插件。当实现PF_CreateFunc函数时,需要返回一个 C 对象同应用程序的 C 对象模型进行交互。

但是,我们知道,C 语言是一个过程语言,没有对象的概念。那么,刚刚提到的 C 对象和 C 对象模型是怎么回事呢?为了理解这一点,我们必须认识到,C 语言已经提供了足够多的抽象机制来实现对象、引入多态(这一点在我们的插件框架中尤其重要)以及支持面向对象风格的编程。事实上,原始的 C++ 编译器就是一个 C 编译器的前端。它将 C++ 代码翻译成 C 代码,然后直接输出给一个普通的 C 编译器(当然,我们说的是“原始的”C++ 编译器)。它的名字就叫 Cfront 这已经足够说明问题了。

核心技术是,使用包含函数指针的结构体。每个函数声明的第一个参数都应该是指向本结构体的指针。这个结构体也可能包含其他数据成员。也就是说,我们使用结构体模拟了 C++ 的类,提供了封装(在一个地方保存状态和行为)、继承(通过将父结构体作为第一个数据成员实现)和多态(通过设置不同的函数指针实现)。

C 不支持析构函数、函数和运算符的重载以及命名空间,所以在定义接口时,我们能够设置的选项极为有限。这可能有点因祸得福,因为那些掌握了 C++ 语言不同子集的人们都明白使用接口,而不一定了解析构函数、运算符重载那些机制。减少接口的语言结构上的限制,有助于简化接口,提高可用性。

在下面的章节中,我们将讨论面向对象的 C 语言设计。下面是我们的简单游戏的 C 对象模型的实现。如果你快速浏览一遍,你会发现它甚至支持集合类型和对象遍历器。

C

 1 #ifndef C_OBJECT_MODEL
 2 #define C_OBJECT_MODEL
 3
 4 #include <apr-1/apr.h>
 5
 6 #define MAX_STR 64 /* max string length of string fields */
 7
 8 typedef struct C_ActorInfo_
 9 {
10     apr_uint32_t id;
11     apr_byte_t   name[MAX_STR];
12     apr_uint32_t location_x;
13     apr_uint32_t location_y;
14     apr_uint32_t health;
15     apr_uint32_t attack;
16     apr_uint32_t defense;
17     apr_uint32_t damage;
18     apr_uint32_t movement;
19 } C_ActorInfo;
20
21 typedef struct C_ActorInfoIteratorHandle_ { char c; } * C_ActorInfoIteratorHandle;
22 typedef struct C_ActorInfoIterator_
23 {
24   void (*reset)(C_ActorInfoIteratorHandle handle);
25   C_ActorInfo * (*next)(C_ActorInfoIteratorHandle handle);
26
27   C_ActorInfoIteratorHandle handle;
28 } C_ActorInfoIterator;
29
30 typedef struct C_TurnHandle_ { char c; } * C_TurnHandle;
31 typedef struct C_Turn_
32 {
33   C_ActorInfo * (*getSelfInfo)(C_TurnHandle handle);
34   C_ActorInfoIterator * (*getFriends)(C_TurnHandle handle);
35   C_ActorInfoIterator * (*getFoes)(C_TurnHandle handle);
36
37   void (*move)(C_TurnHandle handle, apr_uint32_t x, apr_uint32_t y);
38   void (*attack)(C_TurnHandle handle, apr_uint32_t id);
39
40   C_TurnHandle handle;
41 } C_Turn;
42
43 typedef struct C_ActorHandle_ { char c; } * C_ActorHandle;
44 typedef struct C_Actor_
45 {
46     void (*getInitialInfo)(C_ActorHandle handle, C_ActorInfo * info);
47     void (*play)(C_ActorHandle handle, C_Turn * turn);
48
49     C_ActorHandle handle;
50 } C_Actor;
51
52 #endif

纯 C++ 实现

在纯 C++ 编程模型中,你需要使用 C++ 开发插件。插件的接口函数可以由 static 成员函数实现,或者是普通的 static、全局函数(毕竟,C++ 简单来说就是 C 的超集)。此时,我们的对象模型就可以是普通 C++ 对象模型。下面的代码是同一游戏的 C++ 对象模型的实现。它和前面的 C 对象模型几乎是一样的。

C++

 1 #ifndef OBJECT_MODEL
 2 #define OBJECT_MODEL
 3
 4 #include "c_object_model.h"
 5
 6 typedef C_ActorInfo ActorInfo;
 7
 8 struct IActorInfoIterator
 9 {
10     virtual void reset() = 0;
11     virtual ActorInfo * next() = 0;
12 };
13
14 struct ITurn
15 {
16     virtual ActorInfo * getSelfInfo() = 0;
17     virtual IActorInfoIterator * getFriends() = 0;
18     virtual IActorInfoIterator * getFoes() = 0;
19
20     virtual void move(apr_uint32_t x, apr_uint32_t y) = 0;
21     virtual void attack(apr_uint32_t id) = 0;
22 };
23
24 struct IActor
25 {
26     virtual ~IActor() {}
27     virtual void getInitialInfo(ActorInfo * info) = 0;
28     virtual void play( ITurn * turnInfo) = 0;
29 };
30
31 #endif

同时提供 C/C++

在同时提供 C/C++ 的编程模型中,我们既可以使用 C 语言开发插件,也可以使用 C++。当向系统注册插件时,我们必须指明到底是 C 对象还是 C++ 对象。当你创建一个平台时,这一技术无疑非常有用,因为你不应该限制第三方开发人员必须使用哪种语言。

我们的插件框架支持这种编程模型,但是真正的工作应当由应用程序去完成。每种对象类型都必须同时实现 C 接口和 C++ 接口。这意味着,你会有一个含有虚表的 C++ 类,还会有一个指向虚表中函数的指针的集合。这种机制并不那么重要,我们会在后面的游戏开发中具体演示。

注意,从插件开发者的角度来看,混合 C/C++ 模型不会增加任何额外的复杂度。插件开发者通常会使用 C 接口或者 C++ 接口来开发 C 或者 C++ 插件。

混合使用 C/C++

在混合使用 C/C++ 的编程模型中,我们可以使用 C++ 开发插件,但是需要基于 C 对象模型。这种机制需要引入 C++ 包装类,用于实现 C++ 对象模型,但是要包装成 C 对象。插件开发者需要自己处理这一层,在 C 和 C++ 之间来回翻译每一个调用、参数以及返回值。这意味着,你需要在应用程序对象模型之上做更多工作,但是通常这些工作都很直接。这样做的好处是,能够让插件开发者使用方便的 C++ 编程模型,同时拥有 C 级别的兼容性。我们会在后面的章节中再次回到这个问题上来。

语言对比

下面我们给出两种语言(C 和 C++)、两种部署方式(动态、静态)的对比。

  C++ C
静态
  • 最好的性能
  • API 简单
  • 易于调试
  • 可以使用任何现代 C++ 编译器
  • 在不支持动态库的系统上的唯一选择
  • 避免 DLL 陷阱
  • 要求整个系统的源代码兼容
  • 没有使用的;适用于仅支持 C 编译器、不支持动态库的系统
动态
  • 较好的性能
  • API 简单
  • 易于调试
  • 必须使用虚表兼容的编译器(不是完全的 ABI 兼容)
  • 新版本的应用程序使用的编译器虚表结构改变,就会导致兼容性问题
  • 目标系统要求支持动态库
  • 同时具有 C 和 C++
  • API 笨重
  • 调试困难
  • 可以使用任何编译器发布
  • 更新编译器版本无影响
  • 目标系统要求支持动态库

我们讨论的目的是认识到,在同时支持 C/C++ 的模型中,如果使用 C++ 编写插件,系统将得益于,也会受限于 C++;如果使用 C 编写插件,系统则得益于,也会受限于 C。而在混合 C/C++ 模型中,系统仅受到 C 的影响,因为 C++ 那层完全隐藏在插件实现之下了。初看起来,这有点复杂,但是至少你有机会做出选择,而且插件系统允许你自己选择最合适的实现方式。这并不会强制你使用某一种编程模型,也不会要求去找一个最小公分母。我们只需看清二者的差别,选择最合适的方式。

时间: 2024-12-16 08:27:56

转:自己动手写插件框架(3)的相关文章

转:自己动手写插件框架(2)

转自:http://www.devbean.net/2012/03/building-your-own-plugin-framework-2/ 插件编程接口 所谓插件,其实就是基于接口的设计.基于插件的系统最基本的一点就是,要有一个中心系统,用于加载未知的插件,并且能够使用预先定义好的接口和协议与这些插件进行交互. 最基本的方式是定义一个接口,提供一系列插件(动态的或者是静态)需要暴露出的函数.这种实现从技术上说是可行的,但实际并不那么简单地操作.原因在于,一个插件需要支持两类接口,但是却只能暴

转:自己动手写插件框架(1)

转自:http://www.devbean.net/2012/03/building-your-own-plugin-framework-1/ 本系列文章来自 Building Your Own Plugin Framework,主要内容是讨论使用 C/C++ 语言开发跨平台的插件框架所需要的架构.开发方法以及部署.我们将从分析现有插件/组件系统开始,一步步深入了解如何开发插件框架,以及很多需要注意的问题,比如二进制兼容性等,在文章的最后,我们将给出一个比较合理的解决方案. 在本系列文章中,我们

《开源框架那点事儿20》:自己动手写前端框架

曾经发表过一篇文章:TinyAdmin前端展现框架.其在线演示路径为:http://www.tinygroup.org/tinyadmin/.应该说有很多人还是感觉兴趣的,可是因为这个是基于SmartAdmin框架改写的,尽管我们自己买了SmartAdmin的授权,可是广大用户假设要用的时候,就会有授权相关的问题,这会大大影响一些人的使用决策--尤其是会再发行的朋友. 再一个原因是SmartAdmin初看不是不错的.可是实际用起来.里面的问题比較多.对IE8基本上能够说是不兼容,尽管我们努力进行

《自己动手写开源框架10》:Web界面快速开发实践

下面是一些常用的链接,供大家使用: GIT地址:https://git.oschina.net/tinyframework/tiny问题报告:https://git.oschina.net/tinyframework/tiny/issues更多内容,请看本人博客,不一样的内容,一样的精彩! 在展示过程的同时,会把相关的知识做一个充分的介绍 .一.寻找网站模板 要做网站,不能没有模板,自己不会做网页设计,咋办?问谷歌找百度呗,找了一阵,看到下面这个模板不错,就它了. http://www.toop

自己动手写web框架----1

本文可作为<<自己动手写struts–构建基于MVC的Web开发框架>>一书的读书笔记. 一个符合Model 2规范的web框架的架构图应该如下: Controller层的Servlet就是一个全局的大管家,它判断各个请求由谁去处理. 而各个BusinessLogic就决定具体做什么. 通过上面的图,我们能看出来核心的组件就是那个servlet,它要处理所有的请求. 那么我们就先在web.xml里配置这个servlet: <?xml version="1.0&quo

自己动手写web框架----2

在上一节,我们自己写的web框架,只能运行显示一个HelloWorld.现在我们对其进行一次加工,让他至少能运行一个登陆程序. 首先看login.jsp <%@ page contentType="text/html; charset=UTF-8" language="java" import="java.sql.*" errorPage="" %> <%@ page import=" java.u

自己动手写RPC框架到dubbo的服务动态注册,服务路由,负载均衡功能实现

RPC即远程过程调用,它的实现方式有很多,比如webservice等.框架调多了,烦了,没激情了,我们就该问自己,这些框架的作用到底是什么,来找回当初的激情. 一般来说,我们写的系统就是一个单机系统,一个web服务器一个数据库服务,但是当这单台服务器的处理能力受硬件成本的限制,是不能无限的提升处理性能的.这个时候我们使用RPC将原来的本地调用转变为调用远端的服务器上的方法,给系统的处理能力和吞吐量带来了提升. RPC的实现包括客户端和服务端,即服务的调用方和服务的提供方.服务调用方发送rpc请求

自己动手写javaEE框架-为jqgrid换换皮肤然后加到我的框架里

软件151 苏垚 今天让我倒腾半天的居然不是写我的javaEE框架,而是更换jqgrid的皮肤.我是相当的讨厌jqgrid的默认皮肤,它总让我想起一直想学而都没学过的EXTJS,看多了那种淡蓝色的皮肤太多了绝对视觉疲劳,所以这篇博文就从jqgrid换肤讲起吧,这个过程在网络上找到的资料比较少,因此我今天的博文还是很有实用价值的.不废话了,下面开始吧! 首先我们在浏览器地址栏里填入地址:http://www.trirand.com/blog/?page_id=6,这是jqgrid的下载界面,如下图

第一招:自己动手写HTTP框架介绍

我相信很多人都会质疑:网上已经有那么多开源的HTTP框架,android-async-http已经很成熟了,Stay你再来讲而且还收费有意义么. Stay在这里统一作答. HTTP作为所有app的必备功能,我觉得有必要来讲. 开源框架授之以鱼,我希望能做到授之以渔. 看看android-async-http里的issues,如果你也碰到变态需求,你是否会改,而不是等待作者帮你解. 每个人的理解能力不一样,可能看不懂源码,我希望把骨架拎出来,一个个讲解,让你弄懂它的设计思想. 可能就算你看懂了代码