cgo中go调c的三种方式


cgo中go调c的三种方式

1. 代码嵌入

  • 只支持c,c++的库不好搞
package main

/*
#include <stdio.h>

int SayHello()
{
    printf("success\n");
    return 0;
}
*/
import "C"
import "fmt"

func main() {
	ret := C.SayHello()
	fmt.Println(ret)
}

2. 直接链接库

  • 先正常编译,搞出库,不需要管__declspec(dllexport) ,但cpp实现中include头文件也要有extern "C"
g++ .\Hello.cpp -fPIC -shared -o .\Hello.dll
  • 在.go中链接
package main
/*
#cgo CFLAGS: -I${SRCDIR}/include
#cgo LDFLAGS: -L${SRCDIR}/lib -lHello
#include "Hello.h"
*/
import "C"
import "fmt"

func main() {
	ret := C.SayHello()
	fmt.Println(ret)
}
  • 这里有个大坑:LDFLAGS只是帮你链接,等到运行的时候会找不到dll动态库,所以还得自己加环境变量或者把库放在可执行文件相同目录

3. 一起编译

  • 如下,可以build再运行,没法直接go run(总是说undefined reference)
// cgotry.go

extern "C"
{
#include "Hello.h"
}

#include <iostream>

int SayHello()
{
    std::cout << "success." << std::endl;
    return 0;
}

-----------------------------------------------------
// Hello.h
    
#ifndef HELLO_H
#define HELLO_H

int SayHello();

#endif

------------------------------------------------------
// Hello.cpp
    
extern "C"	// 如果不按c编译,会链接不上(符号名不能匹配上)
{
#include "Hello.h"
}

#include <iostream>

int SayHello()
{
    std::cout << "success." << std::endl;
    return 0;
}

这三种方式的比较

  • 第一种灵活性和可读性都非常差,不推荐
  • 第二种看似万能且安全,加载的速度似乎会慢一点
  • 第三种介于两者之间,推荐
  • 注意对include头文件extern "C" 很关键,cgo支持的还是c

参考资料

  • https://blog.csdn.net/zdy0_2004/article/details/79124269
  • https://zhuanlan.zhihu.com/p/349197066

  • https://zhuanlan.zhihu.com/p/29128010