英文:
Using Go code in an existing C project
问题
自从Go 1.5发布以来,我开始重新审视如何将其集成到我的一个现有项目中。
该项目的代码库完全使用C语言编写,用于低级别访问硬件和其他有趣的东西。然而,一些较高级别的功能很繁琐,我想用一种更高级别的语言(Go)来编写它们。
有没有办法可以从C程序中调用Go代码?我安装了Go 1.5,它添加了-buildmode=c-archive
选项(https://golang.org/s/execmodes),我正在尝试让它工作。
然而,我似乎无法让Go生成适当的头文件,以便让我的项目能够编译。当我生成存档文件时,我可以在导出的符号中看到该函数(使用objdump),但由于没有包含头文件,gcc会报告该函数不存在(如预期所示)。
我对Go还很陌生,但我喜欢这门语言,想要好好利用它。有没有什么惯用的方式(在Go的世界中经常使用“惯用”这个词...)可以让它们很好地协同工作?
我之所以提出这个问题,并特别提到Go 1.5,是因为根据这份文档(https://docs.google.com/document/d/1nr-TQHw_er6GOQRsF6T43GGhFDelrAP0NqSS_00RgZQ/edit?pli=1#heading=h.1gw5ytjfcoke),Go 1.5增加了对非Go程序调用Go代码的支持。具体地,在“将Go代码链接到非Go程序并从中调用”一节中有提到。
英文:
Ever since Go 1.5 came out, I started taking another look at how I could integrate it into an existing project of mine.
The project's codebase is written entirely in C for low level access to to hardware and other fun stuff. However, some of the higher level things are tedious, and I would like to start writing them in a higher level language (Go)
Is there any way I can call Go code from a C program? I installed Go 1.5, which added -buildmode=c-archive
(https://golang.org/s/execmodes) which I am trying to get working.
However, I can't seem to get Go to generate the appropriate header files to allow my project to actually compile. When I generate the archive, I see the function in the exported symbols (using objdump), but without the header files to include gcc complains about the function not existing (as expected)
I'm quite new to Go - however, I love the language and would like to make use of it. Is there any idiomatic way ("idiomatic" gets used a lot in the world of Go I see...) to get this to play nicely with each other?
The reason I asked this question and specifically mentioned Go 1.5 is that according to this document, https://docs.google.com/document/d/1nr-TQHw_er6GOQRsF6T43GGhFDelrAP0NqSS_00RgZQ/edit?pli=1#heading=h.1gw5ytjfcoke
Go 1.5 added support for non-Go programs to call Go code. Specifically, mentioned under the section "Go code linked into, and called from, a non-Go program"
答案1
得分: 70
构建一个可以从C语言调用的存档文件,你需要将它们标记为导出的CGo符号。
例如,如果我创建一个名为foo.go
的文件,内容如下:
package main
import (
"C"
"fmt"
)
//export PrintInt
func PrintInt(x int) {
fmt.Println(x)
}
func main() {}
需要注意的重要事项有:
- 包名必须为
main
。 - 需要有一个
main
函数,尽管它可以是空的。 - 需要导入
C
包。 - 需要使用特殊的
//export
注释来标记你想从C语言调用的函数。
可以使用以下命令将其编译为可从C语言调用的静态库:
go build -buildmode=c-archive foo.go
结果将会生成一个名为foo.a
的存档文件和一个名为foo.h
的头文件。在头文件中,我们可以看到以下内容(省略了不相关的部分):
...
typedef long long GoInt64;
...
typedef GoInt64 GoInt;
...
extern void PrintInt(GoInt p0);
...
这就足够调用导出的函数了。我们可以编写一个简单的C程序来调用它,如下所示:
#include "foo.h"
int main(int argc, char **argv) {
PrintInt(42);
return 0;
}
可以使用以下命令编译它:
gcc -pthread foo.c foo.a -o foo
需要使用-pthread
选项,因为Go运行时使用了线程。当我运行生成的可执行文件时,它会打印出42
。
英文:
To build an archive callable from C, you will need to mark them as exported CGo symbols.
For example, if I create a file foo.go
with the following contents:
package main
import (
"C"
"fmt"
)
//export PrintInt
func PrintInt(x int) {
fmt.Println(x)
}
func main() {}
The important things to note are:
- The package needs to be called
main
- You need to have a
main
function, although it can be empty. - You need to import the package
C
- You need special
//export
comments to mark the functions you want callable from C.
I can compile it as a C callable static library with the following command:
go build -buildmode=c-archive foo.go
The results will be an archive foo.a
and a header foo.h
. In the header, we get the following (eliding irrelevant parts):
...
typedef long long GoInt64;
...
typedef GoInt64 GoInt;
...
extern void PrintInt(GoInt p0);
...
So that's enough to call the exported function. We can write a simple C program that calls it like so:
#include "foo.h"
int main(int argc, char **argv) {
PrintInt(42);
return 0;
}
We can compile it with a command like:
gcc -pthread foo.c foo.a -o foo
The -pthread
option is needed because the Go runtime makes use of threads. When I run the resulting executable it prints 42
.
答案2
得分: 0
上面的代码工作得很好,但是gcc会对函数和头文件进行投诉。
包含的内容应该是:
#define _GNU_SOURCE
#include <stdio.h>
#include "mygopkg.h"
如果你忘记了#define _GNU_SOURCE
,gcc会报错:
warning: implicit declaration of function 'asprintf'; did you mean 'vsprintf'? [-Wimplicit-function-declaration]
如果你忘记了#include "mygopkg.h"
,gcc会报错:
warning: implicit declaration of function 'PrintString' [-Wimplicit-function-declaration]
最后但同样重要的是,我建议用于生产代码的构建命令行是:
go build -ldflags "-s -w" -buildmode c-archive -o mygopkg.a
这样做会节省最终mygopkg.a
文件大小的53%。
英文:
The code above work just fine, but gcc will complain about functions and headers.
The includes should be:
#define _GNU_SOURCE
#include <stdio.h>
#include "mygopkg.h"
If you forget the #define _GNU_SOURCE, the gcc will complain:
warning: implicit declaration of function 'asprintf'; did you mean 'vsprintf'? [-Wimplicit-function-declaration]
If you forget the #include "mygopkg.h", the gcc will complain:
warning: implicit declaration of function 'PrintString' [-Wimplicit-function-declaration]
The last but not less important. The build command line I recommend for production code is:
go build -ldflags "-s -w" -buildmode c-archive -o mygopkg.a
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论