go传函数指针给c完成回调通知

用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,测试结果。

猜你喜欢

转载自blog.csdn.net/dong_beijing/article/details/79721222
今日推荐