Go与C/C++的交互

 

CGO简介

golang与C有着千丝万缕的联系,go代码中可以调用C代码
由于项目需要,新使用的golang语言需要调用C语言写的加解密函数,所以用到了cgo,在此记录一点使用心得

如何调C代码,按C代码的结构分为以下几种

内嵌式

非常简单,只需要两步

  1. 在golang代码开始部分(package xxx之后),添加注释,注释中编写需要使用的C语言代码
  2. 紧挨着注释结束,另起一行增加import "C",注意跟注释中的C代码紧挨,不要有空行,且不要跟其他golang的import放在一起

这样在golang语言的正文中就可以用C.xxx的方式调用注释中的C代码了

参考:https://www.jianshu.com/p/45e4aa2e7c2e

本地文件式

这种情况,参考以下的描述,Go编译器也会编译C/C++文件。

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.

https://golang.org/cmd/cgo/

这时要注意,使用go build是要在当前目录进行Build,不要Build某个go文件。这样C的部分就不会参与Build的。会出现Link错误。

动态库形式

这里环境是Mac,所以对应的动态库格式是Dylib。

首先,通过Xcode生成对应的动态库。

https://blog.csdn.net/qq981378640/article/details/53908061

Go与C/C++的交互_第1张图片

第二步,把头文件和dylib放到Go的工程目录下

package main


/*
#cgo LDFLAGS: -ldl
#include 
#include 
#include "Test.h"
#include 
//#include "Test.c"
void Hello(char *str) {
   printf("%s\n", str);
   void* handle;
    typedef void (*FPTR)();

    handle = dlopen("./libTestLib.dylib", 1);
    FPTR fptr = (FPTR)dlsym(handle, "hello");

   fptr();
}
*/
import "C"
import (
	"fmt"
	"unsafe"
	//"./Test"
)

func main() {

	//msg := "this is a test for"
	fmt.Printf("done...")
	 s := "Hello Cgo"
	 cs := C.CString(s)
	 C.Hello(cs)
	 //C.hello()
	 C.free(unsafe.Pointer(cs))
	//Test.Test()
	//fmt.Println(C.test())
	fmt.Printf("done...")

}

与Windows类似,也是使用dlopen加载动态库,并关联定义对应的方法。实现调用。

还有很一种更加简单的方法

#cgo LDFLAGS: -lTestLib

其中,你对应的Lib是libTestLib.dylib,一定要注意,在Mac上所有的Lib前有前缀lib。但在使用时不要加上,加上反而会找不到。

使用命令otool -L ./libTest.dylib 可以查看依赖关系。

 

C++使用注意

以上几种方式,C++是不直接嵌入式,但可以当前本地的C文件中。C++的文件也可直接放到本地。通过C间接由Go进行调用。

这也是产生了SWIG的作用。。。

其核心还是把C++文件自动加入C的封装、Go的封装,最终于Go代码进行调用。

另外,如果是C++,对应的文件为.Cpp类型的。一定要以如下的声明方法,才能在CGO中调用。因为CGO只支持C,C调用C++函数实例(关键是让C调用的C++函数接口按照C规范,即使用extern "C"的方式进行声明。

#ifdef __cplusplus
extern "C" {
#endif

    void hello1();

#ifdef __cplusplus
}
#endif

C与C++的调用参考:

https://blog.csdn.net/this_is_me_anyway/article/details/79397018

 

SWIG调用C

先创建C文件

//
//  Test.c
//  TestLib
//
//  Created by benny  on 2018/11/2.
//  Copyright © 2018年 benny . All rights reserved.
//
#include 
#include "Test.h"

void hello()
{
    printf("hello");
}
//
//  Test.h
//  TestLib
//
//  Created by benny  on 2018/11/2.
//  Copyright © 2018年 benny . All rights reserved.
//

#ifndef Test_h
#define Test_h

#include 
void hello();

#endif /* Test_h */

针对C文件的头文件,定义Test.i描述文件

%module Test

%inline %{
    #include "Test.h"
%}

void hello();

把其都放一个GO工程目录下的包文件夹中

运行如下命令

 swig -go -cgo -intgosize 64 test.i

以上一定要注意module名字与包名要相同的,最后生成文件Test_Wrap.c和Test.go文件。当然其实现的原理与上面是一致的,其核心还是使用CGO进行调用C,只不过封装代码通过工具自动生成了。

最后的目录结构如下:

Go与C/C++的交互_第2张图片

最后在Main.go文件引入Test包,就可以调用Hello函数了

package main

import (
	"fmt"
	"swigtest2/Test"
)

func main()  {
	fmt.Print("test")
	Test.Hello()
}

最后Build时一定要对整个目录进行Build。不然会出现链接定义找不到的错误。

有关SWIG的内容,需要进一步的调查。后续跟进。

 

 

 

你可能感兴趣的:(Golang)