Ruby Language
C Расширения
Поиск…
Ваше первое расширение
C состоят из двух общих частей:
- Сам C-код.
- Файл конфигурации расширения.
Чтобы начать работу с вашим первым расширением, добавьте следующее в файл с именем extconf.rb
:
require 'mkmf'
create_makefile('hello_c')
Несколько вещей, чтобы указать:
Во-первых, имя hello_c
- это то, что будет hello_c
вывод вашего скомпилированного расширения. Это будет то, что вы используете в сочетании с require
.
Во-вторых, файл extconf.rb
самом деле может быть назван чем угодно, это просто традиционно то, что используется для создания драгоценных камней, имеющих собственный код, файл, который на самом деле собирается скомпилировать расширение, - это Makefile, сгенерированный при запуске ruby extconf.rb
. Созданный по умолчанию Makefile, который сгенерирован, компилирует все файлы .c
в текущем каталоге.
Поместите следующее в файл с именем hello.c
и запустите ruby extconf.rb && make
#include <stdio.h>
#include "ruby.h"
VALUE world(VALUE self) {
printf("Hello World!\n");
return Qnil;
}
// The initialization method for this module
void Init_hello_c() {
VALUE HelloC = rb_define_module("HelloC");
rb_define_singleton_method(HelloC, "world", world, 0);
}
Разбивка кода:
Имя Init_hello_c
должно совпадать с именем, определенным в файле extconf.rb
, иначе при динамической загрузке расширения Ruby не сможет найти символ для загрузки вашего расширения.
Призыв к rb_define_module
создает модуль Ruby с именем HelloC
который мы будем использовать для пространственных имен функций C.
Наконец, вызов rb_define_singleton_method
делает метод уровня модуля привязан непосредственно к модулю HelloC
который мы можем вызывать из ruby с помощью HelloC.world
.
После того , как скомпилирован расширение с вызовом , чтобы make
мы можем запустить код нашего расширения C.
Запустите консоль!
irb(main):001:0> require './hello_c'
=> true
irb(main):002:0> HelloC.world
Hello World!
=> nil
Работа с C Structs
Чтобы иметь возможность работать с C-структурами как объекты Ruby, вам необходимо обернуть их вызовами Data_Wrap_Struct
и Data_Get_Struct
.
Data_Wrap_Struct
обертывает структуру данных C в объекте Ruby. Он берет указатель на вашу структуру данных, а также несколько указателей на функции обратного вызова и возвращает значение VALUE. Макрос Data_Get_Struct
принимает значение VALUE и возвращает указатель на вашу структуру данных C.
Вот простой пример:
#include <stdio.h>
#include <ruby.h>
typedef struct example_struct {
char *name;
} example_struct;
void example_struct_free(example_struct * self) {
if (self->name != NULL) {
free(self->name);
}
ruby_xfree(self);
}
static VALUE rb_example_struct_alloc(VALUE klass) {
return Data_Wrap_Struct(klass, NULL, example_struct_free, ruby_xmalloc(sizeof(example_struct)));
}
static VALUE rb_example_struct_init(VALUE self, VALUE name) {
example_struct* p;
Check_Type(name, T_STRING);
Data_Get_Struct(self, example_struct, p);
p->name = (char *)malloc(RSTRING_LEN(name) + 1);
memcpy(p->name, StringValuePtr(name), RSTRING_LEN(name) + 1);
return self;
}
static VALUE rb_example_struct_name(VALUE self) {
example_struct* p;
Data_Get_Struct(self, example_struct, p);
printf("%s\n", p->name);
return Qnil;
}
void Init_example()
{
VALUE mExample = rb_define_module("Example");
VALUE cStruct = rb_define_class_under(mExample, "Struct", rb_cObject);
rb_define_alloc_func(cStruct, rb_example_struct_alloc);
rb_define_method(cStruct, "initialize", rb_example_struct_init, 1);
rb_define_method(cStruct, "name", rb_example_struct_name, 0);
}
И extconf.rb
:
require 'mkmf'
create_makefile('example')
После компиляции расширения:
irb(main):001:0> require './example'
=> true
irb(main):002:0> test_struct = Example::Struct.new("Test Struct")
=> #<Example::Struct:0x007fc741965068>
irb(main):003:0> test_struct.name
Test Struct
=> nil
Написание Inline C - RubyInLine
RubyInline - это среда, которая позволяет вставлять другие языки в ваш код Ruby. Он определяет встроенный метод Module #, который возвращает объект-строитель. Вы передаете строителю строку, содержащую код, написанный на языке, отличном от Ruby, и строитель преобразует его в нечто, что вы можете вызывать из Ruby.
При задании кода C или C ++ (два языка, поддерживаемых установкой RubyInline по умолчанию) объекты-строители записывают небольшое расширение на диск, компилируют его и загружают. Вам не нужно разбираться с компиляцией самостоятельно, но вы можете видеть сгенерированный код и скомпилированные расширения в подкаталоге .ruby_inline вашего домашнего каталога.
Вставьте код C прямо в свою программу Ruby:
- RubyInline (доступен как драгоценный камень rubyinline ) автоматически создает расширение
RubyInline не будет работать из irb
#!/usr/bin/ruby -w
# copy.rb
require 'rubygems'
require 'inline'
class Copier
inline do |builder|
builder.c <<END
void copy_file(const char *source, const char *dest)
{
FILE *source_f = fopen(source, "r");
if (!source_f)
{
rb_raise(rb_eIOError, "Could not open source : '%s'", source);
}
FILE *dest_f = fopen(dest, "w+");
if (!dest_f)
{
rb_raise(rb_eIOError, "Could not open destination : '%s'", dest);
}
char buffer[1024];
int nread = fread(buffer, 1, 1024, source_f);
while (nread > 0)
{
fwrite(buffer, 1, nread, dest_f);
nread = fread(buffer, 1, 1024, source_f);
}
}
END
end
end
C function copy_file
теперь существует как метод экземпляра Copier
:
open('source.txt', 'w') { |f| f << 'Some text.' }
Copier.new.copy_file('source.txt', 'dest.txt')
puts open('dest.txt') { |f| f.read }