Suche…


Ihre erste Erweiterung

C-Erweiterungen bestehen aus zwei allgemeinen Teilen:

  1. Der C-Code selbst.
  2. Die Erweiterungskonfigurationsdatei.

Um mit Ihrer ersten Erweiterung zu beginnen, extconf.rb Folgendes in eine Datei namens extconf.rb :

require 'mkmf'

create_makefile('hello_c')

Ein paar Dinge zu beachten:

Der Name hello_c ist der Name der Ausgabe Ihrer kompilierten Erweiterung. Es wird das sein, was Sie in Verbindung mit require .

Zweitens kann die extconf.rb -Datei tatsächlich irgendetwas genannt werden. Traditionell wird dies zum Erstellen von Gems mit nativem Code verwendet. Die Datei, die die Erweiterung kompiliert, ist das Makefile, das beim Ausführen von ruby extconf.rb . Das standardmäßige Makefile, das generiert wird, kompiliert alle .c Dateien im aktuellen Verzeichnis.

hello.c Sie Folgendes in eine Datei namens hello.c und führen Sie 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);
}

Eine Aufschlüsselung des Codes:

Der Name Init_hello_c muss mit dem in Ihrer extconf.rb Datei definierten Namen extconf.rb . Andernfalls kann Ruby beim dynamischen Laden der Erweiterung das Symbol nicht finden, um Ihre Erweiterung zu booten.

Mit dem Aufruf von rb_define_module wird ein Ruby-Modul mit dem Namen HelloC dem wir unsere C-Funktionen unter einem Namen HelloC .

Schließlich rb_define_singleton_method der Aufruf von rb_define_singleton_method dass eine Methode auf HelloC direkt an das HelloC Modul gebunden ist, das wir mit HelloC.world von Ruby aus HelloC.world .

Nachdem wir die Erweiterung mit dem Aufruf kompiliert haben make wir den Code in unserer C-Erweiterung ausführen.

Starten Sie eine Konsole!

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

Mit C Structs arbeiten

Um mit C-Strukturen als Ruby-Objekte arbeiten zu können, müssen Sie sie mit Aufrufen von Data_Wrap_Struct und Data_Get_Struct .

Data_Wrap_Struct eine C-Datenstruktur in ein Ruby-Objekt ein. Es benötigt einen Zeiger auf Ihre Datenstruktur, zusammen mit ein paar Zeigern auf Rückruffunktionen, und gibt einen WERT zurück. Das Data_Get_Struct Makro nimmt diesen WERT und gibt einen Zeiger auf Ihre C-Datenstruktur zurück.

Hier ist ein einfaches Beispiel:

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

Und die extconf.rb :

require 'mkmf'

create_makefile('example')

Nach dem Kompilieren der Erweiterung:

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

Inline C schreiben - RubyInLine

RubyInline ist ein Framework, mit dem Sie andere Sprachen in Ihren Ruby-Code einbetten können. Es definiert die Inline-Methode Module #, die ein Builder-Objekt zurückgibt. Sie übergeben dem Builder eine Zeichenfolge, die Code enthält, der in einer anderen Sprache als Ruby geschrieben ist, und der Builder wandelt ihn in etwas um, das Sie von Ruby aus aufrufen können.

Bei Angabe von C- oder C ++ - Code (den beiden Sprachen, die in der Standardinstallation von RubyInline unterstützt werden) schreiben die Builder-Objekte eine kleine Erweiterung auf die Festplatte, kompilieren diese und laden sie. Sie müssen sich nicht selbst mit der Kompilierung befassen, aber Sie können den generierten Code und die kompilierten Erweiterungen im Unterverzeichnis .ruby_inline Ihres Home-Verzeichnisses sehen.

Betten Sie C-Code direkt in Ihr Ruby-Programm ein:

  • RubyInline (verfügbar als Rubyinline- Edelstein) erstellt automatisch eine Erweiterung

RubyInline funktioniert nicht von irb aus

#!/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

Die C-Funktion copy_file existiert jetzt als Instanzmethode von 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
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow