cgo:Go内存中的Go指针

cgo:Go内存中的Go指针,go,cgo,Go,Cgo,代码是否: unsafe.Pointer(&du) 其中du是一些接口满足以下列表中的规则1吗 Go代码可以将Go指针传递给C,前提是Go内存 它所指向的不包含任何Go指针。这条规则必须遵守 在C执行期间保留,因为程序不能存储任何 把指针插入那个内存 换句话说,C-pointer-to-Go接口是否被视为“指向包含Go指针的Go内存的指针” 更新: 我的问题是以下代码: type Receiver interface { Signal() } func WowFunctio

代码是否:

unsafe.Pointer(&du)
其中
du
是一些
接口
满足以下列表中的规则1吗

Go代码可以将Go指针传递给C,前提是Go内存 它所指向的不包含任何Go指针。这条规则必须遵守 在C执行期间保留,因为程序不能存储任何 把指针插入那个内存

换句话说,C-pointer-to-Go
接口
是否被视为“指向包含Go指针的Go内存的指针”

更新:

我的问题是以下代码:

type Receiver interface {
    Signal()
}

func WowFunction(data []byte, du Receiver) {

    C.wow_function( (*C.char)( unsafe.Pointer(&data[0]) ), 
                    (C.size_t)(len(data)),
                    unsafe.Pointer(&du))    // this is my suspect
}
我的想法是让C代码调用
接收器的“方法”
Signal()
。我通过导出Go回调并将该
&du
作为参数传递给回调来实现这一点:

//export go_callback
func go_callback(object_ptr unsafe.Pointer) {

    object := *(*Receiver)(object_ptr);
    object.Signal()
}
还有别的办法吗?

回答 在@JimB之后,是的,这被认为是一个包含Go指针的Go内存指针,因此在Go>=1.6中,运行程序时会出现“cgo参数具有Go指针到Go指针”恐慌

如果您想在运行时使用类似的东西,可以通过使用
GODEBUG=cgocheck=0
运行程序来禁用死机

实际上,我以前在go<1.6中编写过类似的代码来包装从线程化C代码异步调用的面向对象处理程序代码——所以我认为用例没有那么疯狂

选择 将指针直接传递到底层C代码的一种可能的替代方法是为处理程序创建线程安全的全局注册表,因此基本上可以将一些索引传递到注册表到C代码中,在回调中接收它,在处理程序中查找该索引,然后调用其上的函数


例子 这些有点冗长,但请给出一个实际的工作示例。如果您只想看一下注册表实现示例,请跳到底部

直接指针示例(您的问题) 这不是世界上最好的C语言,但这里有一个我以前使用过的其他代码的快速简化

库代码

生成文件:

libtesting:
  gcc -fPIC -c library/testing.c -o library/testing.o
  gcc -dynamiclib library/testing.o -o library/libtesting.dylib
C标题:

/* library/testing.h */

#ifndef __TESTING_H__
#define __TESTING_H__

#include <pthread.h>

struct worker_node {
  pthread_t worker;
  struct worker_node *next;
};

// Structs for publisher
struct publisher {
  void (* callback)(void *, char *, int);
  void *context;
  struct worker_node *workers;
};

struct publisher * publisher_new(void *, void (*)(void *, char *, int));
void publisher_cleanup(struct publisher *);
void publisher_finish(struct publisher *);
void publisher_publish(struct publisher *, char *, int);

#endif // __TESTING_H__
去:

忽略不清理内存分配

如果将go、c wrappers和Makefile放在顶级目录中,将“c library”放在名为library的文件夹中,然后运行
make&&go build
(在OS X上,调整Linux的Makefile编译器标志),运行二进制文件时,应该会出现“cgo参数具有go指针到go指针”的死机,使用go>=1.6,而go<1.6时不会出现死机。使用go 1.6构建并运行
GODEBUG=cgocheck=0
应输出
收到的测试[116 101 115 116]

注册表示例(可选) 要使此示例在1.6下运行而不禁用
cgocheck
请添加类似这样的注册表:

package main

/*
#cgo LDFLAGS: -lpthread -Llibrary -ltesting
#include "library/testing.h"

extern void cgo_callback_wrapper(void * context, char *message, int message_len);
*/
import "C"

import (
    "fmt"
    "sync"
    "unsafe"
)

var registry map[int]Handler
var handlers int
var mutex = sync.Mutex{}

type Handler interface {
    HandleMessage([]byte)
}

type Publisher struct {
    base *C.struct_publisher
}

