用c编写动态库如下:
#include <stdio.h>
#include <unistd.h>
// 将函数名作为指针的格式为:int (*ptr)(char *str) 即:返回值(指针名)(参数列表)
typedef int (*fnc)(int); // 回调函数的名称为 fnc,参数是int
typedef int (*fnc2)(char*); // 回调函数的名称为 fnc2,参数是 char *str
void printHello()
{
printf("print hello\n");
}
void callBack(fnc notifyMain)
{
int r= notifyMain(4);
sleep(5);
printf("main callback %d\n",r);
}
void callBack2(fnc2 notifyMain)
{
char* msg="msg from c";
int r= notifyMain(msg);
printf("main callback %d %s\n",r,msg);
}
动态库提供3个方法,printHello是go调用c用的普通方法,callBack和callBack2是传入回调的方法,int和char*是c编写的so用来通知外部调用进程的主要的消息类型。
编译c文件为so,libprinthello.so
gcc PrintHello.c -fPIC -shared -o libprinthello.so
编写c的so的接口文件:Interface.h
// 将函数名作为指针的格式为:int (*ptr)(char *str) 即:返回值(指针名)(参数列表)
typedef int (*fnc)(int); // 回调函数的名称为 fnc,参数是int
typedef int (*fnc2)(char*); // 回调函数的名称为 fnc2,参数是 char *str
void printHello();
void callBack(fnc f);
void callBack2(fnc2 f);
以上步骤完成了一个标准so的编写。
下面编写go的部分的代码
编写go和c的桥接函数如下:cfuns.go
package main
/*
#include <stdio.h>
// The gateway function
int addNum_cgo(int in)
{
int addNum(int);
return addNum(in);
}
int showMsg_cgo(char* s )
{
int showMsg(char*);
return showMsg(s);
}
*/
import "C"
定义了两个回调函数,用来满足callback和callback2的出参和入参的要求。
编写go的主函数,来调用libprinthello.so,完成测试,文件名是Main.go
package main
/*
#cgo CFLAGS: -I./
#cgo LDFLAGS: -L./ -lprinthello
#include "Interface.h"
#include <stdlib.h>
int addNum_cgo(int in); // Forward declaration.
int showMsg_cgo(char* s);
*/
import "C"
import (
"fmt"
"unsafe"
)
//export addNum
func addNum(in int) int {
return in + 1
}
//export showMsg
func showMsg(msg *C.char) int {
fmt.Println("show msg in go ", C.GoString(msg))
//defer C.free(unsafe.Pointer(msg)) // will destruct in c
return 1
}
func main() {
C.printHello()
C.callBack((C.fnc)(unsafe.Pointer(C.addNum_cgo)))
C.callBack2((C.fnc2)(unsafe.Pointer(C.showMsg_cgo)))
fmt.Println("end in go")
}
其中addNum和showMsg是实际的回调,注意其中的所有注释,及注释的位置都有意义,不能变化。
在主函数中,printHello是普通的函数调用,callBack和callBack2完成了回调函数的注册。
如果C中调用的callBack是个耗时的函数sleep(5),可以用go的协程来解决问题。
go C.callBack((C.fnc)(unsafe.Pointer(C.addNum_cgo)))
这样就不会阻塞主线程,在执行完c中的callback函数后,也可以直接通知到go的回调函数中了。
附:让go主进程不退出的方法:
b := make([]byte, 1)
os.Stdin.Read(b)
优化后的go的main函数如下:
func main() {
C.printHello()
go C.callBack((C.fnc)(unsafe.Pointer(C.addNum_cgo)))
C.callBack2((C.fnc2)(unsafe.Pointer(C.showMsg_cgo)))
fmt.Println("end in go")
b := make([]byte, 1)
os.Stdin.Read(b)
}
go的文件进行编译:
go build Main.go cfuns.go
将libprinthello.so和Main放到同一目录下,运行./Main,测试结果。