library - Funciones de Call Go desde C
make shared library gcc (4)
La respuesta ha cambiado con el lanzamiento de Go 1.5
Esta pregunta SO que hice hace un tiempo aborda el problema nuevamente a la luz de las 1.5 capacidades adicionales
Estoy tratando de crear un objeto estático escrito en Ir a la interfaz con un programa C (por ejemplo, un módulo kernel o algo así).
Encontré documentación sobre cómo llamar a las funciones C desde Go, pero no he encontrado mucho sobre cómo hacerlo. Lo que he encontrado es que es posible, pero complicado.
Esto es lo que encontré:
Publicación de blog sobre devoluciones de llamadas entre C y Go
Publicación de la lista de correo de Golang
¿Alguien tiene experiencia con esto? En resumen, estoy tratando de crear un módulo PAM escrito completamente en Go.
No es una proposición confusa si usas gccgo. Esto funciona aquí:
foo.go
package main
func Add(a, b int) int {
return a + b
}
bar.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
Por lo que a mí respecta, no es posible:
Nota: no puede definir ninguna función C en el preámbulo si está utilizando exportaciones.
Puedes llamar al código Go desde C. aunque es una propuesta confusa.
El proceso se describe en la publicación de blog a la que se ha vinculado. Pero puedo ver cómo eso no es muy útil. Aquí hay un pequeño fragmento sin bits innecesarios. Debería aclarar un poco las cosas.
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)) )
}
El orden en el que se llama a todo es el siguiente:
foo.MyAdd(a, b) ->
C.doAdd(a, b) ->
C.goCallbackHandler(a, b) ->
foo.goCallbackHandler(a, b)
La clave para recordar aquí es que una función de devolución de llamada debe estar marcada con //export
comentario en el lado Ir y como extern
en el lado C. Esto significa que cualquier devolución de llamada que desee utilizar debe estar definida dentro de su paquete.
Para permitir que un usuario de su paquete suministre una función de devolución de llamada personalizada, utilizamos el mismo enfoque exacto que el anterior, pero suministramos el controlador personalizado del usuario (que es solo una función Go normal) como un parámetro que se pasa a la C lado como void*
. Luego es recibido por el callbackhandler en nuestro paquete y llamado.
Usemos un ejemplo más avanzado con el que estoy trabajando actualmente. En este caso, tenemos una función C que realiza una tarea bastante pesada: lee una lista de archivos de un dispositivo USB. Esto puede demorar un tiempo, por lo que queremos que se notifique a nuestra aplicación sobre su progreso. Podemos hacer esto pasando un puntero de función que definimos en nuestro programa. Simplemente muestra información de progreso al usuario cada vez que recibe una llamada. Como tiene una firma conocida, podemos asignarle su propio tipo:
type ProgressHandler func(current, total uint64, userdata interface{}) int
Este controlador toma cierta información de progreso (número actual de archivos recibidos y número total de archivos) junto con un valor de interfaz {} que puede contener todo lo que el usuario lo necesite.
Ahora tenemos que escribir la tubería C and Go para permitirnos usar este controlador. Afortunadamente, la función C a la que deseo llamar desde la biblioteca nos permite pasar una estructura userdata de tipo void*
. Esto significa que puede contener todo lo que deseamos, sin hacer preguntas y lo devolveremos al mundo de Go-as. Para que todo esto funcione, no llamamos directamente a la función de la biblioteca desde Go, sino que creamos un contenedor C para ella, que llamaremos goGetFiles()
. Es esta envoltura la que realmente proporciona nuestra devolución de llamada Go a la biblioteca C, junto con un objeto 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"
Tenga en cuenta que la función goGetFiles()
no toma ningún puntero de función para las devoluciones de llamada como parámetros. En cambio, la devolución de llamada que nuestro usuario ha suministrado está empaquetada en una estructura personalizada que contiene tanto ese manejador como el propio valor de datos de usuario del usuario. Pasamos esto a goGetFiles()
como el parámetro 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 ))
}
Eso es todo por nuestras ataduras de C El código del usuario ahora es muy directo:
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
}
Todo esto parece mucho más complicado de lo que es. El orden de las llamadas no ha cambiado en comparación con nuestro ejemplo anterior, pero recibimos dos llamadas adicionales al final de la cadena:
El orden es el siguiente:
foo.GetFiles(....) ->
C.goGetFiles(...) ->
C.somelib_get_files(..) ->
C.goProgressCB(...) ->
foo.goProgressCB(...) ->
main.myProgress(...)