Функции вызов из C


Я пытаюсь создать статический объект, написанный в Go to interface с программой на C (скажем, модуль ядра или что-то еще).

Я нашел документацию по вызову функций C из Go, но я не нашел много о том, как идти в другую сторону. Я обнаружил, что это возможно, но сложно.

вот что я нашел:

сообщение в блоге о обратных вызовах между C и Go

Cgo документация

Golang письмо в список рассылки

есть ли у кого опыт с этим? Короче говоря, я пытаюсь создать модуль PAM, полностью написанный в Go.

4 133

4 ответа:

вы можете вызвать код Go из C. это запутанное предложение, хотя.

процесс описан в блоге, на который вы ссылаетесь. Но я вижу, что это не очень полезно. Вот короткий фрагмент без каких-либо лишнее. Это должно сделать вещи немного яснее.

package foo

// extern int goCallbackHandler(int, int);
//
// static int doAdd(int a, int b) {
//     return goCallbackHandler(a, b);
// }
import "C"

//export goCallbackHandler
func goCallbackHandler(a, b C.int) C.int {
    return a + b
}

// This is the public function, callable from outside this package.
// It forwards the parameters to C.doAdd(), which in turn forwards
// them back to goCallbackHandler(). This one performs the addition
// and yields the result.
func MyAdd(a, b int) int {
   return int( C.doAdd( C.int(a), C.int(b)) )
}

порядок, в котором все называется так:

foo.MyAdd(a, b) ->
  C.doAdd(a, b) ->
    C.goCallbackHandler(a, b) ->
      foo.goCallbackHandler(a, b)

ключ, чтобы помнить здесь, что функция обратного вызова должна быть отмечена с //export комментарий на ходу сбоку и как extern на C стороне. Это означает, что любой обратный вызов, который вы хотите использовать, должен быть определен внутри вашего пакета.

чтобы позволить пользователю вашего пакета предоставить пользовательскую функцию обратного вызова, мы используем тот же подход, что и выше, но мы предоставляем пользовательский обработчик пользователя (который является просто обычной функцией Go) в качестве параметра, который передается на сторону C как void*. Затем он получает callbackhandler в нашем пакете и называемый.

давайте использовать более продвинутый пример, с которым я сейчас работаю. В этом случае у нас есть функция C, которая выполняет довольно тяжелую задачу: она считывает список файлов с USB-устройства. Это может занять некоторое время, поэтому мы хотим, чтобы наше приложение было уведомлено о его прогрессе. Мы можем сделать это, передав указатель на функцию, который мы определили в нашей программе. Он просто отображает некоторую информацию о прогрессе для пользователя, когда он получает вызов. Поскольку он имеет хорошо известную подпись, Мы можем назначить его своим собственным тип:

type ProgressHandler func(current, total uint64, userdata interface{}) int

этот обработчик принимает некоторую информацию о ходе работы (текущее количество полученных файлов и общее количество файлов) вместе с значением интерфейса {}, которое может содержать все, что нужно пользователю.

теперь нам нужно написать C и пойти сантехника, чтобы позволить нам использовать этот обработчик. К счастью, функция C, которую я хочу вызвать из библиотеки, позволяет нам передать структуру userdata типа void*. Это означает, что он может держать все, что мы хотим, чтобы он держал, никаких вопросов и мы вернем его в мир Go как есть. Чтобы все это работало, мы не вызываем библиотечную функцию из Go напрямую, но создаем для нее оболочку C, которую назовем goGetFiles(). Это фантик, который поставляет нашим обратного вызова библиотека Си, наряду с объектом, например, userdata.

package foo

// #include <somelib.h>
// extern int goProgressCB(uint64_t current, uint64_t total, void* userdata);
// 
// static int goGetFiles(some_t* handle, void* userdata) {
//    return somelib_get_files(handle, goProgressCB, userdata);
// }
import "C"
import "unsafe"

