在现有的C项目中使用Go代码

huangapple go评论70阅读模式
英文:

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 &lt;stdio.h&gt;
#include &quot;mygopkg.h&quot;

If you forget the #define _GNU_SOURCE, the gcc will complain:

warning: implicit declaration of function &#39;asprintf&#39;; did you mean &#39;vsprintf&#39;? [-Wimplicit-function-declaration]

If you forget the #include "mygopkg.h", the gcc will complain:

warning: implicit declaration of function &#39;PrintString&#39; [-Wimplicit-function-declaration]

The last but not less important. The build command line I recommend for production code is:

go build -ldflags &quot;-s -w&quot;  -buildmode c-archive -o mygopkg.a

It'll save you 53% size of final mygopkg.a.

huangapple
  • 本文由 发表于 2015年8月26日 07:04:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/32215509.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定