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

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

@(知识记录)

简介

最近在研究golang,希望能对目前既有的python服务做一些优化,这些服务目前已经占用了6-7台机器。选择golang的原因,是看上其在并发方面更简单的支持,比c++更高的开发效率,以及比python更高的运行效率。

由于现实的原因,我们不太可能将所有模块都用golang重写一遍,有一些公司通用的模块是用C++编译成为.so的方式提供的。因此,如果想要用golang重构服务,调用C++共享库是不可能绕过的问题,也是首要解决的问题。

本文是对golang调用、封装c++共享库的技术总结,共分为四部分。第一部分介绍golang调用c语言接口的基本方法并介绍cgo;第二部分介绍swig的用法;第三部分是一个示例工程,完整模拟现实环境的调用和封装;第四部分对实际问题中的一个.so模块进行封装。

go调用c及cgo简介

最初遇到本文问题(go封装c++共享库)时,我在网上搜索到最多的文章,就是go如何调用c代码中的函数。当时的感觉是有点失望,因为都没能一步一步手把手完整解决我的问题。但是现在看来,本节的主题(go调用c代码)是后面所有工具的基础。

示例代码

首先,放上一段golang示例代码,这段代码来自cgo官方文档。

package main // #include <stdio.h> // #include <stdlib.h> // // static void myprint(char* s) { // printf("%s\n", s); // } import "C" import "unsafe" func main() { cs := C.CString("Hello from stdio") C.myprint(cs) C.free(unsafe.Pointer(cs)) }

首先,这是一段golang代码,从package定义、到import包、到函数定义,都是我们熟悉的golang元素。main函数内部是一个变量初始化和两个函数调用,且变量和函数的定义都来自名为C的package。在目录下运行go build -o test命令,可以得到一个可执行文件test,再运行./test命令,可以看到如下输出。

>./test Hello from stdio 代码解析

这段代码的关键部分在于import "C"及其之前的注释部分。

"C"在这里是一个pseudo-package,并不是一个实际存在的go package。对C语言部分的所有引用,都通过这个pseudo-package来进行。在import "C"之前的注释,可以是任何合法的c语言代码,go代码可以引用这些C语言定义的函数、变量等,仿佛它们就是定义在名为C的package中。可以是定义,也可以通过extern声明其他C文件中的定义。具体到上面的代码,在注释部分定义了一个C函数myprint,然后在go的main函数中调用了它。

cgo指示

另外,在import "C"之前的注释,还可以包括cgo指示( #cgo directives),这个特性在上面的简单示例代码中没有涉及。如下代码所示:

// #cgo CFLAGS: -DPNG_DEBUG=1 // #cgo amd64 386 CFLAGS: -DX86=1 // #cgo LDFLAGS: -L${SRCDIR}/libs -lfoo // #include <png.h> import "C"

cgo指示(go directives)以#cgo开头,并紧接着一个空格。这部分内容不是C代码,但是用来控制C编译器及link的参数,可以包括CFLAGS, CPPFLAGS, CXXFLAGS, FFLAGS和LDFLAGS 。

一个package中所有CPPFLAGS和CFLAGS cgo指示,都被连在一起,在编译C文件时使用;

一个package中所有CPPFLAGS和CXXFLAGS cgo指示,都被连在一起,在编译C++文件时使用;

一个package中所有CPPFLAGS和FFLAGS cgo指示,都被连在一起,在编译Fortran文件时使用;

一个package中所有LDFLAGS cgo指示,都被连在一起,在链接时使用。

这一切怎么发生的

摘抄自cgo文档。

When the Go tool sees that one or more Go files use the special import "C", it will look for other non-Go files in the directory and compile them as part of the Go package. Any .c, .s, or .S files will be compiled with the C compiler. Any .cc, .cpp, or .cxx files will be compiled with the C++ compiler. Any .f, .F, .for or .f90 files will be compiled with the fortran compiler. Any .h, .hh, .hpp, or .hxx files will not be compiled separately, but, if these header files are changed, the package (including its non-Go source files) will be recompiled. Note that changes to files in other directories do not cause the package to be recompiled, so all non-Go source code for the package should be stored in the package directory, not in subdirectories. The default C and C++ compilers may be changed by the CC and CXX environment variables, respectively; those environment variables may include command line options.

当go tool发现一个或多个文件里包含import "C"时,它会寻找目录内其他非go源码的文件,并且将它们编译为package的一部分。对于.c,.s及.S结尾的文件,会用c编译器编译;对于.cc,.cpp和.cxx文件,会用c++编译器编译;对于.f,.F,.for和.f90文件,会用fortran编译器编译。对于.h,.hh,.hpp和.hxx文件,虽然不会编译,但如果这些头文件发生了变化,package整个会被重新编译。其他目录的文件如果发生变化,不会引起package重新编译。因此,所有所有非go文件都应该放在package的目录下,不要放在任何子目录里。默认的c和c++班一起,会被CC和CXX环境变量影响,这些环境变量可以在命令行参数中包括。

其他注意事项 指针

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

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