英文:
How to reuse a Go callback in several packages from C?
问题
有没有一种方法可以构建一个Go + C应用程序,实现以下功能:
- 从主包X中导入包Y和Z。
- 包M导出一个Go回调函数F。
- 包X和Y都使用了相应的C文件进行构建,都想从C源代码中调用F。
一般来说,我正在尝试找出如何从其他模块的附带C文件中调用回调函数,这些模块用于构建最终应用程序。我无法找出如何实现这一点或类似的功能。我也对复杂的解决方案感兴趣。
英文:
Is there a way to build a Go + C application that:
- From main package X, import packages Y and Z.
- Package M exports a go callback F.
- Packages X and Y are both built with accompanying C files, both want
to call F from C source code.
Generally speaking I'm trying to figure out how to call a callback from accompanying C files in other modules which are used to build a final application. I coudn't figure out how to achieve this or something similar. I'm also interested in convoluted solutions.
答案1
得分: 3
我看不到一种跨包调用Go函数的方法,但是所有的cgo包都会链接到同一个二进制文件中,并且可以相互调用。这意味着你可以将M.F导出为M包中的一个C函数,并从Y和Z包中调用该C函数。
m/m.go:
package m
// void F();
import "C"
import "fmt"
//export F
func F() {
fmt.Println("m.f")
}
m/m.h:
void m_f();
m/m.c:
#include <stdio.h>
#include "_cgo_export.h"
#include "m.h"
void m_f() {
printf("m_f\n");
F();
}
y/y.go:
package y
// The LDFLAGS lines below are needed to prevent linker errors
// since not all packages are present while building intermediate
// packages. The darwin build tag is used as a proxy for clang
// versus gcc because there doesn't seem to be a better way
// to detect this.
// #cgo darwin LDFLAGS: -Wl,-undefined -Wl,dynamic_lookup
// #cgo !darwin LDFLAGS: -Wl,-unresolved-symbols=ignore-all
// #include "y.h"
import "C"
import (
"fmt"
_ "m"
)
func Y() {
fmt.Println("y.Y")
C.y()
}
y/y.h:
void y();
y/y.c:
#include <stdio.h>
#include "../m/m.h"
void y() {
printf("y.C.y\n");
m_f();
}
英文:
I don't see a way to call a Go function across packages, but all cgo packages are linked into the same binary and can call each other. This means that you can export M.F to a C function in package M and call that C function from packages Y and Z.
m/m.go:
package m
// void F();
import "C"
import "fmt"
//export F
func F() {
fmt.Println("m.f")
}
m/m.h:
void m_f();
m/m.c:
#include <stdio.h>
#include "_cgo_export.h"
#include "m.h"
void m_f() {
printf("m_f\n")
F();
}
y/y.go:
package y
// The LDFLAGS lines below are needed to prevent linker errors
// since not all packages are present while building intermediate
// packages. The darwin build tag is used as a proxy for clang
// versus gcc because there doesn't seem to be a better way
// to detect this.
// #cgo darwin LDFLAGS: -Wl,-undefined -Wl,dynamic_lookup
// #cgo !darwin LDFLAGS: -Wl,-unresolved-symbols=ignore-all
// #include "y.h"
import "C"
import (
"fmt"
_ "m"
)
func Y() {
fmt.Println("y.Y")
C.y()
}
y/y.h:
void y();
y/y.c:
#include <stdio.h>
#include "../m/m.h"
void y() {
printf("y.C.y\n");
m_f();
}
答案2
得分: 0
这是一个示例,它将接受任何Go回调(不是线程安全的)。
b.go:
package b
// typedef void (*cbFunc) ();
// void do_run(cbFunc);
// void goCallback();
import "C"
//export goCallback
func goCallback() {
if goCallbackHolder != nil {
goCallbackHolder()
}
}
var goCallbackHolder func()
func Run(callback func()) {
goCallbackHolder = callback
C.do_run(C.cbFunc(C.goCallback))
}
b.c:
#include "_cgo_export.h"
void do_run(void (*callback)())
{
callback();
}
英文:
Here is an example, that will accept any go callback (not thread-safe).
b.go:
package b
// typedef void (*cbFunc) ();
// void do_run(cbFunc);
// void goCallback();
import "C"
//export goCallback
func goCallback() {
if goCallbackHolder != nil {
goCallbackHolder()
}
}
var goCallbackHolder func()
func Run(callback func()) {
goCallbackHolder = callback
C.do_run(C.cbFunc(C.goCallback))
}
b.c:
#include "_cgo_export.h"
void do_run(void (*callback)())
{
callback();
}
答案3
得分: 0
我无法以简单的方式完成工作,这是我个人的看法。
假设有一个主包X
,导入了Y
和Z
,它们都需要调用(来自C源代码的)在包M
中声明的F
函数。
我必须执行以下步骤:
- 在
Y
中创建一个小的包装器W1
,用于F
函数,并将其导出以供Y
的C源代码调用。 - 在
Z
中创建一个小的包装器W2
,用于F
函数,并将其导出以供Z
的C源代码调用。 - 在
Y
的CGO CPPFLAGS中定义-DCALLBACK=W1
。 - 在
Z
的CGO CPPFLAGS中定义-DCALLBACK=W2
。 - 现在,无论在C源代码的任何地方,我都可以将
F
称为CALLBACK
(是的,在内部,这些都是不同的东西,我使用一个名称在一端引用,在另一端调用一个函数)。
这种方法很复杂,但它是有效的,尽管配置这些宏并生成小的包装器并不理想。如果有人能详细说明一个更简单的过程,我会很高兴。我尝试过的所有方法都导致了重复的符号或不可见的声明。
英文:
I couldn't make it to work in a simple fashion IMO.
Given main package X
that imports Y
and Z
, both having to call (from C source code) F
declared in package M
,
I had to:
- Create a small wrapper
W1
forF
inY
and export it to be called fromY
's C source. - Create a small wrapper
W2
forF
inZ
and export it to be called fromZ
's C source. - In
Y
CGO CPPFLAGS define-DCALLBACK=W1
- In
Z
CGO CPPFLAGS define-DCALLBACK=W2
- From C source code, anywhere, I'm now able to refer to
F
asCALLBACK
(yeah, internally it's all different stuff, which I refer to using a single name at one end to call a single function at the other end).
This is convoluted, but it's working, although configuring such macros and producing little wrappers
is not being ideal. If anyone could detail a simpler procedure I would be glad. Everything
I tried ended up with duplicated symbols or non-visible declarations.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论