许多人都知道MapGuide提供了.NET、PHP和Java三种类型的Web API,但是不知道MapGuide是如何创建这三种类型的API的。试想一下,如果分别去创建这三种API,这将是一个很难维护的工作。每次增加或修改一些功能,就需要对三种类型的API都进行修改。所以,MapGuide使用了SWIG来自动生成这三种类型的API。我想这个时候许多人会问,什么是SWIG呢?我怎么从来没有听说过这个东东呢!其实,我也是在做MapGuide开发的时候才开始了解SWIG的。所以,首先让我们来认识一下SWIG,然后再来看MapGuide是如何使用SWIG来生成API的。

1. SWIG简介

SWIG是Simple Wrapper and Interface Generator的缩写,是一个帮助使用C或者C++编写的软件创建其他编语言的API的工具。例如,我想要为一个C++编写的程序创建.NET API,一般情况下我必须使用托管C++(Managed C++)去编写大量的代码才能生成它的.NET API。有了SWIG,这个机械的工作将变得非常简单。你只须要使用一个接口文件告诉SWIG要为那些类创建.NET API,SWIG就会自动帮你生成它的.NET API。是不是非常的酷啊?

当然,SWIG不仅仅支持创建.NET API。最新版本的SWIG支持常用脚本语言Perl、PHP、Python、Tcl、Ruby和非脚本语言C#, Common Lisp (CLISP, Allegro CL, CFFI, UFFI), Java, Modula-3, OCAML以及R,甚至是编译器或者汇编的计划应用(Guile, MzScheme, Chicken)。

下面我们通过一个例子来看看SWIG是如何帮我们创建API的。假设我打算为如下的C++类创建C#和Java的API。

/* SwigTest.h */

class CSwigTest {
    public:
        CSwigTest();
        virtual ~CSwigTest();
        int Add(int a, int b) { return a + b; }
        int Substract(int a, int b) { return a - b; }

int Multiple(int a, int b) { return a * b; }
        float Divide(int a, int b) { return (float)a / (float)b; }
    };

1.1 接口文件

首先,你需要写一个接口文件(Interface File),告诉SWIG要为那些类的那些方法创建API。如下的接口文件只为类CSwigTest的方法Add(...)和Subtract(...)生成API,因为在接口文件的接口声明部分只声明了两个方法。

/* SwigTest.i */
    %module SwigTest
    %{
    #include "SwigTest.h"
    %}

/* --- 接口声明部分 ---*/
    class CSwigTest {
    public:
        int Add(int a, int b);

int Substract(int a, int b);

};

注解:%module标记用于定义SWIG生成的模块的名称,%{%}标记中的内容会被一字不差地插入SWIG自动生成的文件xxx_wrapper.c中,其中xxx代表用%module指定的模块名称。这个文件会在下面介绍,不必着急去理解它究竟有什么作用。

如果打算为类中所有方法创建API,那么有一个非常简单的办法,在接口文件的类声明部分使用%include标记。SWIG将对%include所指定的文件进行语法分析,类中所有公有方法(Public Method)都将在API中暴露。

/* SwigTest.i */
    %module SwigTest
    %{
    #include "SwigTest.h"
    %}
    #include “SwigTest.h”

1.2 编译模块

有了接口文件之后,剩下的事就是执行几条命令。下面我们以Windows平台上生成.NET API为例介绍这些命令。

    (a) 调用SWIG自动生成代码

swig -csharp  SwigTest.i

执行上面的命令会产生一个C语言文件SwigTest_wrapper.c和多个C#文件。在文件SwigTest_wrapper.c中,SWIG为接口文件中接口声明部分指定的每个方法产生一个全局方法,以便C#使用Pinvoke调用这些函数。而那些C#文件就是用来生成.NET API的。

(b) 为C++代码生成DLL(动态链接库)

cl SwigTest_wrapper.c *.cpp

link *.obj /out:SwigTest.dll

执行上面的命令,会为我们编写C++代码生成DLL。在编译C++文件时,一定要包括SWIG为我们生成的C++文件SwigTest_wrapper.cpp。

注意:为了让大家便于理解上述命令,这些命令并没有列出完整的编译和链接选项。

(C) 生成.NET模块

csc /out:SwigTestNotNetAPI.dll /target:library *.cs

执行上面的命令就生成了.NET API模块SwigTestNotNetAPI.dll。如果用户想使用这些API,只需要添加对SwigTestNotNetAPI.dll的引用(Reference)就可以了。

