Wrapping C data structures
When developing a ruby extension in C, it may be necessary to save an allocated C structure inside a Ruby object. For instance, in the ruby-libvirt bindings, a virConnectPtr (which points to a libvirt connection object) is saved inside of Libvirt::Connect ruby object, and that pointer is fetched from the object any time an instance method is called. Note that the pointer to the C structure is stored inside the Ruby object in a way that the ruby code can't get to; only C extensions will have access to this pointer. There are only 3 APIs that are used to manipulate these pointers:- Data_Wrap_Struct(VALUE klass, void (*mark)(), void (*free)(), void *ptr) - Wrap the C data structure in ptr into a class of type klass. The free argument is a function pointer to a function that will be called when the object is being garbage collected. If the C structure references other ruby objects, then the mark function pointer must also be provided and must properly mark the other objects with rb_gc_mark(). This function returns a VALUE which is an object of type klass.
- Data_Make_Struct(VALUE klass, c-type, void (*mark)(), void (*free)(), c-type *ptr) - Similar to Data_Wrap_Struct(), but first allocates and then wraps the C structure in an object. The klass, mark, free, and ptr arguments have the same meaning as Data_Wrap_Struct(). The c-type argument is the actual name of the type that needs to be allocated (sizeof(type) will be used to allocate).
- Data_Get_Struct(VALUE obj, c-type, c-type *ptr) - Get the C data structure of c-type out of the object obj, and put the result in ptr. Note that this pointer assignment works because this is a macro.
An example will demonstrate the use of these functions:
1) static VALUE m_example;
2) static VALUE c_conn;
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) static VALUE example_open(VALUE m)
15) {
16) struct mystruct *conn;
17) conn = ALLOC(struct mystruct);
18) conn->a = 25;
19) conn->b = 99;
20) return Data_Wrap_Struct(c_conn, NULL, mystruct_free, conn);
21) }
22)
23) static VALUE conn_get_a(VALUE c)
24) {
25) struct mystruct *conn;
26) Data_Get_Struct(c, struct mystruct, conn);
27) return INT2NUM(conn->a);
28) }
29)
30) void Init_example(void)
31) {
32) m_example = rb_define_module("Example");
33) rb_define_module_function(m_example, "open", 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) }
On lines 32 and 33, we define the Example module and give it a module function called "open". Lines 34 and 35 define a Conn class under the Example module, and gives the Conn class a "get_a" method. Lines 14 through 21 are where we implement the Example::open function. There, we allocate memory for our C structure, then use Data_Wrap_Struct() to wrap that C structure in a ruby object of type Example::Conn. Note that we also pass mystruct_free() as the free callback; when the object gets reaped by the garbage collector, this function on lines 9 through 12 will be called to free up any memory. Now when the user calls "get_a" on the Example::Conn ruby object, the function on lines 23 through 27 will be called. There we use Data_Get_Struct() to fetch the structure back out of the object, and then return a ruby number for the integer stored inside. Update: added links to all of the previous articles. Update Jan 27, 2014: Updated the example to fix the use of ALLOC(). Thanks to Thomas Thomassen in the comments.