Вызовите функции Go из C

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

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

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

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

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

Почтовая рассылка Golang

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

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

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

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 на стороне Go и как extern на стороне C. Это означает, что любой обратный вызов, который вы хотите использовать, должен быть определен внутри вашего пакета.

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

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

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

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

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

 package foo // #include  // 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() не принимает никаких указателей на функции для обратных вызовов в качестве параметров. Вместо этого обратный вызов, который был предоставлен нашим пользователем, упакован в настраиваемую структуру, которая содержит как обработчик, так и собственное значение пользовательских данных пользователя. Мы передаем это в 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. Это работает здесь:

foo.go

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

bar.c

 #include  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

  • Как разбить длинную строку кода в Голанге?
  • Как создать случайную строку фиксированной длины в Go?
  • есть ли способ создать экземпляр структуры из строки?
  • Как читать ввод с консоли?
  • Каков наилучший способ связывания статических ресурсов в программе Go?
  • json.Marshal (struct) возвращает "{}"
  • Почему я получаю ошибку «не могу назначить» при установке значения для структуры как значения на карте?
  • Имена ключевых слов JSON с JSON Marshal в Go
  • Зачем мне делать () или new ()?
  • Мои структуры не сортируются в json
  • Что это за "foo. (Bar.Baz)" вещь в коде Go?
  • Давайте будем гением компьютера.