Ruby Language
C estensioni
Ricerca…
La tua prima estensione
Le estensioni C sono composte da due parti generali:
- Il codice C stesso.
- Il file di configurazione dell'estensione.
Per iniziare con la prima estensione, inserisci quanto segue in un file chiamato extconf.rb
:
require 'mkmf'
create_makefile('hello_c')
Un paio di cose da sottolineare:
Innanzitutto, il nome hello_c
è ciò che verrà chiamato l'output dell'estensione compilata. Sarà quello che usi in congiunzione con require
.
In secondo luogo, il file extconf.rb
può effettivamente essere chiamato qualsiasi cosa, è solo tradizionalmente ciò che viene usato per costruire gemme che hanno codice nativo, il file che sta per compilare l'estensione è il Makefile generato durante l'esecuzione di ruby extconf.rb
. Il Makefile predefinito che viene generato compila tutti i file .c
nella directory corrente.
Metti quanto segue in un file chiamato hello.c
ed esegui 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);
}
Una ripartizione del codice:
Il nome Init_hello_c
deve corrispondere al nome definito nel file extconf.rb
, altrimenti quando si carica dinamicamente l'estensione, Ruby non sarà in grado di trovare il simbolo per eseguire il bootstrap dell'estensione.
La chiamata a rb_define_module
sta creando un modulo Ruby di nome HelloC
che andrà a trovare il nostro spazio dei nomi sotto le funzioni C.
Infine, la chiamata a rb_define_singleton_method
rende un metodo a livello di modulo legato direttamente al modulo HelloC
che possiamo richiamare da ruby con HelloC.world
.
Dopo aver compilato l'estensione con la chiamata per make
in make
che possiamo eseguire il codice nella nostra estensione C.
Accendi una console!
irb(main):001:0> require './hello_c'
=> true
irb(main):002:0> HelloC.world
Hello World!
=> nil
Lavorare con C Structs
Per poter lavorare con le strutture C come oggetti Ruby, è necessario Data_Wrap_Struct
con chiamate a Data_Wrap_Struct
e Data_Get_Struct
.
Data_Wrap_Struct
una struttura dati C in un oggetto Ruby. Ci vuole un puntatore alla struttura dei dati, insieme ad alcuni riferimenti alle funzioni di callback e restituisce un VALORE. La macro Data_Get_Struct
accetta VALUE e restituisce un puntatore alla struttura dati C.
Ecco un semplice esempio:
#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);
}
E il file extconf.rb
:
require 'mkmf'
create_makefile('example')
Dopo aver compilato l'estensione:
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
Scrittura Inline C - RubyInLine
RubyInline è un framework che ti consente di incorporare altre lingue all'interno del tuo codice Ruby. Definisce il metodo # inline del modulo, che restituisce un oggetto costruttore. Si passa al builder una stringa contenente codice scritto in una lingua diversa da Ruby e il builder lo trasforma in qualcosa che è possibile chiamare da Ruby.
Quando viene fornito il codice C o C ++ (le due lingue supportate nell'installazione predefinita di RubyInline), gli oggetti del builder scrivono una piccola estensione sul disco, la compila e la carica. Non si ha a che fare con la compilazione da soli, ma è possibile vedere il codice generato e le estensioni compilate nella sottodirectory .ruby_inline della propria directory home.
Incorpora il codice C direttamente nel tuo programma Ruby:
- RubyInline (disponibile come gemma di rubino ) crea automaticamente un'estensione
RubyInline non funzionerà da dentro 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
La funzione C copy_file
esiste ora come metodo di istanza di Copier
:
open('source.txt', 'w') { |f| f << 'Some text.' }
Copier.new.copy_file('source.txt', 'dest.txt')
puts open('dest.txt') { |f| f.read }