运行上述命令后,在$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())); } }