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

运行上述命令后,在$GOPATH/compare_length/路径下生成了连个文件compare_length_wrapper.cxx和compare_length.go文件。compare_length_wrapper.cxx文件将compare_length.i中指定的C++类型和接口导出为C风格的接口和类型。compare_length.go文件在Go环境引用C接口,并将其封装为Go风格的接口。通过vim查看compare_length.go的内容,可以看到import "C"。

extern _Bool _wrap_VecInt_isEmpty_compare_length_d0802815884ccdeb(uintptr_t arg1); extern void _wrap_VecInt_clear_compare_length_d0802815884ccdeb(uintptr_t arg1); extern void _wrap_VecInt_add_compare_length_d0802815884ccdeb(uintptr_t arg1, swig_intgo arg2); extern swig_intgo _wrap_VecInt_get_compare_length_d0802815884ccdeb(uintptr_t arg1, swig_intgo arg2); extern void _wrap_VecInt_set_compare_length_d0802815884ccdeb(uintptr_t arg1, swig_intgo arg2, swig_intgo arg3); extern void _wrap_delete_VecInt_compare_length_d0802815884ccdeb(uintptr_t arg1); extern swig_intgo _wrap_compare_compare_length_d0802815884ccdeb(uintptr_t arg1, uintptr_t arg2); #undef intgo */ import "C"

这里面导出的函数,都位于compare_length_wrapper.cxx文件,而且是SWIG自动生成的。

第三步,修改compare_length.go文件。添加对动态库的链接参数。在import "C"之前的注释部分添加这一句内容,#cgo LDFLAGS: -L${SRCDIR}/ -ll2。修改后的compare_length.go文件内容如下。

extern _Bool _wrap_VecInt_isEmpty_compare_length_d0802815884ccdeb(uintptr_t arg1); extern void _wrap_VecInt_clear_compare_length_d0802815884ccdeb(uintptr_t arg1); extern void _wrap_VecInt_add_compare_length_d0802815884ccdeb(uintptr_t arg1, swig_intgo arg2); extern swig_intgo _wrap_VecInt_get_compare_length_d0802815884ccdeb(uintptr_t arg1, swig_intgo arg2); extern void _wrap_VecInt_set_compare_length_d0802815884ccdeb(uintptr_t arg1, swig_intgo arg2, swig_intgo arg3); extern void _wrap_delete_VecInt_compare_length_d0802815884ccdeb(uintptr_t arg1); extern swig_intgo _wrap_compare_compare_length_d0802815884ccdeb(uintptr_t arg1, uintptr_t arg2); #undef intgo #cgo LDFLAGS: -L${SRCDIR}/ -ll2 */ import "C"

这是本文第一部分讲到的,添加Cgo指令。由于我们用到了动态库,需要指定链接时的参数。

第四步,在$GOPATH/compare_length/路径下运行go build。或者在任意位置运行go build compare_length。看到没有报错,就是build成功了。

测试使用

现在,$GOPATH/compare_length/中的compare_length模块已经和一般的Go模块一样,可以被其他Go代码调用。我们建立一个测试路径$GOPATH/compare_length_test/,在其中添加一个测试文件runme.go。

package main import ( "compare_length" "fmt" ) func main() { l1 := compare_length.NewVecInt(); l2 := compare_length.NewVecInt(); l1.Add(1); l1.Add(2); l1.Add(3); l2.Add(1); l2.Add(2); l2.Add(4); ret := compare_length.Compare(l1, l2); fmt.Println(ret); }

运行go build -o runme,可以看到生成了可执行文件runme。然后在本地运行./runme,遇到报错信息。

./runme: error while loading shared libraries: libl2.so: cannot open shared object file: No such file or directory

通过ldd runme看一下。可以看到libl2.so未找到。

libl2.so => not found

由于我们用到了动态库,因此要指定一下环境变量LD_LIBRARY_PATH。

export LD_LIBRARY_PATH=$GOPATH/src/compare_length/:$LD_LIBRARY_PATH ./rumme

可以看到返回了正确内容。

-1 实际问题

我的实际问题,是用Go调用一个已有的NLP模块,该模块是用C++写的。与上一节中的示例项目基本一致,只是链接的动态库更多,导出的函数及类型更多。

之前这个模块已经通过SWIG导出给Python,因此segment.i文件没有做任何修改。

%module segment %{ #include "segment.h" %} %include "typemaps.i" %include "std_string.i" %include "std_vector.i" %include "segment.h" %template(VecDouble) std::vector<double>; %template(VecInt) std::vector<int>; %template(CoreSegmentItemVec) std::vector<CoreSegmentItem>; int coreSegment(void* INOUT, const std::string& IN, std::vector<CoreSegmentItem>& OUT); std::string postag2string(int wtype); std::string t2sgchar(const std::string& IN, bool ifcase = true); std::string sbc2dbc(const std::string& IN, bool ifcase = true);

运行SWIG命令,生成segment_wrapper.cxx和segment.go两个文件。

swig -c++ -go -gointsize 64 -cgo segment.i

修改segment.go文件,添加链接参数,在import "C"之前的注释里添加。

#cgo LDFLAGS: -L${SRCDIR}/lib -lssplatform -lencoding -lCoreSegmentor

然后尝试go build。没有提示错误就是build成功。然后在$GOPATH下的另一个目录写一段测试代码。

package main import ( "github.com/terencezhou/segment" "github.com/axgle/mahonia" "fmt" ) func main(){ test_str := "中华人民共和国国家主席于今年10月对美国进行了访问。"; encoder_gbk := mahonia.NewEncoder("gbk") decoder_gbk := mahonia.NewDecoder("gbk") gbk_test_str := encoder_gbk.ConvertString(test_str) segment.Init(); handler := segment.CreateCoreHandle(); seg_res := segment.NewCoreSegmentItemVec(); ret := segment.CoreSegment(handler, gbk_test_str, seg_res); fmt.Println(test_str); fmt.Printf("Segment status : %d\n", ret); for idx:=0; int64(idx) < seg_res.Size(); idx++{ coreItem := seg_res.Get(idx); fmt.Println(decoder_gbk.ConvertString(coreItem.GetTxt())); } }

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

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