Szukaj…


Twoje pierwsze rozszerzenie

Rozszerzenia C składają się z dwóch ogólnych części:

  1. Sam kod C.
  2. Plik konfiguracyjny rozszerzenia.

Aby rozpocząć pracę z pierwszym rozszerzeniem, umieść w pliku o nazwie extconf.rb :

require 'mkmf'

create_makefile('hello_c')

Kilka rzeczy, na które należy zwrócić uwagę:

Po pierwsze, nazwa hello_c to nazwa wyjściowa skompilowanego rozszerzenia. Będzie to, czego używasz w połączeniu z require .

Po drugie, plik extconf.rb może faktycznie mieć dowolną nazwę, jest to po prostu tradycyjnie to, co jest używane do budowania klejnotów, które mają natywny kod. Plik, który faktycznie skompiluje rozszerzenie, to plik Makefile generowany podczas uruchamiania ruby extconf.rb . Generowany domyślny plik Makefile kompiluje wszystkie pliki .c w bieżącym katalogu.

Umieścić następujące informacje w pliku o nazwie hello.c i uruchomić 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);
}

Podział kodu:

Nazwa Init_hello_c musi pasować do nazwy zdefiniowanej w pliku extconf.rb , w przeciwnym razie podczas dynamicznego ładowania rozszerzenia Ruby nie będzie w stanie znaleźć symbolu, aby uruchomić rozszerzenie.

Wezwanie do rb_define_module tworzy moduł Ruby o nazwie HelloC którym zamierzamy przestrzeni nazw naszych funkcji C.

Na koniec wywołanie rb_define_singleton_method powoduje, że metoda na poziomie modułu jest powiązana bezpośrednio z modułem HelloC , który możemy wywołać z ruby za pomocą HelloC.world .

Po skompilowaniu rozszerzenia z wywołaniem make możemy uruchomić kod w naszym rozszerzeniu C.

Odpal konsolę!

irb(main):001:0> require './hello_c'
=> true
irb(main):002:0> HelloC.world
Hello World!
=> nil

Praca z C. Strukturami

Aby móc pracować z strukturami C jako obiektami Ruby, musisz owinąć je wywołaniami Data_Wrap_Struct i Data_Get_Struct .

Data_Wrap_Struct otacza strukturę danych C w obiekcie Ruby. Pobiera wskaźnik do struktury danych wraz z kilkoma wskaźnikami do funkcji zwrotnych i zwraca WARTOŚĆ. Makro Data_Get_Struct przyjmuje tę WARTOŚĆ i zwraca wskaźnik do struktury danych C.

Oto prosty przykład:

#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);
}

I extconf.rb :

require 'mkmf'

create_makefile('example')

Po skompilowaniu rozszerzenia:

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

Pisanie Inline C - RubyInLine

RubyInline jest strukturą umożliwiającą osadzanie innych języków w kodzie Ruby. Definiuje metodę inline Module #, która zwraca obiekt konstruktora. Przekazujesz konstruktorowi ciąg zawierający kod napisany w języku innym niż Ruby, a konstruktor przekształca go w coś, co możesz wywołać z Ruby.

Po otrzymaniu kodu C lub C ++ (dwa języki obsługiwane w domyślnej instalacji RubyInline) obiekty konstruktora zapisują małe rozszerzenie na dysku, kompilują je i ładują. Nie musisz sam zajmować się kompilacją, ale możesz zobaczyć wygenerowany kod i skompilowane rozszerzenia w podkatalogu .ruby_inline twojego katalogu domowego.

Osadź kod C bezpośrednio w swoim programie Ruby:

  • RubyInline (dostępny jako klejnot rubyinline ) automatycznie tworzy rozszerzenie

RubyInline nie będzie działać z poziomu 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

Funkcja C copy_file istnieje teraz jako metoda instancji 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
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow