go通过swig封装、调用c++共享库的技术总结 (3)

根据具体需求,其他需要的辅助指令。包括%include指令、%template指令等。具体可以根据需要参考SWIG安装文件的Examples,路径位于SWIG_SOURCE_ROOT/Examples,也可以参考SWIGPlus doc。

接口文件写完后。需要运行SWIG命令,对于不同的目标语言,有不同的参数可选。我们这里以python和go为例说明一下。

swig -c++ -python compare_length.i swig -c++ -go -intgosize 64 -cgo compare_length.i

总结一下。第一,需要根据需要写一个.i接口文件,定义导出的接口;第二,根据目标语言运行swig命令,生成连接C/C++与目标语言的代码。

剩下的部分,就因目标语言而异了。对于Python,需要将swig生成的[module_name]_wrapper.cpp文件与原有的C/C++库或文件编译为一个.so,然后通过生成的[module_name].py在Python中使用。对于Go,需要将[module_name]_wrapper.cpp、[module_name].go,以及原有的C/C++库或文件放到GOPATH下的一个具体路径里然后通过go来build。

SWIG与Go

这里先说一下版本问题,我们用的是Go 1.8和SWIG 3.0。Go 1.4和Go 1.5的go tool有较大差别,很多方法中用到的6c、6g、8c、8g在1.5以后的版本都去掉了,但1.5以后支持cgo。SWIG 3.0支持-cgo参数。本文用的方法都是基于cgo的。

因为C++与Go语法上存在一些差别,不能完全对应上,因此在将C++的元素导出时会有一些修改。

所有的Go代码都必须在一个package内,SWIG生成的Go代码,也都位于一个package内,这个package的名称由SWIG接口文件中的%package指令设置;

由于在Go中只有大写字母开头的名称,才是在package外可用的,因此所有被导出的C++名称(变量、函数、类等),如果是小写字母开头,则会被转换为大写字母开头;

导出C++的变量,会被封装为Get和Set两个函数。例如一个C++变量名为var,SWIG会为其生成SetVar和GetVar两个方法;

导出C++变量如果是常亮,则只提供Get方法;

导出C++的宏,在Go中会变成一个常量;

导出C++的类,会被Go导出为两个类型。由于Go没有类的概念,因此会为其生成两个类型,一个类型用来持有C++对象的指针,一个与C++类同名的接口类型用来调用方法。所有的C++类的公共变量,都会在这个接口内生成Set和Get两个方法。另外,Go会生成一个NewClassName的函数来初始化对象。

其他细节我这里也没有涉及,有需要可以参考SWIG文档中SWIG and Go这一部分。

现在我们回过头来看一下SWIG所做的事情,就是利用一些trick把C++接口中与Go无法对应的部分,在尽量不影响语义的前提下对应起来。这也正是如果我们如果手工封装C++模块时所要做的。现在SWIG替我们做了这些累活。

用SWIG封装C++动态库示例 项目简介

本节以一个示例来说明,如何使用SWIG连接Go和C++代码。示例项目包括C++编译的动态库.so,C++源码,C++风格的函数接口,C++风格的数据类型。这些特性大部分是Cgo不能直接支持的,也是在实际项目中经常遇到的。

libl2.so,l2.h。一个动态库及其头文件,其中的函数int l2(const std::vector<int>& elements)用于计算一个向量的长度。我们没有该函数的实现代码。

compare_length.cxx,compare_length.h。一个cxx文件和其头文件,这个cxx文件中的函数int compare(const std::vector<int>& vl, const std::vector<int>& vr)调用了在l2.h中定义l2函数,比较两个向量的长度。这个函数是我们要导出给Go的函数。

我们没有l2函数的实现代码,只有动态库,对应于在实际工程中用到第三方动态库。我们有compare函数的源代码和头文件,对应于已有的C++实现的一些功能,这部分功能我们不想在Go中重复实现。

libl2.so位于$GOPATH/compare_length/路径下。

l2.h的内容如下,位于$GOPATH/compare_length/路径下:

#include <vector> int l2(const std::vector<int>& elements);

compare_length.h的内容如下,位于$GOPATH/compare_length/路径下:

#include<vector> #include "l2.h" int compare(const std::vector<int>& vl, const std::vector<int>& vr);

compare_length.cxx的源码如下,位于$GOPATH/compare_length/路径下:

#include <vector> #include "l2.h" #include "compare_length.h" int compare(const std::vector<int>& vl, const std::vector<int>& vr) { int l2_l = l2(vl); int l2_r = l2(vr); return l2_l - l2_r; } 编译步骤

首先,写一个SWIG接口文件compare_length.i,用于指定导出的函数和数据类型。

%module compare_length %{ #include "compare_length.h" %} %include "typemaps.i" %include "std_vector.i" %template(VecInt) std::vector<int>; int compare(const std::vector<int> vl, const std::vector<int> vr);

导出的模块通过%module指定为compare_length。导出一个函数compare和一个类型VecInt。

第二步,运行以下SWIG命令,生成compare_length_wrapper.cxx文件和compare_length.go文件。

> swig -c++ -go -intgosize 64 -cgo compare_length.i

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyzdff.html