先说 Mac os 下面吧
一路上顺风顺水。
总结了一下,CGO所使用的原理,在Go里面调用C的程序
其实就是使用了C++的一个特性,extern
// 网上是这样解释的extern可置于变量或者函数前,以表示变量或者函数的定义在别的文件中提示编译器遇到此变量和函数时在其他模块中寻找其定义。
当你对一个函数 extern 声明后,这个函数你就可以使用了,但是其实它并不存在,需要Go去生成
// Go 生成有个前地,就是在这个函数上面加一行(//export cgo_connect),cgo_connect 就是函数名
但是当使用的时候,其实Go已经生成好了,编译到程序里面,所以运行的就是所以 Cgo 的程序
// 可以通过一个命令来看 Go 中间做了什么go tool cgo xxx.go// 这 xxx.go 里面要写的就是下面这些C与Go的混合代码/*// 这里看到 extern 了把,使用这个的函数,只有三个地方存在// 一,注释内// 二,Go 内用 //export drv_cgo_connect 实现的// 三,在 C 里面实现的(可能是文件,也可能是库)extern void drv_cgo_connect(int (*)(void *, int));static void init_callback(){extern int cgo_connect(void *, int);drv_cgo_connect(&cgo_connect);}*/// #include <stdio.h>// #include <stdlib.h>// #cgo LDFLAGS: -L./ -lexamples // 库内有一切,不仅调用,还可以互访import "C"import "unsafe"func start() {C.init_callback()}// 这里就是生成了一个 C 的 cgo_connect 函数// 想要在 C 中使用这个函数,就需要在 C 里面 extern int cgo_connect(...) 才可以.//export cgo_connectfunc cgo_connect(_content unsafe.Pointer, _size C.int) C.int {device := string(C.GoBytes(_content, _size))log.Println("Go->", device)if err := StaticConn.Connect(device); err != nil {return C.int(0)}return C.int(1)}
这样一来,你就可以在 Go 里面调用 C 里面实现的函数
同样原理,如果 C 想要调用 Go 的函数,相同方法,只是把 extern 调换一下!
上面简单的了解了一下,CGO 原理,现在说一下具体情况,比如 QT 这样庞大的程序
对于 QT 这样的大程序,而且涉及到 QMake 编译,所以使用 CGO 本身混编(.go,.c.,.h)就没戏了
所以怎么办呢,这位同学说对了,就是这样,使用库(windows是dll)(linux是so)(mac是dylib)
将 QT 的 .Pro 文件改一下 TEMPLATE = lib 这样就会生成库文件
但是上面的同学问了,库文件能被Go调用,那库文件怎么调用Go呢
问题就来了,我试验了无数次。坑,坑,坑!
你想到了,extern 没错,但是 extern 的函数需要在本库中找到实现的函数
对于Cgo里面,你没有实现是因为你设置了 //export 所以 Go 就帮你做了
所以怎么办,对了,你想到,go tool cgo 把 C 编译出来,导入到 QT 中不就了。
错,大错特错,编译出来的文件,不完整,可远观,不可近玩嫣!
我试的时候虽然能编译过,但是 Go 转 C 的函数根本没运行
这样一来,楼上的同学哭了,那怎么办啊。
当当当当~!在这里用到了 C++ 的另外一个关键字 inline 隆重出马
// 网上这样介绍的inline关键字用来定义一个类的内联函数,引入它的主要原因是用它替代C中表达式形式的宏定义。
也就是说,你在C库中用inline定义一个空函数,然后使用TA,然后在Go里面用//export 定义一个同名函数。
这样一来,C里面就使用的不是inline定义的函数,而是你Go里面的函数了,天哪,太简单,太方便了。

