# dragonruby-bind doc This document describes how to use `dragonruby-bind` and also covers some implementation details that will help to build the right mental model ### Hello World Create a simple file `bridge.c` with the following content: ```c double square(double d) { return d * d; } ``` Now, generate bindings: ```bash dragonruby-bind bridge.c --output=bindings.c ``` The output file `bindings.c` will contain something like the following: ```c #include #include #include #include #include #include #include "bridge.c" // MRuby `typedef`s mrb_int in the mruby/value.h // Then `#define`s mrb_int in mruby.h // We need to undo the macro and avoid it's usage // FIXME: I'm surely doing something wrong #ifdef mrb_int #undef mrb_int #endif void *(*drb_symbol_lookup)(const char *sym) = NULL; static void (*drb_free_foreign_object_f)(mrb_state *, void *); static struct RClass *(*mrb_module_get_f)(mrb_state *, const char *); static mrb_int (*mrb_get_args_f)(mrb_state *, mrb_args_format, ...); static struct RClass *(*mrb_module_get_under_f)(mrb_state *, struct RClass *, const char *); static struct RClass *(*mrb_class_get_under_f)(mrb_state *, struct RClass *, const char *); static struct RClass *(*mrb_define_module_under_f)(mrb_state *, struct RClass *, const char *); static void (*mrb_define_module_function_f)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec); static struct RClass *(*mrb_define_class_under_f)(mrb_state *, struct RClass *, const char *, struct RClass *); static void (*mrb_define_method_f)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec); static void (*mrb_define_class_method_f)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec); static struct RData *(*mrb_data_object_alloc_f)(mrb_state *, struct RClass *, void *, const mrb_data_type *); static mrb_value (*mrb_str_new_cstr_f)(mrb_state *, const char *); static void (*mrb_raise_f)(mrb_state *, struct RClass *, const char *); static struct RClass *(*mrb_exc_get_f)(mrb_state *, const char *); static void drb_free_foreign_object_indirect(mrb_state *state, void *pointer) { drb_free_foreign_object_f(state, pointer); } static double drb_ffi__ZTSd_FromRuby(mrb_state *state, mrb_value self) { return mrb_float(self); } static mrb_value drb_ffi__ZTSd_ToRuby(mrb_state *state, double value) { return mrb_float_value(state, value); } static mrb_value drb_ffi_square_Binding(mrb_state *state, mrb_value value) { mrb_value *args = 0; mrb_int argc = 0; mrb_get_args_f(state, "*", &args, &argc); double d_0 = drb_ffi__ZTSd_FromRuby(state, args[0]); double ret_val = square(d_0); return drb_ffi__ZTSd_ToRuby(state, ret_val); } static int drb_ffi_init_indirect_functions(void *(*lookup)(const char *)); DRB_FFI_EXPORT void drb_register_c_extensions(void *(*lookup)(const char *), mrb_state *state, struct RClass *FFI) { if (drb_ffi_init_indirect_functions(lookup)) return; struct RClass *module = mrb_define_module_under_f(state, FFI, "CExt"); struct RClass *object_class = state->object_class; mrb_define_module_function_f(state, module, "square", drb_ffi_square_Binding, MRB_ARGS_REQ(1)); } static int drb_ffi_init_indirect_functions(void *(*lookup)(const char *fnname)) { drb_symbol_lookup = lookup; if (!(drb_free_foreign_object_f = (void (*)(mrb_state *, void *)) lookup("drb_free_foreign_object"))) return -1; if (!(mrb_class_get_under_f = (struct RClass *(*)(mrb_state *, struct RClass *, const char *)) lookup("mrb_class_get_under"))) return -1; if (!(mrb_data_object_alloc_f = (struct RData *(*)(mrb_state *, struct RClass *, void *, const mrb_data_type *)) lookup("mrb_data_object_alloc"))) return -1; if (!(mrb_define_class_method_f = (void (*)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec)) lookup("mrb_define_class_method"))) return -1; if (!(mrb_define_class_under_f = (struct RClass *(*)(mrb_state *, struct RClass *, const char *, struct RClass *)) lookup("mrb_define_class_under"))) return -1; if (!(mrb_define_method_f = (void (*)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec)) lookup("mrb_define_method"))) return -1; if (!(mrb_define_module_function_f = (void (*)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec)) lookup("mrb_define_module_function"))) return -1; if (!(mrb_define_module_under_f = (struct RClass *(*)(mrb_state *, struct RClass *, const char *)) lookup("mrb_define_module_under"))) return -1; if (!(mrb_exc_get_f = (struct RClass *(*)(mrb_state *, const char *)) lookup("mrb_exc_get"))) return -1; if (!(mrb_get_args_f = (mrb_int (*)(mrb_state *, mrb_args_format, ...)) lookup("mrb_get_args"))) return -1; if (!(mrb_module_get_f = (struct RClass *(*)(mrb_state *, const char *)) lookup("mrb_module_get"))) return -1; if (!(mrb_module_get_under_f = (struct RClass *(*)(mrb_state *, struct RClass *, const char *)) lookup("mrb_module_get_under"))) return -1; if (!(mrb_raise_f = (void (*)(mrb_state *, struct RClass *, const char *)) lookup("mrb_raise"))) return -1; if (!(mrb_str_new_cstr_f = (mrb_value (*)(mrb_state *, const char *)) lookup("mrb_str_new_cstr"))) return -1; return 0; } ``` Compile this as a shared library: ```c # DRB_ROOT is the path to the gtk source directory clang -shared \ -isystem $DRB_ROOT/mruby/include/ \ -isystem $DRB_ROOT/ \ -o native/macos/bindings.dylib \ bindings.c ``` Now, somewhere within `main.rb` do the following: ```rb # Teach DRGTK about the bindings $gtk.ffi_misc.dlopen("bindings") # Use the `square` function seamlessly puts FFI::CExt::square(42) # yeilds: 1764.0 ``` This is the bare minimum that is needed to start using the C extensions. Now, more complex parts. ### DRB FFI It is a good idea to include `dragonruby.h` into the C bridging file. It comes with a number of helpful macros. As an example, you can specify for which functions to generate bindings or change the name under which the function is available from DRGTK: ```c #include /// Binding for this function won't be generated void something_useless() { /// .... } /// This one is accessible from Ruby as `square` DRB_FFI double square(double d) { return d * d; } /// This one is accessible from Ruby as `sqr_int` DRB_FFI_NAME("sqr_int") int ffi_square_int(int d) { return d * d; } ``` _Note: It is always a good idea to mark your functions with `DRB_FFI` or `DRB_FFI_NAME` explicitly. Support for unannotated functions is meant for third-party libraries._ ### Structs It is possible to use C structs from DRGTK. Here is a C example and its corresponding usage from Ruby. ```c #include typedef struct Point { int x; int y; } Point; DRB_FFI void printPoint(Point p) { printf("Point(%d, %d)\n", p.x, p.y); } DRB_FFI Point fourtyPoint() { Point p; p.x = 42; p.y = 42; return p; } ``` Here is how to use it in the Ruby code: ```ruby $gtk.ffi_misc.gtk_dlopen("bindings") include FFI::CExt p = Point.new p.x = 15 p.y = 22 printPoint(p) p2 = fourtyPoint() puts "Point(#{p2.x}, #{p2.y}) (from Ruby)" p2.x = 152 puts "Point(#{p2.x}, #{p2.y}) (from Ruby)" ``` The output will be: ``` Point(15, 22) Point(42, 42) (from Ruby) Point(152, 42) (from Ruby) ``` ### Pointers Here is how to use pointers: ```c #include DRB_FFI int *createInts(int size) { int *ints = calloc(size, sizeof(int)); return ints; } DRB_FFI void printInts(int *ints, int size) { printf("int array: "); for (int i = 0; i < size; i++) { printf("%d ", ints[i]); } printf("\n"); } DRB_FFI void free_ints(int *ints) { free(ints); } ``` The usage: ```rb $gtk.ffi_misc.gtk_dlopen("bindings") include FFI::CExt ints = createInts(10) 10.times do |i| ints[i] = i end printInts(ints, 10) freeInts(ints) p = IntPointer.new p[0] = 15 printInts(p, 1) puts "print int from ruby: #{p[0]}" ``` The output: ``` print ints from C: 0 1 2 3 4 5 6 7 8 9 print ints from C: 15 print int from ruby: 15 ``` Important part here is the memory ownership. In the case of `createInts` the points is allocated in the C land, and therefore it should be deallocated in the C land. It is a responsibility of developer to take care of this memory. In the case of `IntPointer.new` the pointer is allocated in the Ruby land, and therefore it will be deallocated by DRGTK, without any need to worry about the ownership. ### C Strings Technically, the C string is a just a pointer, i.e.: `char *`. But we provide some convenient sugar. Here is an example: ```c #include DRB_FFI char *allocateString() { char *str = calloc(6, sizeof(char)); str[0] = 'h'; str[1] = 'e'; str[2] = 'l'; str[3] = 'l'; str[4] = 'o'; str[5] = '\n'; return str; } DRB_FFI void freeString(char *s) { free(s); } DRB_FFI void printString(char *s) { printf("hello from C: %s\n", s); } DRB_FFI char *getStaticString() { return "Some static string"; } ``` Usage: ```rb $gtk.ffi_misc.gtk_dlopen("bindings") include FFI::CExt s1 = allocateString() printString(s1) freeString(s1) printString("or Ruby?") s2 = getStaticString() printString(s2) puts "print C string from Ruby #{s2.str}" ``` Output: ``` hello from C: hello hello from C: or Ruby? hello from C: Some static string print C string from Ruby: Some static string ``` General rules are the same as with any other pointers, but there are few more additional notes: - `CharPointer`s have `str` method that returns a normal Ruby string - Ruby strings automatically converted to `char *`, as in the `printString("or Ruby?")` ### CExt In order to avoid clashes and name collisions all the bridging functions are put under a separate module (or namespace) under `FFI`. By default, the name `CExt` is used, but it can be changed to anything else via the `--ffi-module` module, e.g.: ```bash dragonruby-bind --ffi-module=CoolStuff bridge.c ``` Then one can use `include FFI::CoolStuff` instead. ### Pitfalls There is no so-called marshalling when it comes to structs. When you read or write to a struct field you are writing to the underlying C struct, which brings some unexpected results. The following structs can be easily used from Ruby: ```c typedef struct Point { int x; int y; } Point; typedef struct Size { int width; int height; } Size; typedef struct Rectangle { Point origin; Size size; } Rectangle; ``` ```rb o = Point.new o.x = 15 o.y = 25 s = Size.new s.width = 150 s.height = 250 r = Rectangle.new r.origin = o r.size = s puts "#{r.origin.x}, #{r.origin.y}" #1 r.origin.x = 42 #2 puts "#{r.origin.x}, #{r.origin.y}" #3 p = r.origin p.x = 42 r.origin = p puts "#{r.origin.x}, #{r.origin.y}" #4 ``` In this example `15, 25` will be printed at line `#1`, after the assignment at `#2` the same string will be printed `15, 25` at line `#3`. That's because each `.` in Ruby returns a new object, in this case `p.origin` returns a copy of the original `Point`. The right way to handle this case is right before `#4`. ### Rough edges Currently, there are no type checks and no checks on the number of arguments. If you call a C function that expects an integer with a double - it may give some garbage. If you call a C function with more or fewer arguments, then it may give some garbage, or crash. This will come in later.