如何在多个包中从C中重用Go回调函数?

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

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 &quot;C&quot;
import &quot;fmt&quot;

//export F
func F() {
    fmt.Println(&quot;m.f&quot;)
}

m/m.h:

void m_f();

m/m.c:

#include &lt;stdio.h&gt;
#include &quot;_cgo_export.h&quot;
#include &quot;m.h&quot;

void m_f() {
    printf(&quot;m_f\n&quot;)
    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&#39;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 &quot;y.h&quot;
import &quot;C&quot;

import (
    &quot;fmt&quot;
    _ &quot;m&quot;
)

func Y() {
    fmt.Println(&quot;y.Y&quot;)
    C.y()
}

y/y.h:

void y();

y/y.c:

#include &lt;stdio.h&gt;
#include &quot;../m/m.h&quot;

void y() {
    printf(&quot;y.C.y\n&quot;);
    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 &quot;C&quot;

//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 &quot;_cgo_export.h&quot;

void do_run(void (*callback)())
{
    callback();
}

答案3

得分: 0

我无法以简单的方式完成工作,这是我个人的看法。

假设有一个主包X,导入了YZ,它们都需要调用(来自C源代码的)在包M中声明的F函数。

我必须执行以下步骤:

  1. Y中创建一个小的包装器W1,用于F函数,并将其导出以供Y的C源代码调用。
  2. Z中创建一个小的包装器W2,用于F函数,并将其导出以供Z的C源代码调用。
  3. Y的CGO CPPFLAGS中定义-DCALLBACK=W1
  4. Z的CGO CPPFLAGS中定义-DCALLBACK=W2
  5. 现在,无论在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:

  1. Create a small wrapper W1 for F in Y and export it to be called from Y's C source.
  2. Create a small wrapper W2 for F in Z and export it to be called from Z's C source.
  3. In Y CGO CPPFLAGS define -DCALLBACK=W1
  4. In Z CGO CPPFLAGS define -DCALLBACK=W2
  5. From C source code, anywhere, I'm now able to refer to F as CALLBACK (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.

huangapple
  • 本文由 发表于 2015年3月15日 11:07:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/29056808.html
匿名

发表评论

匿名网友

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

确定