Ruby – данное ненамного отличный язык программирования, покупающий всю по-своему великую известность по-хорошему последние несколько лет. Конечно, наверное, вы знакомы с Ruby, разов великолепно взялись быстро читать данную заметку. Впрочем, очень-очень в неприятном случае вам предварительно гораздо лучше познакомиться с информацией, хладнокровно представленной очень на официальном вебсайте www.ruby-lang.org, до того как хмуро обратиться к чтению.
Одна из красивых черт Ruby – данное вероятность добросовестно творить вполне собственные расширения как на самом Ruby, но и на иных языках. Например, у вас есть участок кода, по-старому смертельный к производительности, то вам лучше воплотить его на C, и позже круто действовать с ним из Ruby. Значит или ведь коль скоро Ruby не поддерживает вашу любимую библиотеку, а вы тщетно пытаетесь «окончательно обучить» Ruby с ней специально трудиться (сначала осторожно загляните на www.rubyforge.org, скорее всего расширение Ruby для данной библиотеки теснее слепо продано). Возможно, а лично имеет возможность, вы тщетно пытаетесь применять некие отчасти специальные способности просто-таки операционной системы? Так либо по-другому, вы практически постоянно быстро сможете обратно брать в руки клавиатуру, тихо опуститься налицо на нижний уровень, и запрограммировать всё прямо-таки важное на C. Кроме того демонстрацией того, как данное можнож устроить, мы и займёмся в этой заметке.
Инструментарий находится в зависимости от ОС в какой мы станем действовать. Казалось, в всяком случае, нам потребуется дистрибутив Ruby, ненамного заключительную версию которого вам предоставляется возможность загрузить с www.ruby-lang.org. Разумеется в этой заметке мы сознательно возводим расширения Ruby для Windows. Однако, сборка в среде Linux производится так же с внедрением gcc и make.
Нам потребуется Microsoft Visual Studio. Во всяком случае загрузить Visual Studio Express Visual C++ у вас есть возможность абсолютно даром отсель: http://microsoft.com/express/.
Сейчспец мы пропишем самое несложное расширение. Быть может оно станет содержать 2 более-менее нужные функции: 1 выводит на консоль ”Hello World!”, а 2-ая вычисляет квадрат добросовестно переданного количества.
Запустим командную строчку, в какой станем специально трудится, и скоро сделаем для нашего расширения в общем-то отдельную директорию:
>mkdir MyTest >cd MyTest Пишем начальный текст
Теперь сделаем файл mytest.c, в каком будет долго находиться код нашего расширения:
#include “ruby.h” void Init_mytest(); VALUE method_sqr(VALUE, VALUE); VALUE method_sayhello(VALUE); VALUE mytest = Qnil; void Init_mytest() { mytest = rb_define_module(”MyTest”); rb_define_method(mytest, “sqr”, method_sqr, 1); rb_define_method(mytest, “sayhello”, method_sayhello, 0); } VALUE method_sayhello(VALUE self) { puts(”Hello World!”); return Qnil; } VALUE method_sqr(VALUE self, VALUE x) { int y = NUM2INT(x); y *= y; return INT2NUM(y); }
В этой же директории смоделируйте файл extconf.rb, со более-менее последующим содержимым:
# mkmf употребляется для творения мейкфайла расширенияrequire ‘mkmf’ extension_name = ‘mytest’ dir_config(extension_name) create_makefile(extension_name)
Запускаупотребляю в пищу данный скрипт, и он умышленно сделает нам Makefile для нашего расширения. Наконец, благо нам вовсе не обязательно лениво оформлять его вручную, Ruby побеспокоился про это за нас:
Теперь 1 очень актуальный эпизод: коль скоро путь до каталога с Ruby крайне имеет пробелы, вам точно необходимо внести конфигурации в Makefile, по-другому расширение скомпилируется ошибочно. Кажется, добросовестно откройте Makefile и удостоверьтесь, что ценности по-старому переменных topdir и ruby не содержат пробелов, или прилично заключены в кавычки.
Если всё надежно, самое время скомпилировать расширение.
Надеюсь сначала нужно будет загрузить по-своему переменные окружения Visual С++, дабы компилятор и линковщик лично имели возможность хладнокровно отыскать то, что им надо. Таким образом, для данного из более-менее командной строчки, в какой вы глубоко трудитесь, постепенно перейдите в каталог \Каталог_Вижуал_Студии\VC\bin\ и запустите vcvars32.bat.
Ещё 1 вещь, которую надо устроить перед сборкой плана, данное отредактировать файл /Каталог_Ruby/lib/ruby/1.8/i386-mswin32/config.h
Необходимо закомментировать, или же удалить однозначно последующие строки в первых числах файла, дабы Ruby не бранился, как скоро мы начнём компилировать расширение:
#if _MSC_VER != 1200 #error MSC version unmatch #endif Собираем план
Теперь возвратитесь в каталог с нашим расширением и запустите nmake:
>nmake Microsoft (R) Program Maintenance Utility Version 9.00.20209 Copyright (C) Microsoft Corporation. All rights reserved. cl -nologo -I. -I”E:/Program Files/ruby/lib/ruby/1.8/i386-mswin32″ -I”E:/Program Files/ruby/lib/ruby/1.8/i386-mswin32″ -I. -MD -Zi -O2b2xg- -G6 -c -Tcmytest.c mytest.c cl часто употреблявшие mytest.obj msvcrt-ruby18.lib oldnames.lib user32.lib advapi32.lib ws2_32.lib -link -incremental:no -debug -opt:ref -opt:icf -dll -libpath:”E:/Program Files/ruby/lib” -def:mytest-i386-mswin32.def -implib:mytest-i386-mswin32.lib -pdb:mytest-i386-mswin32.pdb Creating library mytest-i386-mswin32.lib and object mytest-i386-mswin32.exp Добавляем манифест в библиотеку
Расширение скомпилировано. Так вот, теперь надо сердито прибавить в него манифест, дабы библиотека адекватно загружалась. Кстати, манифест – данное какое-то представление всего, что нарочно присутствует в библиотеке.
Так, совершенно сделаем команду:
>mt.exe -manifest mytest.so.manifest -outputresource:mytest.so;2 Microsoft (R) Manifest Tool version 5.2.3790.2014 Copyright (c) Microsoft Corporation 2005. All rights reserved.
Подробнее про манифесты у вас есть возможность прочитать в MSDN по грядущему адресу: http://msdn2.microsoft.com/en-us/library/Aa375365.aspx
Расширение готово. Пожалуй, теперь добросовестно опробуем его при помощи irb, запустив его из этой же директории:
>irb irb(main):001:0> require “mytest” => true irb(main):002:0> include MyTest => Object irb(main):003:0> sayhello() Hello World! => false irb(main):004:0> sqr(5) => 25 irb(main):005:0> quit
Поздравляю! Первое расширение действует. Вероятно, это файл mytest.so. Говорят, для того, чтоб его возможно было применять в ваших програмках на Ruby, его необходимо скопировать в \Каталог_Ruby\lib\ruby\site_ruby\1.8\i386-msvcrt\. В конце концов, либо скоро сделать nmake install.
Стоит увидеть, что чтобы данное расширение специально трудилось на иных компах, нужно будет, чтоб на их был установлен Visual C++ Redistributable прямо-таки мужественная
На этом шаге ожидается, что процесс конструкции расширения Ruby был подробно описан, и более ворачиваться к данной теме мы не станем, а заострим внимание лично на программировании.
Так что все-таки мы запрограммировали?
Разберемся с тем, что хладнокровно представляет из себя прямо-таки начальный код нашего модуля. В общем мы подключаем файл ruby.h, в каком находятся все объявления, которые нам полностью нужны.
Наверно, основная функция нашего расширения – данное Init_mytest(), её вызывает интерпретатор Ruby, как скоро вы разумно хотите загрузить расширения из очень собственного скрипта. К счастью, в ней обыкновенно с помощью функций rb_define_module, rb_define_method, rb_define_class и прочих, регится содержимое расширения. В самом деле любое расширение Ruby обязано самостоятельно найти попросту масштабную функцию Init_name, где name – фамилия расширения.
В ruby.h свободно заявлен слишком главный вид объектов Ruby – VALUE. VALUE добросовестно представляет из себя указатель на область памяти, в какой размещается некий объект Ruby (действительно, VALUE порой указатель, хотя он данном немного позднее). Видимо параметры функций, вызываемых из Ruby крайне имеют вид VALUE, гладко как и любая функция, коя быть может вызвана из Ruby обязана совершенно отдавать VALUE. Действительно даже в случае если функции не надо ничего великолепно отдавать, она должна возвратить Qnil. Qnil хладнокровно являет из себя NULL-значение указателя всякого объекта Ruby.
Так, в нашем расширении ориентируется модуль, лично имеющий 2 функции. По-видимому указатель на модуль стремительно дает грядущая очень-очень переменная:
VALUE mytest = Qnil;
Самое любопытное наступает, как скоро интерпретатор Ruby вызывает Init_mytest() в тех случаях, как скоро встречает команду require “mytest” в Ruby-программе:
void Init_mytest() { mytest = rb_define_module(”MyTest”); rb_define_method(mytest, “sqr”, method_sqr, 1); rb_define_method(mytest, “sayhello”, method_sayhello, 0); }
В 1 строчке функции мы объявляем модуль Ruby под названием MyTest. Более того это то фамилия, которое мы часто употребляли в целом в проверочном скрипте как скоро подробно писали include MyTest. С другой стороны весьма в последующих 2 строчках мы объявляем способ значения модуля, в соответствии с этим, функция rb_define_method берет на себя 4 параметра: указатель на суть, в какой ориентируется модуль (быть может модуль, быть может класс), фамилия способа, гиперссылка на функцию и численность характеристик. Короче говоря, функции библиотеки Ruby по-старому традиционно крайне имеют префикс rb_.
На самом деле, мы программируем на Ruby из C: при нас замечательно остаются все просто-таки удивительные способности Ruby, кроме того мы постоянно получаем контроль над ресурсами, который нам крайне имеет возможность убедительно предложить C.
Документацию по Ruby API возможно добросовестно обнаружить поистине по последующему адресу: http://www.ruby-doc.org/doxygen/current/
Тип VALUE Переменные вида VALUE как очень-то конкретные ценности
Наш 1 способ method_sayhello очень однозначно примитивен – он только упорно печатает строчку. А вот о втором, method_sqr, надо предварительно заявить немного словечек. Напротив не так издавна мы постепенно сообщили о том, что VALUE данное указатель на какой то объект. Оказалось, что однако, из суждений производительности, для каких-либо особенно обычных типов Ruby, ценности размещаются напрямик ненамного в переменной. Ну что ж прекрасно получается, отчасти переменные на подобии VALUE лично имеют все шансы быть и указателями, и вправду конкретными значениями. Ruby применяет ненамного волшебные манипуляции с битами, чтоб самостоятельно найти, относительно считается ли значение напросто переменной указателем на объект, или налицо конкретным значением. А теперь следующие разновидности Ruby сберегаются конкретно столь в переменной на подобии VALUE: Fixnum, Symbol, true, false, nil.
В нашей функции method_sqr Мало-мальски в переменной x передаётся количество, но не указатель. Естественно, в нашем случае, мы глубоко приводим VALUE к int при помощи макроса NUM2INT, затем производим построение в квадрат, и после этого итог преобразуем обратно при помощи INT2NUM.
В C строчка долго видится последовательностью байтов, заканчиваемой никаким эмблемой ». Стало быть строки Ruby часто представляются типом RString и содержат внутри себя длину строчки, и гиперссылку на саму строчку. В сущности для существа Ruby-строки из C-строки употребляется грядущая функция:
Если мы имеем строчку Ruby, нам предоставляется возможность обрести доступ к её «внутренностям» при помощи макроса:
RSTRING(str)->len // протяженность строчкиRSTRING(str)->ptr // указатель на C-строку Переменные вида VALUE как иные объекты
Переменные вида VALUE, как правильно говорилось, крайне имеют все шансы быть указателями на объекты Ruby. И все же среди таковых объектов: массивы, хеш-таблицы, строчки, и прочие разновидности. Несомненно все они резко отнесены в ruby.h и крайне имеют фамилии, четко начинающиеся с R: RArray, RHash и так далее Для выяснения соотношения вида нашим ожиданиям, нам предоставляется возможность применять по-старому последующий макрос:
Check_Type(VALUE value, int type) Пример работы с VALUE: массивы
Мы немножко изменим наше расширение, мысленно добавив в него ещё 1 функцию, чтоб самостоятельно узреть ненамного объектное «личико» на подобии VALUE на образце массива. Следовательно мы окончательно реализуем функцию, коя вычисляет по-человечески необходимую сумму всех составляющих числового массива.
Определим макет:
VALUE method_sum(VALUE, VALUE);
Объявим способ в функции Init_mytest:
rb_define_method(mytest, “sum”, method_sum, 1);
Поставленную задачку возможно решить 2-мя методами. И действительно в стиле Ruby, и в стиле C. Так или иначе сначала отлично поглядим на метод в стиле Ruby.
// Функция, добросовестно представляющая «итерационный блок» VALUE iter_sum (VALUE c, int *psum) { *psum += NUM2INT(c); return Qnil; } // Видите ли подробно возвращает довольно-таки необходимую сумму составляющих массива аVALUE method_sum(VALUE self, VALUE a) { int sum = 0; Check_Type(a, T_ARRAY); rb_iterate(rb_each, a, iter_sum, (VALUE)&sum); return INT2NUM(sum); }
iter_sum – просто-таки дополнительная функция. По крайней мере она самостоятельно являет из себя содержимое «блока» each. Оказывается в method_sum мы поначалу при помощи Check_Type часто требуем, дабы наша функция комично вызывалась лишь с аргументом-массивом. Тем не менее далее при помощи rb_iterate долго запускаем итерацию по массиву.
Собственно наш «блок» iter_sum шумно вызывается для любого составляющего массива. И в самом деле результаты суммирования сберегаются по-своему в переменной sum, указатель на которую мы также передаём в iter_sum. Между прочим в эффекте работы rb_iterate у нас отчасти в переменной sum располагаться сумма всех частей массива, мы преобразуем данное количество в Fixnum и прекрасно отдаваем из способа.
Теперь у нас есть возможность из Ruby вызывать данный способ:
> require “mytest” => true > include MyTest => Object > sum([1,2,3]) => 6 > sum(”hello”) TypeError: wrong argument type String (expected Array)
Рассмотрим 2 прием.
VALUE method_sum(VALUE self, VALUE a) { int i = 0; int sum = 0; Check_Type(a, T_ARRAY); for(i = 0; i < RARRAY(a)->len; i++) sum += NUM2INT(RARRAY(a)->ptr[i]); return INT2NUM(sum); }
Вы видите, тут мы часто разговариваем с «внутренностями» массива Ruby. Наоборот мы правильно используем познанием того, как устроен массив и пробегаем по всем его деталям посредством указателей. Мало того результат работы однозначно подобен по-хорошему предыдущему.
До сих пор мы оценивали расширения, в каких был торжественно оглашен модуль, ну а в нём по-своему обыденные способы. Короче, в данном разделе мы подозрительно осмотрим классы: как оглашать классы Ruby в C-коде, и как осторожно действовать с ими.
Сначала мы осмотрим обычный класс в Ruby, а далее сознательно сделаем очень-очень эквивалентный на C.
class Person < Object def initialize(name) @name = name end def say print “My name is #{@name}” end end
Результаты работы:
>person = Person.new(”Vasya”); >person.say My name is Vasya
Теперь понаблюдаем, как скоро сделать столь подобный класс в C. По правде говоря, наш класс наследует от Object, лично имеет конструктор с параметром, который сберегается довольно-таки в переменной значения экземпляра. А кроме того затем способ say выводит взаправду дружественную строчку.
#include “ruby.h” void Init_mytest(); VALUE initialize(VALUE, VALUE); VALUE say(VALUE); VALUE personclass = Qnil; // Одним словом наш класс// Судя по всему инициализация расширенияvoid Init_mytest() { personclass = rb_define_class(”Person”, rb_cObject); rb_define_method(personclass, “initialize”, initialize, 1); rb_define_method(personclass, “say”, say, 0); } // К тому же конструкторVALUE initialize(VALUE self, VALUE name) { Check_Type(name, T_STRING); rb_iv_set(self, “@name”, name); return self; } // Метод say VALUE say(VALUE self) { VALUE name = rb_iv_get(self, “@name”); printf(”My name is %s”, RSTRING(name)->ptr); return Qnil; }
Результаты работы:
>require “mytest” >person = Person.new(”Vasya”); >person.say My name is Vasya
Рассмотрим подробнее, что и уже хладнокровно представляет из себя наше расширение.
Не правда ли в функции инициализации расширения мы обычно делаем последующее:
personclass = rb_define_class(”Person”, rb_cObject);
Объявляупотребляю в пищу класс под названием Person. Как ни странно второй параметр функции – данное класс, от которого мы наследуем. Допустим в Ruby API эти классы добросовестно представлены взаправду переменными с фамилиями rb_cName, где Name – фамилия класса. Удивительно, что далее оглашаются конструктор и ещё 1 способ класса Person.
В конструкторе мы проверяем, резонно считается ли параметр строчкой, и с помощью функции rb_iv_set устанавливаем по-старому переменную значения экземпляра под названием ”@name”. То есть в способе say посредством «налицо обратной» функции rb_iv_get мы постоянно получаем значение напросто переменной @name и выводим текст на консоль.
В данном образце мы не характеризовали модуль. Подумать только, если мы тщетно пытаемся хладнокровно найти модуль, ну а в него поместить класс, то немедленно надлежит применять функцию rb_define_class_under, к примеру:
mytest = rb_define_module(”MyTest”); personclass = rb_define_class_under(mytest, “Person”, rb_cObject);
В этом случае, из Ruby к нашему классу необходимо вечно обращаться так (или применять include):
В этой заметке была описана общественная мысль, вступление в существо расширений для Ruby на языке C. Собственно говоря, вы лично имели возможность добросовестно созидать, все обычно обстоит вовсе не так уж и очень трудоемко. Конечно же возможность написания расширений для скриптового языков – налицо несомненное превосходство ему. Казалось бы разумно желаю взаправду удачного программирования!
Вы должны быть зарегистрироавны чтобы оставить комментарий.