Tạo c extension cho ruby (phần 1)
Tối ngày hôm qua bên ruby VN có tổ chức sự kiện hacknight mà mình có tham gia. Mình cùng với bạn Hoàng trong ban tổ chức đã thử tạo một C extension nhằm mục đích tìm ra các phần tử mà bị lặp lại trong Array sử dụng C extension của Ruby. Nhiều bạn nghĩ đến C extension thấy có vẻ khó khăn nhưng ...
Tối ngày hôm qua bên ruby VN có tổ chức sự kiện hacknight mà mình có tham gia. Mình cùng với bạn Hoàng trong ban tổ chức đã thử tạo một C extension nhằm mục đích tìm ra các phần tử mà bị lặp lại trong Array sử dụng C extension của Ruby.
Nhiều bạn nghĩ đến C extension thấy có vẻ khó khăn nhưng thực ra không hề khó chút nào, bởi ruby đã cung cấp một lượng API đủ để bạn có thể thao tác trên C giống như đang thao tác trên ruby, ví dụ như tạo object mới, tạo array, push vào array....
C extension có những ứng dụng rất phổ biến mà bạn đang sử dụng hàng ngày như nokogiri, hay database driver.
Lý do để sử dụng C extension cho ruby có thể gồm
- tốc độ nhanh hơn trong một số trường hợp
- bạn muốn sử dụng các thư viện đã viết sẵn bằng C, ví dụ như libxml, hay opencv, rmagick....
Mình có tham khảo cách làm trên rubygems.org :
http://guides.rubygems.org/gems-with-extensions/
Về cơ bản thì để làm một C extension đơn giản thì bạn cần
- logic viết trên C (có thể là một file, hoặc một bộ thư viện)
- make file generator viết trên ruby
Thực hành với ví dụ đơn giản
Logic viết trên C
foo.c
#include <stdio.h> /* khi require 'foo' 、 thì Init_foo() sẽ được gọi */ void Init_foo() { puts("Init_foo"); }
Make file generator
mk.rb
require 'mkmf' create_makefile('foo')
thứ tự run như sau
ruby mk.rb
make
make install
Sau đó mở irb lên và thử require 'foo', bạn sẽ thấy dòng chữ "Init_foo" được gọi!
Vậy là chúng ta đã có một ví dụ đơn giản nhất về C extension rồi.
Giải thích:
Chắc bạn nào đã từng lập trình C hay C++ đều đã biết, làm một makefile chuẩn với thư viện ngoài, đặc biệt khi thư viện đó phức tạp (ví dụ như openCV hay boost), là một điều khá mệt mỏi.
Để viết C extension cho ruby thì ruby cung cấp cho chúng ta API thông qua <ruby.h>.Việc include thì đơn giản bởi ruby.h đã nằm sẵn trong include path, nhưng để link với các file static library đi kèm với nó thì không hề.Do vậy mà ruby đã làm hộ chúng ta việc tạo ra makefile thông qua thư viện mkmf và hàm create_makefile. Hàm này có input là tên của thư viện đầu vào, ở đây là foo.
Sau khi chạy đoạn code ruby để tạo ra makefile thì chúng ta sẽ thu được một file Makefile có nội dung rất phức tạp :D
Chính vì thế mà không có thư viện mkmf thì chắc chúng ta sẽ khó mà viết được file này.
Tiếp đó make sẽ giúp chúng ta build foo.c thành thư viện link tĩnh của C, và make install sẽ giúp chúng ta đặt thư viện đó vào chỗ mà ruby có thể tìm và require được.
Như vậy là chúng ta đã hoàn thành bước đầu tiên để tạo C extension trong ruby.
Trong bài tiếp theo mình sẽ nói về cách sử dụng API của ruby.h để thao tác với ruby interpreter, giúp cho chúng ta làm được các ứng dụng phức tạp hơn.