生成其它语言类型API的命令基本类似,下面我们再以Java在Unix平台下的命令为例结束对SWIG的介绍。事实上,SWIG也是一个开源项目。如果想了解更多关于SWIG的信息,大家可以登陆SWIG的官方网站www.swig.org,那里有SWIG最详细的资料。
    $ swig -java SwigTest_wrapper.i
    $ gcc -c *.cpp SwigTest_wrapper.c -I/c/jdk1.3.1/include -I/c/jdk1.3.1/include/win32
    $ gcc -shared *.o -mno-cygwin -Wl,--add-stdcall-alias  -o SwigTest.dll

2. SWIG在MapGuide中的应用

我们在前面已经提到过,MapGuide使用了SWIG来自动生成.NET、Java和PHP这三种类型的API。但是,SWIG也有不少限制和缺陷,所以MapGuide对SWIG源代码进行了大量的修改,以满足自己的要求。下面,我们看看这些改进。

2.1 IMake工具

SWIG要求开发人员编写一个接口文件,那么能否让接口文件自动生成呢?借用一句中国移动的广告词,我能!虽然SWIG没有提供这方面的工具,但是我们可以自己开发吗!IMake(Interface Maker)就是为了满足这样的要求而开发一个工具,给定一个XML文件,它能帮你自动生成SWIG接口文件。登录MapGuide开源版的代码浏览页面(http://trac.osgeo.org/mapguide/browser),在root/trunk/MgDev/BuildTools/WebTools/IMake文件夹下可以找到IMake的源代码。

下面我们以MapGuide中使用的XML文件/trunk/MgDev/Web/src/MapGuideApi/MapGuideApiGen.xml为例,介绍一下IMake的用法。为了便于理解,在此我删掉了文件中的部分内容。

<?xml version="1.0" encoding="UTF-8"?>
    <Parameters>
      <!-- 对应于%Module标记. -->
      <Module name="MapGuideApi" />

<!-- 生成的接口文件的名称. -->
      <Target path="./MapGuideApi.i" />

<!-- 对应于%{%}标记 -->
      <CppInline>
        #include &lt;string&gt;
        #include &lt;map&gt;
        #include "MapGuideCommon.h"
        #include "WebApp.h"
        ......
      </CppInline>

<!-- 用于替换接口中使用的部分类型 -->
      <TypeReplacements>
        <TypeReplacement oldtype="CREFSTRING" newtype="STRINGPARAM" />
        <TypeReplacement oldtype="INT64" newtype="long long" />
      </TypeReplacements>

<!-- 此部分的内容添加在%{%}之后,接口声明部分之前 -->
      <SwigInline>
        %include "language.i"   //typemaps specific for each language
        ......
      </SwigInline>
     
      <!-- 为指定的C++文件生成接口声明 -->
      <Headers>
        <Header path="../../../Common/Foundation/Data/Property.h" />
        ......
      </Headers>
    </Parameters>

执行命令“IMake MapGuideApiGen.xml”,IMake就帮我们自动生成了如下SWIG接口文件MapGuideApi.i。

/* MapGuideApi.i */
    %module MapGuideApi
    %{
        #include <string>;
        #include <map>;
        #include "MapGuideCommon.h"
        #include "WebApp.h"
        ......
    %}
 
    %include "language.i"   //typemaps specific for each language
    ......

class MgProperty: public MgNamedSerializable
    {
    public:
        virtual INT16 GetPropertyType();
        STRING GetName();
        void SetName(CREFSTRING name);
    };

......

如果打开文件Proper.h,我们可以看到MgProperty有更多的方法,例如CanSetName(...)。为什么只有三个方法添加到了SWIG接口文件中?IMake在生成接口文件时,它会查找C++头文件中的宏PUBLISHED_API。只有被PUBLISHED_API修饰的方法,才会添加到接口文件中。

注:宏PUBLISHED_API和INTERNAL_API的定义如下。

#define PUBLISHED_API public

#define INTERNAL_API public

class MG_FOUNDATION_API MgProperty : public MgNamedSerializable
    {
    PUBLISHED_API:

virtual INT16 GetPropertyType() = 0;  /// __get  
        STRING GetName();  /// __get, __set
        void SetName(CREFSTRING name);
 
    INTERNAL_API:
        virtual bool CanSetName();
 
    protected: 
        INT32 GetClassId();
        MgProperty();
        virtual ~MgProperty();
        virtual void Dispose();  
        virtual void ToXml(string &str, bool includeType = true, string rootElmName = "Property") = 0;
 
    private:
        friend class MgPropertyCollection;
        STRING m_propertyName;
 
    CLASS_ID:
        static const INT32 m_cls_id = Foundation_Property_Property;
    };     
    给定一个C++常量定义文件,IMake还可以自动生成对应的其他语言的常量定义文件。MapGuide .NET Web API中的所有常量都是使用IMake来生成的,例如MgMineType、MgPropertyType等。下面我们以MapGuide中使用的XML文件/trunk/MgDev/Web/src/MapGuideApi/Constants.xml为例,介绍如何自动生成各种语言的常量定义文件。同样,为了便于理解,在此我删掉了文件中的部分内容。与MapGuideApiGen.xml不同,Constants.xml包含一个新的元素Classes用来指出需要在目标语言中产生对应的常量类的C++类。
    <?xml version="1.0" encoding="UTF-8"?>

<Parameters>

<!-- 用于替换类型 -->

<PHPTypeReplacements>
        <TypeReplacement oldtype="STRING" newtype="" />
        <TypeReplacement oldtype="INT16" newtype="" />
        ......
    </PHPTypeReplacements>
    <CSharpTypeReplacements>
        <TypeReplacement oldtype="STRING" newtype="string" />
        <TypeReplacement oldtype="INT16" newtype="short" />
        ......
    </CSharpTypeReplacements>
    <JavaTypeReplacements>
        <TypeReplacement oldtype="STRING" newtype="String" />
        <TypeReplacement oldtype="INT16" newtype="short" />
        ......
    </JavaTypeReplacements>

<Namespace>OSGeo.MapGuide</Namespace>
    <Package>org.osgeo.mapguide</Package>

<!-- 用于指出需要在目标语言中产生对应的常量类的C++类 -->

<Classes>
        <Class name="MgMineType" />
        <Class name="MgPropertyType" />
        ......
    </Classes>

<Headers>
        <Header path="../../../Common/Foundation/Data/MimeType.h" />
        <Header path="../../../Common/Foundation/Data/PropertyType.h" />
        ......
    </Headers>

</Parameters>

执行命令“IMake.exe Constants.xml C# Constants.cs”,IMake就帮我们自动生成了一个C#常量文件Constants.cs。对于文件/trunk/MgDev/Common/Foundation/Data/PropertyType.h中定义了如下常量,

class MgPropertyType
    {
    PUBLISHED_API: 
       static const int Null     =  0;
       static const int Boolean  =  1;
       static const int Byte     =  2;
       static const int DateTime =  3;
       static const int Single   =  4;
       ......
    };

在生成的Constants.cs文件中,有如下的类定义。
    class MgPropertyType
    { 
       static const int Null     =  0;
       static const int Boolean  =  1;
       static const int Byte     =  2;
       static const int DateTime =  3;
       static const int Single   =  4;
       ......
    };

这个文件可以被C#的编译器直接编译,所以MapGuide没有使用SWIG生成常量的API,而是直接使用IMake。 如果想生成PHP或Java的常量定义文件,只需要将IMake命令的参数"C#"替换为"PHP"或"Jave"就可以了。

2.2 MapGuide对SWIG的修改
    在MapGuide开始使用SWIG的时候,可用的SWIG的最高版本是1.3.21,从那以后MapGuide在没有升级过SWIG。所以,到现在为止,MapGuide的SWIG版本仍然是1.3.21。这个版本的SWIG有不少限制和缺陷,

  • 无法创建基于自定义根异常类MgException的异常处理机制。
  • 无法创建属性(Property)。
  • 对某些方法无法产生正确的API。例如,如果方法GetA(...)返回的是类A的子类B的实例,SWIG创建的API返回的仍然是A类的实例。此时如果你把返回值转换为类B,那么转换会失败。
        A* GetA();
  • ......

事实上最新的SWIG版本也没有全部解决这些问题,所以MapGuide对SWIG源代码进行了大量的修改,以满足自己的要求。看看MapGuide在使用SWIG命令是传入的参数,我们可以发现有许多参数不是SWIG标准的参数,例如proxydir、clsidcode、clsiddata、catchallcode等。

swig -c++ -csharp -dllname MapGuideUnmanagedApid -namespace OSGeo.MapGuide -proxydir ./custom -baseexception MgException -clsidcode getclassid.code -clsiddata m_cls_id -catchallcode catchall.code -dispose &quot;((MgDisposable*)arg1)-&gt;Release()&quot; -rethrow &quot;e-&gt;Raise();&quot; -nodefault -noconstants -module MapGuideApi -o MgApi_wrap.cpp -lib ../../../Oem/SWIGEx/Lib MapGuideApi.i

在此,我们不打算一一介绍这些参数,因为在多数情况下你没有必要对了解参数的含义。我们只介绍MapGuide是如何来解决上述SWIG的第二和第三个问题的,因为在扩展MapGuide Web API的时候你可能会用得着。

2.2.1 创建属性
    如果你看过MapGuide源代码的话,你会发现有许多方法声明之后有“__get”、“__set”或“__get, __set”这样的注释,如类MgProperty中的方法。
    class MgProperty : public MgNamedSerializable
    {
    PUBLISHED_API:
        virtual INT16 GetPropertyType() = 0;  /// __get  
        STRING GetName();  /// __get, __set
        void SetName(CREFSTRING name);
        ......
};
    这些注释是有特殊含义的,它们就是用来解决上述SWIG的第二个问题的。当IMake工具扫描C++头文件时发现这注释后,会在目录“./custom”下为每个类产生一个帮助创建属性的代码文件。例如,如果要类MgProperty生成.NET API,IMake会在“./custom”生成一个文件名为MgProperty的C#代码文件,它的内容如下:
    public int PropertyType {
        get {return GetPropertyType(); }
    }
    public int Name {
        get { return GetPropertyType(); }
        set { setName(value);}
    }
    如果在SWIG的命令行中使用了参数proxydir,那么SWIG在为每个类生成代码的时候,会在proxydir所指定的目录下查找和类名相同的文件,并且将这个文件中的代码插入类的目标代码中。通过这种办法,就解决了上述SWIG的第二个问题。

2.2.2 ClassId
    MapGuide Web API中的所有类都是从MgObject继承而来的,在类MgObject中有一个方法GetClassId()用来返回每个类唯一的ID值。MapGuide就是用这个方法来解决上述SWIG的第三个问题的,所以如果要在MapGuide Web API中增加一个新类,一定要覆盖(override)这个方法,并且提供一个唯一的ID值。
    class MgObject
    {
    EXTERNAL_API:
        virtual INT32 GetClassId();
        virtual STRING GetClassName();
    INTERNAL_API:
        virtual ~MgObject();
        bool IsOfClass(INT32 classId);
    };

3. 扩展MapGudie Web API

如果你发现现有的MapGuide Web API无法满足你的要求,没有关系,你可以去尝试扩展它,因为MapGuide是开源的。

如果要新添类,基本步骤如下:
    (a) 修改C++代码,添加新的类。对于需要暴露于API的方法,使用宏PUBLISHED_API修饰。
    (b) 修改XML文件/trunk/MgDev/Web/src/MapGuideApi/MapGuideApiGen.xml的Headers部分,为每个新添加类所在的C++头文件增加一个Header元素。下面的示例中,"path"代表C++头文件的路径,"filename.h"代表文件的名称。
    <Headers>
        <Header path="path/filename.h" />
        ......
    </Headers>
    (c) 重新编译MapGuide的Web模块(/trunk/MgDev/Web/src/)。
 
    如果要增加一些新的方法到现有的类中,基本步骤如下:
    (a) 修改C++代码,添加新的方法,并且使用宏PUBLISHED_API修饰这些方法。
    (b) 重新编译MapGuide的Web模块(/trunk/MgDev/Web/src/)。

如果要新增常量类,基本步骤如下:
    (a) 修改C++代码,添加新的常量类。
    (b) 修改XML文件/trunk/MgDev/Web/src/MapGuideApi/Constants.xml,在Classes部分为每个新添加常量类增加一个Class元素,在Headers部分为每个新添加常量类所在的C++头文件增加一个Header元素。下面的示例中,"ClassName"代表新添加的C++常量类的名称。

<Classes>
        <Class name="ClassName" />
        ......
    </Classes>
    <Headers>
        <Header path="path/filename.h" />
        ......
    </Headers>   
    (c) 重新编译MapGuide的Web模块(/trunk/MgDev/Web/src/)。

SWIG和MapGuide Web API相关推荐

  1. MapGuide HTTP API

    本节将介绍什么是HTTP API,HTTP API与MapGuide Web API的关系,以及如何使用HTTP API.一般情况下,我们并不需要使用HTTP API,而且Viewer API已经包装 ...

  2. MapGuide源码分析----MapGuide Web扩展源码分析

    本节中,我们将通过介绍如何完成枚举资源功能来介绍MapGuide Web扩展的部分源代码. 在浏览器端的地址栏输入类似如下字符串,就会发送一个枚举资源的HTTP请求. http://hostname/ ...

  3. Web Api学习一

    接触WebApi读的第一篇文章: ASP.NET Web API(一):使用初探,GET和POST数据 实践过程中,用的Fiddler模拟Post请求时收到的对象总是为空null 解决:将文章中的内容 ...

  4. python 图表_Python入门学习系列——使用Python调用Web API实现图表统计

    使用Python调用Web API实现图表统计 Web API:Web应用编程接口,用于URL请求特定信息的程序交互,请求的数据大多以非常易于处理的格式返回,比如JSON或CSV等. 本文将使用Pyt ...

  5. (四)Asp.net web api中的坑-【api的返回值】

    (四)Asp.net web api中的坑-[api的返回值] 原文:(四)Asp.net web api中的坑-[api的返回值] void无返回值 IHttpActionResult HttpRe ...

  6. WCF 和 ASP.NET Web API

    地址:https://docs.microsoft.com/zh-cn/dotnet/framework/wcf/wcf-and-aspnet-web-api WCF 是 Microsoft 为生成面 ...

  7. 【Web API系列教程】1.2 — Web API 2中的Action Results

    前言 本节的主题是ASP.NET Web API怎样将控制器动作的返回值转换成HTTP的响应消息. Web API控制器动作能够返回下列的不论什么值: 1. void 2. HttpResponseM ...

  8. Dynamics CRM2016 Web API之创建记录

    前篇介绍了通过primary key来查询记录,那query的知识点里面还有很多需要学习的,这个有待后面挖掘,本篇来简单介绍下用web api的创建记录. 直接上代码,这里的entity的属性我列了几 ...

  9. ASP.NET Web API自身对CORS的支持:从实例开始

    在<通过扩展让ASP.NET Web API支持W3C的CORS规范>中我们通过自定义的HttpMessageHandler为ASP.NET Web API赋予了跨域资源共享的能力,具体来 ...

最新文章

  1. 直观、形象、动态,一文了解无处不在的标准差
  2. 堆栈溢出从入门到提高
  3. 上机环境是什么意思_380元入手RX580满血显卡,跑分17万,还要什么自行车
  4. 从C语言中的指针看C#中委托
  5. 一文读懂ClickHouse(概述,安装,数据类型,表引擎,sql语法)
  6. web前端开发初学者必看的学习路线图课程内容分享
  7. 06 外键的三种分类
  8. matlab将txt转成dat,将matlab中数据保存为txt或dat格式
  9. java 右下角_java右下角弹窗
  10. android 路由跟踪,手机移动端网络路由跟踪探测方式
  11. 域渗透-横向移动(PTH)
  12. 使用Android studio 制作完app运行安装没问题,但是打开显示。。。keeps stopping
  13. 关于期权池Option Pools与Vesting:码农创业防身必备法器
  14. 在哪里可以批量查询京东快递的单号?
  15. Android记录5--关于Android云测试的小思考
  16. STM32-MIDI音乐播放程序
  17. Scala语言基础(2)
  18. RAID及软RAID的实现,包括各级别RAID的原理及各级别RAID的实现
  19. jdk动态代理,cglib代理
  20. 杰理之去掉原唱人声实现【篇】

热门文章

  1. Page Register
  2. POI导出Excel(一)
  3. Twitter的网页代码
  4. ArrayList集合实现RandomAccess接口有何作用?为何LinkedList集合却没实现这接口?
  5. 深度学习基础:一致性的评价方法(皮尔森相关系数法、Cohen‘s Kappa相关系数)
  6. AD练习笔记 USB-TTL转换器
  7. 教你如何听懂英语新闻
  8. LVGL V0.01版本移植到STM32F4
  9. ABAQUS如何输出应力应变曲线(XY曲线)
  10. GBDT(梯度提升树)算法概述