отметим, что goGetFiles() функция не принимает никаких указателей функций для обратных вызовов в качестве параметров. Вместо этого обратный вызов, предоставленный нашим пользователем, упаковывается в пользовательскую структуру это содержит как этот обработчик, так и собственное значение userdata пользователя. Мы передаем это в goGetFiles() в качестве параметра userdata.

// This defines the signature of our user's progress handler,
type ProgressHandler func(current, total uint64, userdata interface{}) int 

// This is an internal type which will pack the users callback function and userdata.
// It is an instance of this type that we will actually be sending to the C code.
type progressRequest struct {
   f ProgressHandler  // The user's function pointer
   d interface{}      // The user's userdata.
}

//export goProgressCB
func goProgressCB(current, total C.uint64_t, userdata unsafe.Pointer) C.int {
    // This is the function called from the C world by our expensive 
    // C.somelib_get_files() function. The userdata value contains an instance
    // of *progressRequest, We unpack it and use it's values to call the
    // actual function that our user supplied.
    req := (*progressRequest)(userdata)

    // Call req.f with our parameters and the user's own userdata value.
    return C.int( req.f( uint64(current), uint64(total), req.d ) )
}

// This is our public function, which is called by the user and
// takes a handle to something our C lib needs, a function pointer
// and optionally some user defined data structure. Whatever it may be.
func GetFiles(h *Handle, pf ProgressFunc, userdata interface{}) int {
   // Instead of calling the external C library directly, we call our C wrapper.
   // We pass it the handle and an instance of progressRequest.

   req := unsafe.Pointer(&progressequest{ pf, userdata })
   return int(C.goGetFiles( (*C.some_t)(h), req ))
}

вот и все для наших Привязок C. Код пользователя теперь очень прямо вперед:

package main

import (
    "foo"
    "fmt"
)

func main() {
    handle := SomeInitStuff()

    // We call GetFiles. Pass it our progress handler and some
    // arbitrary userdata (could just as well be nil).
    ret := foo.GetFiles( handle, myProgress, "Callbacks rock!" )

    ....
}

// This is our progress handler. Do something useful like display.
// progress percentage.
func myProgress(current, total uint64, userdata interface{}) int {
    fc := float64(current)
    ft := float64(total) * 0.01

    // print how far along we are.
    // eg: 500 / 1000 (50.00%)
    // For good measure, prefix it with our userdata value, which
    // we supplied as "Callbacks rock!".
    fmt.Printf("%s: %d / %d (%3.2f%%)\n", userdata.(string), current, total, fc / ft)
    return 0
}

все это выглядит намного сложнее, чем есть на самом деле. Порядок вызовов не изменился в отличие от нашего предыдущего примера, но мы получаем два дополнительных вызова в конце цепочки:

порядок выглядит следующим образом:

foo.GetFiles(....) ->
  C.goGetFiles(...) ->
    C.somelib_get_files(..) ->
      C.goProgressCB(...) ->
        foo.goProgressCB(...) ->
           main.myProgress(...)

это не запутанное предложение, если вы используете gccgo. Это работает здесь:

фу.иди

package main

func Add(a, b int) int {
    return a + b
}

бар.c

#include <stdio.h>

extern int go_add(int, int) __asm__ ("example.main.Add");

int main() {
  int x = go_add(2, 3);
  printf("Result: %d\n", x);
}

Makefile

all: main

main: foo.o bar.c
    gcc foo.o bar.c -o main

foo.o: foo.go
    gccgo -c foo.go -o foo.o -fgo-prefix=example

clean:
    rm -f main *.o

ответ изменился с выходом Go 1.5

этот вопрос, который я задал некоторое время назад, снова решает проблему в свете 1.5 добавленных возможностей

использование кода Go в существующем проекте C

насколько я понимаю, это невозможно:

Примечание: Вы не можете определить любые функции C в преамбуле, если вы используете поставляемый.

источник:https://github.com/golang/go/wiki/cgo