首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >cgo: Go内存中的Go指针

cgo: Go内存中的Go指针
EN

Stack Overflow用户
提问于 2016-08-02 21:43:45
回答 2查看 2.2K关注 0票数 2

代码是:

代码语言:javascript
复制
unsafe.Pointer(&du)

du是否满足以下列表中的规则1?

https://github.com/golang/go/issues/12416

Go代码可以传递一个Go指针到C,条件是它所指向的Go内存不包含任何Go指针。在C执行过程中必须保留该规则,因为程序不能将任何Go指针存储到该内存中。

换句话说,Go的C指针是否被认为是“包含Go指针的Go内存指针”?

更新:

我的问题是以下代码:

代码语言:javascript
复制
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代码调用Receiver的“方法”Signal()。为此,我导出了一个回呼,并将该&du作为参数传递给回调:

代码语言:javascript
复制
//export go_callback
func go_callback(object_ptr unsafe.Pointer) {

    object := *(*Receiver)(object_ptr);
    object.Signal()
}

还有别的方法可以达到这个目的吗?

EN

回答 2

Stack Overflow用户

发布于 2016-08-03 15:52:22

回答

在@JimB的后续中,是的,这被认为是一个包含Go指针的Go内存指针,所以在go >= 1.6中,您将在运行程序时得到"cgo参数有Go指针到Go指针“的恐慌。

如果您想在运行时使用这样的东西,可以通过使用GODEBUG=cgocheck=0运行您的程序来禁用恐慌。

我以前在go < 1.6中写过这样的代码,用来包装从线程C代码异步调用的面向对象的处理程序代码--所以我不认为用例那么疯狂。

备选方案

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

示例

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

直接指针示例(您的问题)

不是世界上最好的C语言,但下面是我以前使用过的其他代码的快速简化

库代码

Makefile:

代码语言:javascript
复制
libtesting:
  gcc -fPIC -c library/testing.c -o library/testing.o
  gcc -dynamiclib library/testing.o -o library/libtesting.dylib

C标题:

代码语言:javascript
复制
/* 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__

C资料来源:

代码语言:javascript
复制
/* library/testing.c */

#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "testing.h"

struct message_wrapper {
  void * context;
  char * message;
  int message_len;
  void (* callback)(void *, char *, int);
};

struct publisher * publisher_new(void *context, void (*callback)(void *, char *, int)) {
  struct publisher * self = (struct publisher *)malloc(sizeof(struct publisher));
  assert(self);
  assert(self->callback = callback);
  self->context = context;
  self->workers = NULL;

  return self;
}

void publisher_cleanup(struct publisher * self) {
  struct worker_node * next_node;
  struct worker_node * node = self->workers;
  while (node != NULL) {
    next_node = node->next;
    free(node);
    node = next_node;
  }
  free(self);
  self = NULL;
}

static void * publisher_worker_thread(void * args) {
  struct message_wrapper * wrapper = (struct message_wrapper *)args;

  wrapper->callback(wrapper->context, wrapper->message, wrapper->message_len);

  free(wrapper->message);
  free(wrapper);

  pthread_exit(NULL);
}

void publisher_publish(struct publisher *self, char * message, int message_len) {
  pthread_attr_t attr;

  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

  struct worker_node * new_node = (struct worker_node *)malloc(sizeof(struct worker_node));
  new_node->next = self->workers;
  self->workers = new_node;

  struct message_wrapper *wrapper = (struct message_wrapper *)malloc(sizeof(struct message_wrapper));
  wrapper->message = malloc(message_len);
  memcpy(wrapper->message, message, message_len);
  wrapper->message_len = message_len;
  wrapper->context = self->context;
  wrapper->callback = self->callback;

  assert(!pthread_create(&self->workers->worker, &attr, publisher_worker_thread, (void *)wrapper));
}

void publisher_finish(struct publisher *self) {
  struct worker_node * node = self->workers;
  while (node != NULL) {
    assert(!pthread_join(node->worker, NULL));
    node = node->next;
  }
}

Go代码

C包装:

代码语言:javascript
复制
/* testing_c.c */

#include "_cgo_export.h"

void cgo_callback_wrapper(void * context, char *message, int message_len) {
    callbackWrapper(context, message, message_len);
}

去:

代码语言:javascript
复制
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"
    "unsafe"
)

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) {
    handler := *(*Handler)(cContext)
    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 {
    return &Publisher{
        base: C.publisher_new(unsafe.Pointer(&handler), (*[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()
}

无视不清理内存分配..。

如果您将go、c wrappers和makefile放在顶层目录中,那么名为make && go build的文件夹中的"C库“将运行make && go build(在OS上,为Linux调整Makefile编译器标志),使用Go >= 1.6,您应该会惊慌地看到"cgo参数有go指针到go指针”,而在运行二进制文件时不害怕Go < 1.6。使用go 1.6构建并使用GODEBUG=cgocheck=0运行应该输出Test received [116 101 115 116]

注册表示例(备选方案)

若要使此示例在不禁用cgocheck的情况下运行在1.6以下,请添加如下所示的注册表:

代码语言:javascript
复制
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()
}

注意添加了CreatePublishercallbackWrapper中的注册表代码,而不是传递指向接口的指针,而是传递指向注册表中接口索引的指针。编译方式相同,不再惊慌!

票数 1
EN

Stack Overflow用户

发布于 2017-06-29 13:29:25

下面是另一个解决这个问题的麻烦。这是不鼓励的,除非你的C库没有访问任何传递的内容(就像C库一样,传递它只是为了回调)。

想法是将您的unsafe.Pointer转换为C.ulonglong

调用C

代码语言:javascript
复制
C.function(C.ulonglong(uintptr(unsafe.Pointer(&something))))

把它倒回去

代码语言:javascript
复制
(*SomethingType)(unsafe.Pointer(uintptr(the_c_long_value)))
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38730989

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档