Ricerca…


La tua prima estensione

Le estensioni C sono composte da due parti generali:

  1. Il codice C stesso.
  2. 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 }


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow