Este es el décimo tercero en mi serie de posts sobre escribir extensiones de rubí en C. El primer post hablaba de la estructura básica de un proyecto, incluyendo cómo configurar edificio. El segundo mensaje habló sobre la generación de la documentación. El tercer mensaje habló sobre la inicialización del módulo y la creación de clases. El cuarto mensaje habló sobre los tipos y valores de retorno. El quinto puesto se centró en la creación y el manejo de excepciones. El sexto puesto habló de bloques catch rubí y tiro. El puesto séptimo charla sobre cómo tratar con números. El octavo puesto habló de cuerdas. El noveno puesto se centró en las matrices. El décimo puesto miró hashes. El undécimo mensaje exploró bloques y devoluciones de llamada. El duodécimo puesto miró a asignar y liberar memoria. Este post se centrará en envolver las estructuras de datos C en objetos de rubí.
Envolver las estructuras de datos C
En el desarrollo de una extensión de rubí en C, puede ser necesario para salvar una estructura C asignada dentro de un objeto Ruby. Por ejemplo, en los enlaces rubí libvirt, un virConnectPtr (que apunta a un objeto de conexión libvirt) se guarda en el interior del objeto Libvirt :: Connect rubí, y que el puntero se recupera desde el objeto cada vez que un método de instancia se llama. Tenga en cuenta que el puntero a la estructura C se almacena dentro del objeto de Ruby de una manera que el código ruby no puede llegar a; sólo extensiones de C tendrán acceso a este puntero. Sólo hay 3 APIs que se utilizan para manipular estos consejos:
- Data_Wrap_Struct (klass VALUE, void (* marca) (), void (*) (libre), void * ptr) – Envuelva la estructura de datos C en ptr en una clase de tipo klass. El argumento libre es un puntero a función a una función que se llamará cuando el objeto está siendo recolectado. Si las referencias a estructuras C otros objetos rubí, entonces el puntero de función marca también deben estar disponibles y deben marcar adecuadamente los otros objetos con rb_gc_mark (). Esta función devuelve un valor que es un objeto de tipo klass.
- Data_Make_Struct (klass VALUE, de tipo c, void (* marca) (), void (* libre) (), de tipo c * ptr ) – Similar a Data_Wrap_Struct (), pero primero asigna y luego se envuelve la estructura C en un objeto. El klass, marca, libre y ptr argumentos tienen el mismo significado que Data_Wrap_Struct (). El argumento de tipo c es el nombre real del tipo que necesita ser asignado (sizeof (tipo) se utilizará para asignar).
- Data_Get_Struct (obj VALUE, de tipo c, c-type * ptr ) – Obtener la estructura de datos del C-type c del objeto obj, y poner el resultado en ptr. Tenga en cuenta que esta asignación de puntero funciona porque se trata de una macro
Un ejemplo demostrará el uso de estas funciones:.
1) estática m_example VALOR;
2) estática c_conn VALOR;
3)
4) struct {MyStruct
5) int a;
6) int b;
7)} ;
8)
9) static void mystruct_free (void * s)
10) {
11) xfree (s);
12)}
13)
14) VALOR estática example_open (VALOR m)
15) {
16) struct MyStruct * conn;
17) conn = ALLOC (struct MyStruct);
18) conn-> a = 25;
19) conn-> b = 99;
20) volver Data_Wrap_Struct (c_conn, NULL, mystruct_free, conn);
21)}
22)
23) conn_get_a valor estático (VALOR c)
24) {
25) struct MyStruct * conn;
26) Data_Get_Struct (c, struct MyStruct, conn) ;
27) volver INT2NUM (conn-> a);
28)}
29)
30) void Init_example (void)
31) {
32) m_example = rb_define_module ("Ejemplo");
33) rb_define_module_function (m_example, "abierto", example_open, 0);
34) c_conn = rb_define_class_under (m_example, "Conn", rb_cObject);
35) rb_define_method (c_conn "get_a", conn_get_a, 0);
36)}
En las líneas 32 y 33, se define el módulo de ejemplo y darle una función de módulo llamado «abierto». Líneas 34 y 35 definen una clase Conn debajo del módulo Ejemplo, y le da un método «get_a» la clase Conn. Líneas 14 a 21 son donde implementamos el Ejemplo :: función open. Allí, asignamos memoria para nuestra estructura de C, a continuación, utilizar Data_Wrap_Struct () para envolver esa estructura C en un objeto de rubí de tipo Ejemplo :: Conn Tenga en cuenta que también pasamos mystruct_free () como la devolución de llamada gratuita, cuando el objeto se cosechó por el recolector de basura, esta función en las líneas 9 a 12 serán llamados para liberar cualquier memoria. Ahora, cuando el usuario llama «get_a» en el Ejemplo :: objeto Conn rubí, la función de las líneas 23 a 27 se llamará. No usamos Data_Get_Struct () para ir a buscar la estructura de respaldo fuera del objeto, y luego devolver un número de rubí para el entero almacenado en su interior. Actualización: enlaces añadidos a todos los artículos anteriores. Actualización 27 de enero 2014: Se ha actualizado el ejemplo para fijar el uso de ALLOC (). Gracias a Thomas Thomassen en los comentarios.