Поиск…


Ваше первое расширение

C состоят из двух общих частей:

  1. Сам C-код.
  2. Файл конфигурации расширения.

Чтобы начать работу с вашим первым расширением, добавьте следующее в файл с именем 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 }


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow