Buscar..


Tu primera extension

Las extensiones en C se componen de dos piezas generales:

  1. El código C en sí.
  2. El archivo de configuración de extensión.

Para comenzar con su primera extensión, coloque lo siguiente en un archivo llamado extconf.rb :

require 'mkmf'

create_makefile('hello_c')

Un par de cosas para señalar:

Primero, el nombre hello_c es el nombre de la salida de la extensión compilada. Será lo que uses junto con el require .

En segundo lugar, el archivo extconf.rb puede llamarse de cualquier manera, simplemente es lo que tradicionalmente se usa para construir gemas que tienen código nativo, el archivo que realmente compilará la extensión es el Makefile generado al ejecutar ruby extconf.rb . El archivo Makefile predeterminado que se genera compila todos los archivos .c en el directorio actual.

Coloque lo siguiente en un archivo llamado hello.c y ejecute 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);
}

Un desglose del código:

El nombre Init_hello_c debe coincidir con el nombre definido en su archivo extconf.rb , de lo contrario, al cargar dinámicamente la extensión, Ruby no podrá encontrar el símbolo para iniciar su extensión.

La llamada a rb_define_module está creando un módulo Ruby llamado HelloC cual vamos a poner un espacio de nombre bajo nuestras funciones en C.

Finalmente, la llamada a rb_define_singleton_method hace un método de nivel de módulo vinculado directamente al módulo HelloC que podemos invocar desde ruby ​​con HelloC.world .

Después de haber compilado la extensión con la llamada para make que podamos ejecutar el código en nuestra extensión C.

¡Enciende una consola!

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

Trabajando con C Structs

Para poder trabajar con C structs como objetos Ruby, necesita envolverlos con llamadas a Data_Wrap_Struct y Data_Get_Struct .

Data_Wrap_Struct envuelve una estructura de datos C en un objeto Ruby. Toma un puntero a su estructura de datos, junto con algunos punteros a las funciones de devolución de llamada, y devuelve un valor. La macro Data_Get_Struct toma ese VALOR y le devuelve un puntero a su estructura de datos C.

Aquí hay un ejemplo simple:

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

Y el extconf.rb :

require 'mkmf'

create_makefile('example')

Después de compilar la extensión:

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

Escritura en línea C - Ruby en línea

RubyInline es un marco que te permite incrustar otros idiomas dentro de tu código de Ruby. Define el método en línea del Módulo #, que devuelve un objeto constructor. Le pasas al constructor una cadena que contiene un código escrito en un idioma que no es Ruby, y el constructor lo transforma en algo que puedes llamar desde Ruby.

Cuando se le da un código C o C ++ (los dos idiomas admitidos en la instalación predeterminada de RubyInline), los objetos del constructor escriben una pequeña extensión en el disco, la compilan y la cargan. No tiene que lidiar con la compilación usted mismo, pero puede ver el código generado y las extensiones compiladas en el subdirectorio .ruby_inline de su directorio de inicio.

Incruste el código C directamente en su programa Ruby:

  • RubyInline (disponible como la gema rubyinline ) crea una extensión automáticamente

RubyInline no funcionará desde dentro de 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 función C copy_file ahora existe como un método de instancia de 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
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow