英文:
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 '_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)
The code:
// 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()
}
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
这里有两件事情:
go build
编译*.c
文件以及*.go
文件<sup>1</sup>#include <print.c>
等同于用print.c
的内容替换 include 语句
因此,你会将 print.c
的内容编译两次:一次是由 CC 编译 print.c
,另一次是由 CGo 编译 print.go
。因此,print.c
和 print.go
的目标文件都包含了 print.c
中定义的所有导出符号。所以你会得到两个 do_print
的副本。将 do_print
声明为静态函数可以解决这个问题,因为声明为 static
的函数不会被 CC 导出。
包含一个 .c
文件(例如 #include <file.c>
)通常是一个不好的做法。 如果你有一个合理的理由需要包含函数体,惯例是使用头文件(.h
文件)。这就是 C++ 模板库(如 Boost,如果我没记错的话)所做的。因为通常不会包含 C 文件,而通常会包含 H 文件,所以包含一个 C 文件会违反预期,很可能会导致混乱和问题。
<sup>1</sup> 如果一个包中的任何 Go 文件导入了 "C"
,Go 将编译该包中的 C 文件。也就是说,如果一个包中没有任何 Go 文件使用 CGo,Go 将忽略该包中的 C 文件。
英文:
There are two things going on here:
go build
compiles*.c
in addition to*.go
<sup>1</sup>#include <print.c>
is exactly equivalent to overwriting the include statement with the contents ofprint.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 <file.c>
) 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 "C"
. That is, if no Go files in a package use CGo, Go will ignore C files in that package.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论