Article:
Viết extension cho Ruby
1171
ngocdaothanh.myopenid.com 172Updated over 3 years ago |
Dùng Ruby thật tự do thoải mái, nhưng có những trường hợp nên dùng C, như:
- Không có thư viện cho Ruby, nhưng lại có thư viện cho C.
- Cần tăng tốc độ chương trình.
Gặp những trường hợp thế này, có thể giải quyết bằng cách viết extension bằng C rồi ghép vào chương trình Ruby.
Khái niệm căn bản
Đọc:
- How to create a Ruby extension in C in under 5 minutes
- Chương Extending Ruby trong quyển Programming Ruby – The Pragmatic Programmer’s Guide
- File README.EXT có trong source code của Ruby
Compiler
Muốn extension hoạt động tốt thì phải compile nó bằng cùng compiler đã dùng để compile Ruby.
Trên Linux, đối với một phiên bản của hệ điều hành, nói chung mọi thứ đều được compile bởi duy nhất một phiên bản của compiler (thường là gcc). Do đó trên Linux Ruby và extension hầu như luôn cùng được compile bằng cùng một compiler. Compile extension trên Linux cũng rất dễ, xin xem tutorial trong mẹo Khởi động.
Trên Windows thì đúng là lắm thầy nhiều ma, có rất nhiều loại compiler: Visual C++ 6, Visual C++ 2003, Visual C++ 2005, Borland C++ 5.5, Borland C++ Builder… Trong các compiler, Visual C++ 6 được coi là dễ dùng nhất, One-Click Installer compile bằng cái này.
Để compile extension, thường thì:
- Mở console, chuyển vào thư mục cài Visual C++ 6, chạy VCVARS32.BAT để cập nhật các biến hệ thống.
- Chuyển đến thư mục chứa source code của extension, gõ lệnh
ruby extconf.rbđể tạo Makefile. - Gõ lệnh
nmakeđể bắt đầu compile. - Sau khi extension (file .so) được tạo ra, muốn install file extension này vào thư mục chức thư viện chuẩn của Ruby, thì gõ lệnh
namke install. - Có thư viện mkrf hay hơn mkmf.
Dùng RubyInline hoặc SWIG để tiết kiệm thời gian
Dùng RubyInline hoặc SWIG tiết kiệm được khá nhiều thời gian. SWIG tiện hơn khi chỉ muốn wrap (gói) nguyên xi thư viện C có sẵn thành module để Ruby có thể gọi.
Một số lỗi thường gặp
Số
Lỗi thường gặp đối với số là chỉ dùng NUM2INT và INT2NUM khi chuyển đổi. Đối với số, dùng những hàm chuyển đổi sau:
- 32bit: NUM2INT/INT2NUM, NUM2UINT/UINT2NUM
- 64bit: NUM2LL/LL2NUM, NUM2ULL/ULL2NUM
string
Có một số macro để đổi từ chuỗi kiểu VALUE sang kiểu char *, như StringValuePtr hoặc RSTRING->ptr. Xin xem trong ruby.h. Tuy nhiên các macro này không hoạt động với nil. Do đó, hàm sau khá tiện:
char*MyStringValuePtr(VALUE obj) {*
if (NIL_P(obj))
return NULL;
Check_Type(obj, T_STRING);
return (char) (RSTRING(obj)->ptr);
}
struct
Data_Wrap_Struct(class_muon_gan_cho_struct, ...)
Thường không cần mark, đôi khi không cần nhờ Ruby free giùm (ta sẽ tự free, ví dụ đối với danh sách liên kết) Data_Wrap_Struct(..., NULL, NULL, ...)
Data_Get_Struct không hoạt động với nil, dùng macro sau khá tiện:
#define My_Data_Get_Struct(obj, type, sval) do {\
if (TYPE(obj) == T_NIL)\
sval = NULL;\
else {\
Check_Type(obj, T_DATA);\
sval = (type *) DATA_PTR(obj);\
}\
} while (0)
array
Chậm hơn nhưng an toàn hơn.
ret = rb_ary_new2(n);
/*
RARRAY(ret)->len = n;
for (i = 0; i < n; i++) {
RARRAY(ret)->ptr[i] = INT2FIX(a[i]);
}
*/
// Slower, but more garbage-collector-friendly because if the memory is
// not enough, Ruby will try to free some space.
for (i = 0; i < n; i++) {
rb_ary_push(ret, INT2FIX(a[i]));
}
172