再说 Windows 这个坑吧
当你 Mac 或者 Linux 按照上面方法,写完了,你可能兴高采烈的跑去 windows 编译
当你 费了 九牛二虎之力,把环境编译好了,之后呢,编译呗,坑坑坑,恭喜,你又掉坑里了。
这个问题,我找啊找,查啊查,费了就牛二老之力,找到了问题了。
问题就是 inline // 网上是这样说的inline说明对编译器来说只是一种建议,编译器可以选择忽略这个建议。比如,你将一个长达1000多行的函数指定为inline,编译器就会忽略这个inline,将这个函数还原成普通函数。
天哪,看编译器心情吗,在 Mac 里面就用 Go 里面 原始函数,在 Windows 下面就选择了,inline 定义的空函数,这可怎么办啊。
网上一顿乱找,什么,强制 inline ,编译器不优化,一顿乱找,搞不定啊。
后来听说,windows 对 dll 有特殊限制,有些 关键字无法传递,比如 inline ,所以,唉!!!,没办法.
既然不用 inline 也就不能用 extern 因为 只有加了 extern inline 的函数才变成实际存在的,如果去掉 inline
编译器会一直提示你,没有找到,没有找到,没有找到。啊啊啊啊啊啊啊!
找啊找,查啊查,坑啊坑。看了一些别人的代码,涉及到的太少,几乎没有
翻了一下 liteide 的代码,哈哈,看到曙光了,怎么办呢
是怎么处理的呢,比较复杂,但是可行
就是在 Cgo 里面自己调用自己的函数,然后 CGo 的方法,把函数指针传到库里去,在库里面搞个全局保存一下.
/*extern void drv_cgo_connect(int (*)(void *, int));static void init_callback(){extern int cgo_connect(void *, int);drv_cgo_connect(&cgo_connect);}*/// #include <stdio.h>// #include <stdlib.h>// #cgo LDFLAGS: -L./ -lexamplesimport "C"import "unsafe"func start() {C.init_callback()}//export cgo_connectfunc cgo_connect(_content unsafe.Pointer, _size C.int) C.int {device := string(C.GoBytes(_content, _size))log.Println("Go->", device)if err := StaticConn.Connect(device); err != nil {return C.int(0)}return C.int(1)}// 在 C 里面是这样typedef int (*COMMAND_CGO_CONNECT_FUNCTION)(void *, int);typedef int (*COMMAND_CGO_CHECKCONN_FUNCTION)();COMMAND_CGO_CONNECT_FUNCTION cgo_connect;COMMAND_CGO_CHECKCONN_FUNCTION cgo_checkconn;extern "C" void drv_cgo_connect(int (*_a)(void *, int)){cgo_connect = _a;}extern "C" void drv_cgo_checkconn(int (*_a)()){cgo_checkconn = _a;}
这样一来,问题解决了,但是复杂了一些,对 Cgo 减10分.
补充,当 windows 运行的时候 会显示 DOS 窗口,怎么办呢
go build -ldflags -H=windowsguigo build -ldflags -H=windowsgui XXX.go
再次补充:
// 可以把函数当指针传,这样就不需要那么多的 drv_cgo_xxx/*extern void cgo_init();extern int cgo_start();extern void cgo_callback(void *);extern void drv_cgo_callback(int, void*);extern void drv_cgo_callback_2(int, void*);static void init_callback(){int _cgo_connect = 1;int _cgo_checkconn = 2;int _cgo_disconn = 3;int _cgo_command = 4;int _cgo_shortcuts = 5;int _cgo_message = 6;extern int cgo_connect(void *, int);extern int cgo_checkconn();extern void cgo_disconn();extern void cgo_command(void *, int);extern void cgo_shortcuts(void *, int);extern void * cgo_message();drv_cgo_callback_2(_cgo_connect, &cgo_connect);drv_cgo_callback_2(_cgo_checkconn, &cgo_checkconn);drv_cgo_callback(_cgo_checkconn, &cgo_checkconn);drv_cgo_callback(_cgo_disconn, &cgo_disconn);drv_cgo_callback(_cgo_command, &cgo_command);drv_cgo_callback(_cgo_shortcuts, &cgo_shortcuts);drv_cgo_callback(_cgo_message, &cgo_message);}*/typedef int (*COMMAND_CGO_CONNECT_FUNCTION)(void *, int);typedef int (*COMMAND_CGO_CHECKCONN_FUNCTION)();static COMMAND_CGO_CONNECT_FUNCTION cgo_connect = 0;static COMMAND_CGO_CHECKCONN_FUNCTION cgo_checkconn = 0;extern "C" void drv_cgo_callback_2(int _a, void * _b){/*int _cgo_connect = 1;int _cgo_checkconn = 2;*/switch (_a) {case 1:cgo_connect = (COMMAND_CGO_CONNECT_FUNCTION)_b;break;case 2:cgo_checkconn = (COMMAND_CGO_CHECKCONN_FUNCTION)_b;break;}}
