В прошлой части мы рассмотрели библиотеку RubyInline, которая позваляет делать вставки C кода прямо в Ruby методы. Ее очень удобно использовать если нам нужно ускорить несколько критичных методов. Но если нам нужно реализовать C библиотеку и использовать в Ruby, или сделать обертку для уже существующей, то нам на помощь придет C API для создания расширений.
Ruby C API предоставляет нам средства написания Ruby кода с помощью C. Звучит немного странно, но давайте рассмотримм пример.
Ruby код:
Аналогичный C код:
Тривиальный пример — создание класса с одним методом. Теперь рассмотрим подробнее C код.
С помощью функции rb_define_class мы создаем класс Test наследник класса Object. Затем с помощью функции rb_define_method мы создаем метод test класса Test, и в качестве реализации этого метода передаем функцию test_method. Последний параметр означает что наш метод не имеет аргументов.
В примере везде используется тип данных VALUE. Любой Ruby объект, будь то строка, число, класс, модуль и т.п., в C имеет тип VALUE.
Ruby C API предоставляет набор макросов и функции для конвертации типов данных Ruby в C и наоборот.
Вот некоторые из них(из Ruby в С):
"
Из С в Ruby:
Модули создаются аналогично классам:
Теперь о методах.
Есть несколько функций для создания методов. Вот некоторые из них:
Функция с реализацией метода должна быть следующего вида:
self, это указатель на объект, он присутствует всегда, даже если у метода нет аргументов.
В зависимости от значения аргумента argc, сигнатура может меняться. Отрицательные значения argc используются для определения метода с переменным числом аргументов.
Если argc равен -1:
argc — количество аргументов, *argv — массив аргументов.
Если argc равен -1:
args — Ruby массив с аргументами.
Функция Init_test() вызывается при инициализации нашего расширения, в ней мы как раз создаем классы и модули.
Теперь, после рассмотрения основных возможностей Ruby C API, давайте попробуем создать собственное расширение. Я решил не придумывать искусственных примеров и проиллюстрировать создание расширения с помощью обертки для какой-нибудь существующей C библиотеки. Посли долгих поисков(почти для всех популярных библиотек уже есть обертки) выбор пал на libdecodeqr, библиотеку для распознавания QR кодов. Простой пример для работы с ней в С можно посмотреть тут qrtest.c
Наше расширение будет очень простым, класс QRDecoder со статическим методом decode.
Метод decode в качестве аргумента берет имя файла с QR кодом, и возвращает строку там закодированную.
Пример использования (сравните с кодом в С:)):
Задача ясна, приступим к реализации.
Что нам потребуется:
1. пакет ruby1.8-dev для сборки расширений.
2. библиотека libdecodeqr.
Начнем с инициализации расширения:
Все просто, создаем класс QRDecoder и статический метод decode.
Теперь реализация функции decode:
Наше расширение готово! Код целиком можно посмотреть тут decodqr.c
Теперь нам нужно собрать наше расширение. Для этого мы используем стандартную Ruby библиотеку mkmf, с помощью которой мы сгенерируем Make файл.
Пример, файл extconf.rb:
И результат его выполнения:
А теперь стандартные:
и наше расширение готово к использованию.
Таким образом, если у вас есть С код, или хотите использовать С библиотеку, или вы хотите ускорить Ruby, вы можете просто сделать расширение и использовать его в своих Ruby программах.
Чего я не коснулся в этой статье:
1. Работа со строками и массивами
2. Сборщик мусора
3. Создание оберток для С структур
Об этом вы можете почитать в приведенных ссылках:
1. Файл README.EXT, который поставляеться с Ruby дистрибутивом.
2. Глава Extending Ruby в знаменитом pickaxe
3. Этот замечательный блог maxidoors.ru
Для заинтересовавшихся, библиотека libdecodeqr: trac.koka-in.org/libdecodeqr
Осторожно! Там все по японски:) Документацию можно посмотреть прямо в заголовочном файле:
trac.koka-in.org/libdecodeqr/browser/tags/release-0.9.3/src/libdecodeqr/decodeqr.h
В следующей части я расскажу про использование Ruby в качестве скриптового языка в C/C++ приложениях.
Ruby C API предоставляет нам средства написания Ruby кода с помощью C. Звучит немного странно, но давайте рассмотримм пример.
Ruby код:
class Test
def test
#реализация метода
end
end
Аналогичный C код:
VALUE test_method(VALUE self) {
//реализация метода
}
Init_test() {
VALUE cTest = rb_define_class("Test", rb_cObject); //создание класса Test
rb_define_method("test", cTest, test_method, 0); //создание метода test в классе Test
}
Тривиальный пример — создание класса с одним методом. Теперь рассмотрим подробнее C код.
С помощью функции rb_define_class мы создаем класс Test наследник класса Object. Затем с помощью функции rb_define_method мы создаем метод test класса Test, и в качестве реализации этого метода передаем функцию test_method. Последний параметр означает что наш метод не имеет аргументов.
В примере везде используется тип данных VALUE. Любой Ruby объект, будь то строка, число, класс, модуль и т.п., в C имеет тип VALUE.
Ruby C API предоставляет набор макросов и функции для конвертации типов данных Ruby в C и наоборот.
Вот некоторые из них(из Ruby в С):
int NUM2INT(VALUE)
int FIX2INT(VALUE)
double NUM2DBL(VALUE)
char *StringValuePtr(VALUE) Также есть макрос STR2CSTR, но его лучше не использовать, цитата из документации:"
because STR2CSTR() has a risk of a dangling pointer problem in the to_str() impliclit conversion."Из С в Ruby:
VALUE INT2NUM(int)
VALUE INT2FIX(int)
VALUE rb_str_new2(char *)
VALUE rb_float_new(double)
Модули создаются аналогично классам:
VALUE rb_define_module(const char *name)Теперь о методах.
Есть несколько функций для создания методов. Вот некоторые из них:
void rb_define_method(VALUE klass, const char *name, VALUE (*func)(), int argc) void rb_define_private_method(VALUE klass, const char *name, VALUE (*func)(), int argc)Аргументами являются класс, имя метода, ссылка на функцию с реализацией метода, и количество аргументов.
Функция с реализацией метода должна быть следующего вида:
VALUE test(VALUE self, VALUE arg1, VALUE arg2, ...)self, это указатель на объект, он присутствует всегда, даже если у метода нет аргументов.
В зависимости от значения аргумента argc, сигнатура может меняться. Отрицательные значения argc используются для определения метода с переменным числом аргументов.
Если argc равен -1:
VALUE test(int argc, VALUE *argv, VALUE self)argc — количество аргументов, *argv — массив аргументов.
Если argc равен -1:
VALUE test(VALUE args, VALUE self)args — Ruby массив с аргументами.
Функция Init_test() вызывается при инициализации нашего расширения, в ней мы как раз создаем классы и модули.
Теперь, после рассмотрения основных возможностей Ruby C API, давайте попробуем создать собственное расширение. Я решил не придумывать искусственных примеров и проиллюстрировать создание расширения с помощью обертки для какой-нибудь существующей C библиотеки. Посли долгих поисков(почти для всех популярных библиотек уже есть обертки) выбор пал на libdecodeqr, библиотеку для распознавания QR кодов. Простой пример для работы с ней в С можно посмотреть тут qrtest.c
Наше расширение будет очень простым, класс QRDecoder со статическим методом decode.
Метод decode в качестве аргумента берет имя файла с QR кодом, и возвращает строку там закодированную.
Пример использования (сравните с кодом в С:)):
require "decodeqr"
puts QRDecoder.decode "test.png"
Задача ясна, приступим к реализации.
Что нам потребуется:
1. пакет ruby1.8-dev для сборки расширений.
sudo apt-get install ruby1.8-dev2. библиотека libdecodeqr.
sudo apt-get install libdecodeqr libdecodeqr-devНачнем с инициализации расширения:
Init_decodeqr() {
VALUE cQRDecoder = rb_define_class("QRDecoder", rb_cObject);
rb_define_singleton_method(cQRDecoder, "decode", decode, 1);
}
Все просто, создаем класс QRDecoder и статический метод decode.
Теперь реализация функции decode:
VALUE decode(VALUE self, VALUE file) {
char *file_name = StringValuePtr(file); //конвертируем Ruby строку в С
//работа с библиотекой libdecodeqr, более подробно можно посмотреть в С примере
QrDecoderHandle qr = qr_decoder_open();
IplImage *src = cvLoadImage(file_name, 0);
qr_decoder_decode_image(qr, src, DEFAULT_ADAPTIVE_TH_SIZE, DEFAULT_ADAPTIVE_TH_DELTA);
QrCodeHeader *qrh = calloc(sizeof(QrCodeHeader), 1);
qr_decoder_get_header(qr, qrh);
char *buf = calloc(qrh->byte_size+1, 1);
qr_decoder_get_body(qr, (unsigned char*)buf, qrh->byte_size+1);
qr_decoder_close(qr);
return rb_str_new2(buf); //возвращаем Ruby строку
}
Наше расширение готово! Код целиком можно посмотреть тут decodqr.c
Теперь нам нужно собрать наше расширение. Для этого мы используем стандартную Ruby библиотеку mkmf, с помощью которой мы сгенерируем Make файл.
Пример, файл extconf.rb:
require 'mkmf'
if have_library("cv") and #если есть библиотека "cv"
have_library("decodeqr") and #и библиотека "decodeqr"
have_library("highgui") and #а также "highgui"
find_header("highgui.h", "/usr/include/opencv/") then # и нашли заголовочный файл "highgui.h"
create_makefile("decodeqr") #тогда создаем Make файл
else
puts "oops..." #а иначе не создаем
end
И результат его выполнения:
$ ruby extconf.rb
checking for main() in -lcv... yes
checking for main() in -ldecodeqr... yes
checking for main() in -lhighgui... yes
checking for highgui.h in /usr/include/opencv/... yes
creating Makefile
А теперь стандартные:
make
sudo make install
и наше расширение готово к использованию.
Таким образом, если у вас есть С код, или хотите использовать С библиотеку, или вы хотите ускорить Ruby, вы можете просто сделать расширение и использовать его в своих Ruby программах.
Чего я не коснулся в этой статье:
1. Работа со строками и массивами
2. Сборщик мусора
3. Создание оберток для С структур
Об этом вы можете почитать в приведенных ссылках:
1. Файл README.EXT, который поставляеться с Ruby дистрибутивом.
2. Глава Extending Ruby в знаменитом pickaxe
3. Этот замечательный блог maxidoors.ru
Для заинтересовавшихся, библиотека libdecodeqr: trac.koka-in.org/libdecodeqr
Осторожно! Там все по японски:) Документацию можно посмотреть прямо в заголовочном файле:
trac.koka-in.org/libdecodeqr/browser/tags/release-0.9.3/src/libdecodeqr/decodeqr.h
В следующей части я расскажу про использование Ruby в качестве скриптового языка в C/C++ приложениях.