Recherche…


Votre première extension

Les extensions C sont composées de deux pièces générales:

  1. Le code C lui-même.
  2. Le fichier de configuration d'extension.

Pour commencer avec votre première extension, placez-le dans un fichier nommé extconf.rb :

require 'mkmf'

create_makefile('hello_c')

Quelques points à souligner:

D'abord, le nom hello_c est ce que la sortie de votre extension compilée va être nommée. Ce sera ce que vous utiliserez en même temps que require .

Deuxièmement, le fichier extconf.rb peut en fait être nommé n'importe quoi, il est juste traditionnellement utilisé pour construire des gemmes avec du code natif, le fichier qui va en fait compiler l'extension est le Makefile généré lors de l'exécution de ruby extconf.rb . Le fichier .c par défaut généré compile tous les fichiers .c répertoire en cours.

Placez ce qui suit dans un fichier nommé hello.c et exécutez 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);
}

Une ventilation du code:

Le nom Init_hello_c doit correspondre au nom défini dans votre fichier extconf.rb , sinon, lors du chargement dynamique de l'extension, Ruby ne pourra pas trouver le symbole permettant d'amorcer votre extension.

L'appel à rb_define_module est en rb_define_module créer un module Ruby nommé HelloC lequel nous allons HelloC nos fonctions C sous l'espace de noms.

Enfin, l'appel à rb_define_singleton_method crée une méthode de niveau module directement liée au module HelloC que nous pouvons appeler depuis ruby ​​avec HelloC.world .

Après avoir compilé l'extension avec l'appel à make nous pouvons exécuter le code dans notre extension C.

Allumez une console!

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

Travailler avec des structures C

Pour pouvoir travailler avec des structures C en tant qu'objets Ruby, vous devez les envelopper avec des appels à Data_Wrap_Struct et Data_Get_Struct .

Data_Wrap_Struct une structure de données C dans un objet Ruby. Il prend un pointeur sur votre structure de données, ainsi que quelques pointeurs vers des fonctions de rappel, et renvoie une valeur. La macro Data_Get_Struct prend cette valeur et vous renvoie un pointeur sur votre structure de données C.

Voici un exemple 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);
}

Et l' extconf.rb :

require 'mkmf'

create_makefile('example')

Après avoir compilé l'extension:

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

Écrire en ligne C - RubyInLine

RubyInline est un framework qui vous permet d'intégrer d'autres langages dans votre code Ruby. Il définit la méthode inline du module #, qui renvoie un objet générateur. Vous transmettez au générateur une chaîne contenant du code écrit dans une langue autre que Ruby, et le générateur la transforme en quelque chose que vous pouvez appeler depuis Ruby.

Lorsqu'il est donné du code C ou C ++ (les deux langages pris en charge dans l'installation par défaut de RubyInline), les objets du générateur écrivent une petite extension sur le disque, la compilent et la chargent. Vous n'avez pas à gérer la compilation vous-même, mais vous pouvez voir le code généré et les extensions compilées dans le sous-répertoire .ruby_inline de votre répertoire personnel.

Intégrez le code C directement dans votre programme Ruby:

  • RubyInline (disponible en tant que gem rubyinline ) crée automatiquement une extension

RubyInline ne fonctionnera pas depuis 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 fonction C copy_file existe maintenant comme méthode d'instance 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
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow