swig

Table of Contents

SWIG(Simple Wrapper Interface Generator)是一个C/C++多语言扩展接口生成器。

update@201509: 相比boost::python可以更容易地使用C++来编写Python扩展

1. how it works

首先我们需要编写一个输入文件里面有我们需要包装的C++类或者是函数的声明并且加上一些特殊的标记,然后SWIG内部有一个C++解析器能够解析这些标记以及C++ 的类或者是函数的声明,然后生成包装代码。这个C++解析器来支持预处理的过程,能够处理宏并且进行#include头文件。如果在SWIG预处理处理的时候,那么会定义 宏SWIG.我们可以通过#ifdef SWIG来在我们的.h文件里面包含一些特殊标记。这些特殊标记在被SWIG预处理器处理的时候会生效。

2. a simple example

%module pycompack_  // 生成pycompack_.py和pycompack_wrap.cxx.
// 其中pycompack_.py是包装python代码,而pycompack_wrap.cxx是包装的C++代码。
%include "std_string.i" // 使用std::string返回二进制,我们通常会使用
%inline %{
#include "pycompack.h" // C++类或者是函数声明
%}

然后我们看看pycompack.h内部的片段代码

#ifndef SWIG // 如果没有在SWIG预处理下面的话那么包含头文件
#include "compack/compack.h"
#endif

#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
//we have to include this one
//unless the SWIG doesn't know int8_t  ...
#include <stdint.h>

//thin wrapper class
//because we want the UI simple.

//exception
#ifdef SWIG // 我们可以定义异常,将std::exception重命名为std_exception
%rename(std_exception) std::exception;
%rename(bsl_exception) bsl::Exception;
namespace std{
class exception{ // 对于异常的话我们必须写清楚构造和析构函数,不然内存会存在泄漏情况
  public:
    exception(){}
    ~exception(){}
};
}
namespace bsl{
class Exception{
  public:
    Exception(){}
    ~Exception(){}
};
}
#endif // SWIG
#define PYCOMPACK_THROW throw(bsl::Exception,std::exception)
#define PYCOMPACK_TRY()
#define PYCOMPACK_CATCH()


//to wrapper the binary data
#ifdef SWIG // 这样对于const char* bin,unsigned int binlen这样的话就会传递python的二进制
%apply (char *STRING, size_t LENGTH) { (const char* bin, unsigned int binlen) };
#endif

class Stream{
  public:
#ifndef SWIG
    compack::AutoBuffer _buffer;
#endif
    std::string content(size_t size) PYCOMPACK_THROW{ // 在这里我们可以抛异常
        PYCOMPACK_TRY();
        return std::string(static_cast<char*>(_buffer.buffer()),size);
        PYCOMPACK_CATCH();
    }
};

然后我们看看如何使用swig这个工具

// -python 表示为python扩展
// -c++ 表示我们使用c++语言
// -I 和编译器相同
// -includeall 会将所有的头文件全部包含进来
swig -Wall -python -c++ -I/usr/include -includeall pycompack.swg

然后会有pycompack_wrap.cxx和pycompack_.py生成,至于如何如何生成python扩展的话在python扩展一节说明。 因为python有自己的构建扩展的方式。

3. options

  • -c++ // 使用C++编写扩展
  • -python // 生成python扩展
  • -globals // 如果不加的话全局变量是在module.cvar对象下面定义的,如果加上的话那么全局变量直接出现在module下面。

4. instructions

  • %rename(x) y; // 能够将y重名为x
  • %template(x) y<z>; // 将y<z>这个模板实例化定义为类型x导出

5. about types

默认情况下面的话SWIG只是支持几个C/C++类型比如char,unsigned char,int,unsigned int等,而对于其他类型都认为是opaque pointer.我们经常遇到的问题就是 我们使用int64_t这样的类型,但是SWIG认为这个是一个pointer.当然我们可以使用typedef来进行类型定义,显示地告诉SWIG这个是一个基本类型,比如

typedef long long int64_t;

或者是显示地包含stdint.h这个头文件进来(会更加方便).如果我们返回的不是一个基本类型的话,那么底层SWIG包装的时候就会使用malloc来分配这个类型的大小, 然后返回python这个指针对象,通常这样来说就会造成内存泄露,所以我们最好还是不要返回非基本类型。这里有必要说明的就是C++引用和指针对于SWIG是相同的。


二进制参数. 传入二进制的话我们可以使用swig的typemap搞定

// 这样只要参数中含有const char* bin,unsigned int binlen这样形参的话
// 那么就认为这个部分接收的就是二进制数据
%apply (char *STRING, size_t LENGTH) { (const char* bin, unsigned int binlen) };

传出二进制的话我们可以使用std::string传出,然后再我们的swg描述文件里面加上

%include "std_string.i" // 使用std::string返回二进制,我们通常会使用

传递异常. 首先我们必须构造异常。构造异常非常简单就是编写一个类,但是需要注意的是必须在public下面定义构造和析构函数,不然在抛出异常时候会出现内存泄露问题。 然后使用异常的时候只需要在函数声明部分加上throw即可。

#ifdef SWIG
%rename(bsl_exception) bsl::Exception;
namespace bsl{
class Exception{
  public:
    Exception(){}
    ~Exception(){}
};
}
#endif // SWIG
void foo() throw (bsl::Excption);

6. python extension

python本身提供了非常简单的方式来编译扩展,使用内置的distutils模块即可搞定。我们编写setup.py内容如下:

from distutils.core import setup,Extension
import os
// 首先我们定义我们要编译的扩展对象(.so)
pycompack=Extension('_pycompack_', # _pycompack_.so
                    sources=['pycompack_wrap.cxx','pycompack.cc'], # 源文件
                    include_dirs=['..'],  # 头文件路径
                    libraries=['pthread'], # 库文件
                    library_dirs=['..']) # 库文件路径

// 然后定义我们编译和发布信息
setup(name='pycompack', # 模块名称
      version='1.0.0.0', # 版本
      description='compack python extension', # 简要描述
      long_description='Fuck,This is the Fucking compack python extenison.Still confusing???', # 长描述
      author='dirtysalt',
      maintainer='dirtysalt',
      maintainer_email='zhangyan04@baidu,com',
      author_email='zhangyan04@baidu.com',
      url='http://hi.baidu.com/dirlt',
      license='BSD 3-Clause',
      py_modules=['pycompack','pycompack_'], # 需要发布的python模块
      ext_modules=[pycompack])  # 需要发布的扩展模块

然后我们使用python setup.py build/install进行构建和安装了。