简单的CGO示例在编译时出现“重复符号”错误。

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

Simple CGO example fails compilation with "duplicate symbol" error

问题

我想给出一个简单的示例,展示如何使用CGO从Go调用C代码。但由于某种原因,我无法实现预期的效果。编译失败,出现以下错误:

 go build main.go 
# awesomeProject1/print
duplicate symbol '_do_print' in:
    $WORK/b002/_x002.o
    $WORK/b002/_x003.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

以下是代码:

// print/print.c
#include <stdio.h>

void do_print(char * x){
    printf("%s", x);
}
// print/print.go
package print

// #include <print.c>
import "C"

func DoPrint() {
    C.do_print(C.CString("Hello!"))
}

// main.go
package main

import "awesomeProject1/print"

func main() {
    print.DoPrint()
}

如果我将do_print函数声明为静态的,那么它可以编译通过,但是对于我以后要集成的第三方代码,我无法这样做。我是否遗漏了文档中的一些重要部分?教程都很相似,声称可以正常工作,但我的示例却失败了。请帮忙解决!Go版本为1.16.4。

英文:

I want to make a simple example of calling C code from Go with CGO. But for some reason I can't achieve desired. Compilation fails with the following error:

 go build main.go 
# awesomeProject1/print
duplicate symbol &#39;_do_print&#39; in:
    $WORK/b002/_x002.o
    $WORK/b002/_x003.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

The code:

// print/print.c
#include &lt;stdio.h&gt;

void do_print(char * x){
printf(&quot;%s&quot;, x);
}
// print/print.go
package print

// #include &lt;print.c&gt;
import &quot;C&quot;

func DoPrint() {
	C.do_print(C.CString(&quot;Hello!&quot;))
}

// main.go
package main

import &quot;awesomeProject1/print&quot;

func main() {
	print.DoPrint()
}

If I make do_print function static it compiles but I wouldn't be able to do that for 3rd party code I want to integrate with later.
Am I missing some essential piece from documentation? Tutorials are all alike and claim to work where my example fails. Please help!
Go version 1.16.4

答案1

得分: 2

这里有两件事情:

  1. go build 编译 *.c 文件以及 *.go 文件<sup>1</sup>
  2. #include &lt;print.c&gt; 等同于用 print.c 的内容替换 include 语句

因此,你会将 print.c 的内容编译两次:一次是由 CC 编译 print.c,另一次是由 CGo 编译 print.go。因此,print.cprint.go 的目标文件都包含了 print.c 中定义的所有导出符号。所以你会得到两个 do_print 的副本。将 do_print 声明为静态函数可以解决这个问题,因为声明为 static 的函数不会被 CC 导出。

包含一个 .c 文件(例如 #include &lt;file.c&gt;)通常是一个不好的做法。 如果你有一个合理的理由需要包含函数体,惯例是使用头文件(.h 文件)。这就是 C++ 模板库(如 Boost,如果我没记错的话)所做的。因为通常不会包含 C 文件,而通常会包含 H 文件,所以包含一个 C 文件会违反预期,很可能会导致混乱和问题。


<sup>1</sup> 如果一个包中的任何 Go 文件导入了 &quot;C&quot;,Go 将编译该包中的 C 文件。也就是说,如果一个包中没有任何 Go 文件使用 CGo,Go 将忽略该包中的 C 文件。

英文:

There are two things going on here:

  1. go build compiles *.c in addition to *.go<sup>1</sup>
  2. #include &lt;print.c&gt; is exactly equivalent to overwriting the include statement with the contents of print.c

As a result, you are compiling the contents of print.c twice: once when print.c is compiled by CC, and once when print.go is compiled by CGo. Thus, the object files for print.c and print.go each contain all of the exported symbols defined in print.c. So you get two copies of do_print. Making do_print static works because a function declared as static will not be exported by CC.

Including a .c file (e.g. #include &lt;file.c&gt;) is pretty much always a bad idea. If you have a legitimate reason to #include function bodies, the convention is to use a header (.h) file. This is what C++ templating libraries do (like Boost, IIRC). Because C files normally are not included, and H files normally are included, including a C file subverts expectations and thus is likely to cause confusion and problems.


<sup>1</sup> IIRC, Go will compile C files in a package if any of the Go files in the package import &quot;C&quot;. That is, if no Go files in a package use CGo, Go will ignore C files in that package.

huangapple
  • 本文由 发表于 2021年6月30日 06:03:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/68186187.html
匿名

发表评论

匿名网友

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

确定