Ejecutar Código C/C++ con Golang

Fernando Scasserra
Golang Argentina

--

Compañero del código que has estado años programando en C porque te gusta manejar el sistema operativo a bajo nivel, tengo una buena noticia para ti.

Golang es extremadamente parecido a C, pero con la ventaja de tener un webserver integrado, un garbage collector y un framework de testing.

Pero este post no es para analizar las bondades de este nuevo lenguaje, si están interesados en aprenderlo les recomiendo realizar este tour, leer este pequeño tutorial y ver estos ejemplos prácticos. Si esto no es suficiente, este es el libro más recomendado.

Lo que estuve probando hoy y me pareció fantástico es la interoperabilidad (palabra complicada si las hay) que existe entre Golang y C.

¿Qué significa esto?

Básicamente, desde un programa en Golang, se pueden llamar a librerías de C/C++ de una forma muy simple.

¿Por qué esto es importante?

Bien, hay decenas de miles de librerías/aplicaciones/drivers/sistemas legacy que están desarrollados en C/C++ y el poder ejecutarlos desde Go, nos da la potencia de poder interconectar sistemas existentes y proyectos open source a nuestros nuevos sistemas desarrollados en Golang.

Basta de introducción y vamos a los bifes…

Este es el programa en Golang más simple que puede haber:

Se define el nombre del package, se importa la librería para imprimir en pantalla, se imprime la frase dentro de la función main() que es la primera que se ejecuta.

Bien, como hago para llamar a una función de C?

Simple, al importar el pseudo-package “C” ya tenemos acceso al lenguaje:

Nótese que sobre la línea import “C”, aparece un comentario, todo lo que se ponga dentro de este comentario es lo que se va a interpretar como código fuente de C, en este caso solo se importa el stdlib.h que tiene muchas funciones últies, como la de random.

Si se ejecuta esto desde la consola:

Se puede ver el número Random, generado desde C.

Fantástico, que pase si queremos pasarle algo como parámetro, cómo hago?

Bueno, se pasa el parámetro directamente a la función de C, pero hay que tener cuidado con pasar el tipo de dato correcto, para eso la librería de C brinda algunas ayudas para convertir tipos.

Si queremos por ejemplo llamar a la función que crea una semilla para el random, la cual acepta un número como parámetro, podemos hacer lo siguiente:

Como se puede ver, se crea una función Seed que acepta un int, pero al pasar esta variable a la función srandom, se tiene que castear a C.uint (unsigned-int de C). En la función main() puede verse como se llama a Seed con el número 23 como parámetro. Esto es lo que devuelve en la consola:

Como se ve, el valor random brinda otro valor gracias a la generación a partir de la semilla.

Bien, que pasa ahora si queremos enviar una estructura pero a una función creada por nosotros?

Para esto tenemos que definir primero la estructura, la nueva función y el linkeo desde Golang a C (Vamos a ver cada caso).

Hay varias cosas en el código.

Primero dentro de del bloque de comentarios, se define la estructura “Persona”, se crea también la función “imprimePersona” que recibe un puntero a una estructura del tipo Persona. Eso es todo lo que hay de C.

Otra cosa que tenemos en el código es un tipo de dato llamado GoPersona que se lo define como del tipo “_Ctype_Persona”, de esa forma: con el “_” delante y con “type_X” donde X es la estrutura es como se define una variable en go con tipo de dato de estructura en C, medio críptico pero útil. Al hacer esto nos aseguramos que las estructuras sean las mismas en Go y C.

Por último la línea:

C.imprimePersona((*C.Persona)(unsafe.Pointer(&persona)))

Llama a la función imprimePersona de C, pasando como parámetro un puntero a la estructura persona que se crea arriba “persona := GoPersona{25, 2}”, se lo castea al tipo de dato *C.Persona y además se lo define como un puntero “unsafe”, esto es para que C pueda acceder a la variable en Go.

Esto es medio molesto, pero es lo único que hay que saber, después es siempre igual.

Si ejecutamos esto desde la línea de comando vemos:

Como se puede ver, se ejecuta en C la función que imprime los datos de la persona con los parámetros pasados en la estructura.

Ahora bien, vamos a algo más complicado, poder mandar una estructura a una función, pero en la función de C crear otra estructura, devolverla a Go y utilizarla.

Veamos antes que nada como es el código en C:

Se puede ver que se crean dos estructura nuevas “Animal” y “Ciudad”.

Y se crea una función, que va a recibir un Animal, va a imprimir los datos, va a pedir luego que se ingrese por línea de comando el nombre de una ciudad, va a cagar este nombre en una estructura y lo va a devolver. “pass_struct_animal” se llama la función.

Ahora tenemos que linkear los tipos de datos en Go y además hacer las llamadas:

Qué es lo interesante acá, se crean los tipos de dato como ya vimos antes, pero para GoAnimal y GoCiudad.

Se crea con ese tipo de dato un Animal (línea 49 a 51) nótese que para crear un string (el nombre “Gato”) en go y pasarlo a C, hay que indicarle que es un “CString”.

En la línea 53, es en donde se pasa la estructura Animal a la función en C, igual a como lo vimos antes, pero en este caso el resultado se lo guarda en una variable tipo GoCiudad.

Lo interesante luego es que en ciudad.nombre tenemos guardado el nombre, que en realidad en C es de tipo puntero a Char, hay que indicarle a Go que transforme el contenido de este tipo de dato a string, eso se hace automáticamente si utilizamos la función C.GoString().

Por último, como se creó el nombre con malloc en C, tenemos que liberar la memoria, golang no lo hace solo eso porque no tiene acceso a la memoria manejada desde C (por eso no lo agarra el garbage collector) entonces lo liberamos a mano con “C.free”.

Si corremos desde la consola el programa obtenemos:

Eso es todo, pero antes les voy a mostrar por qué es fantástico.

Supongamos que tenemos un sistema en C que venimos programando hace 3 años y queremos exponerlo como webservice, lo podemos hacer muy simple con el webserver integrado de Go.

Para eso hay que crear un webserver en Go, ponerlo a escuchar, atajar el request que mandamos desde un browser, llamar a la función de C, procesar lo que queremos y devolver los datos al webserver.

Así es como se hace:

Se crea un webserver() se lo deja creado de forma asincrónica (go webserver()) y se le indica que todo request que llegue se ataje con la función processRequest() que dentro lo que hace es llamar a la función Random() que hicimos antes.

Si corremos el programa y ejecutamos desde el browser la url http://localhost:8080 obtenemos:

Como se puede ver, se ha ejecutado una función definida en C desde un browser, gracias a Go.

Bueno, eso es todo, espero que les haya gustado y que lo puedan utilizar,

Les dejo el link al programa que usé para este Post.

Programa

Un agradecimiento especial a Ernest y Ale.Z que me ayudaron cuando les pedí auxilio hace un rato para hacer funcionar los malditos punteros. Dos crack del equipo de Apicore.

Ernest

Ale.Z

Si quieren aprender más sobre como llamar desde Golang a C, les dejo los dos tutoriales que leí para aprenderlo:

C? Go? Cgo!

Command Cgo

Saludos,

Fer

--

--