//export callbackWrapper
func callbackWrapper(cContext unsafe.Pointer, cMessage *C.char, cMessageSize C.int) {
    mutex.Lock()
    handler := registry[*(*int)(cContext)]
    mutex.Unlock()
    message := C.GoBytes(unsafe.Pointer(cMessage), cMessageSize)
    handler.HandleMessage(message)
}

func (p *Publisher) Publish(message []byte) {
    cMessage := (*C.char)(unsafe.Pointer(&message[0]))
    cMessageLen := C.int(len(message))
    C.publisher_publish(p.base, cMessage, cMessageLen)
}

func CreatePublisher(handler Handler) *Publisher {
    mutex.Lock()
    index := handlers
    handlers++
    if registry == nil {
        registry = make(map[int]Handler)
    }
    registry[index] = handler
    mutex.Unlock()
    return &Publisher{
        base: C.publisher_new(unsafe.Pointer(&index), (*[0]byte)(C.cgo_callback_wrapper)),
    }
}

func (p *Publisher) Finish() {
    C.publisher_finish(p.base)
}

//////// EXAMPLE ////////

type TestHandler struct {
    name string
}

func (h TestHandler) HandleMessage(message []byte) {
    fmt.Printf("%s received %v", h.name, message)
}

func main() {
    handler := TestHandler{name: "Test"}

    publisher := CreatePublisher(handler)
    publisher.Publish([]byte("test"))
    publisher.Finish()
}

请注意,在
CreatePublisher
callbackWrapper
中添加了注册表代码,现在我们不传递指向接口的指针,而只传递指向注册表中接口索引的指针。以同样的方式编译,不再恐慌

这里有另一个解决方法。除非您的C库不访问正在传递的内容,否则不鼓励这样做(就像C库只在回调时传递它一样)

这个想法是把你的不安全的指针指向C.ulonglong

呼叫C

C.function(C.ulonglong(uintptr(unsafe.Pointer(&something))))
把它扔回去

(*SomethingType)(unsafe.Pointer(uintptr(the_c_long_value)))

是的,指向接口的指针被认为是“指向包含Go指针的Go内存的指针”,但是将指向Go接口的指针传递到C中有什么有效的用途呢?@JimB请参阅我文章的更新。我对我需要它的原因进行了描述。
中的
&du
是否不安全。指针(&du))//这是我的嫌疑犯
应该是
&de
du
甚至没有声明,因此首先无法编译。但最终,一个接口包含2个指针值,因此cgo不允许您将指针传递给接口,即使有办法使其工作。@JimB“du”=“de”。谢谢。仅供参考,如果您想知道我的用例出现在哪里,它是一个用C编写的高级kafka库的go包装器,该库包装librdkafka,并严重依赖回调和传递上下文。
package main

/*
#cgo LDFLAGS: -lpthread -Llibrary -ltesting
#include "library/testing.h"

extern void cgo_callback_wrapper(void * context, char *message, int message_len);
*/
import "C"

import (
    "fmt"
    "sync"
    "unsafe"
)

var registry map[int]Handler
var handlers int
var mutex = sync.Mutex{}

type Handler interface {
    HandleMessage([]byte)
}

type Publisher struct {
    base *C.struct_publisher
}

//export callbackWrapper
func callbackWrapper(cContext unsafe.Pointer, cMessage *C.char, cMessageSize C.int) {
    mutex.Lock()
    handler := registry[*(*int)(cContext)]
    mutex.Unlock()
    message := C.GoBytes(unsafe.Pointer(cMessage), cMessageSize)
    handler.HandleMessage(message)
}

func (p *Publisher) Publish(message []byte) {
    cMessage := (*C.char)(unsafe.Pointer(&message[0]))
    cMessageLen := C.int(len(message))
    C.publisher_publish(p.base, cMessage, cMessageLen)
}

func CreatePublisher(handler Handler) *Publisher {
    mutex.Lock()
    index := handlers
    handlers++
    if registry == nil {
        registry = make(map[int]Handler)
    }
    registry[index] = handler
    mutex.Unlock()
    return &Publisher{
        base: C.publisher_new(unsafe.Pointer(&index), (*[0]byte)(C.cgo_callback_wrapper)),
    }
}

func (p *Publisher) Finish() {
    C.publisher_finish(p.base)
}

//////// EXAMPLE ////////

type TestHandler struct {
    name string
}

func (h TestHandler) HandleMessage(message []byte) {
    fmt.Printf("%s received %v", h.name, message)
}

func main() {
    handler := TestHandler{name: "Test"}

    publisher := CreatePublisher(handler)
    publisher.Publish([]byte("test"))
    publisher.Finish()
}
C.function(C.ulonglong(uintptr(unsafe.Pointer(&something))))
(*SomethingType)(unsafe.Pointer(uintptr(the_c_long_value)))