Ruby Language
C Extensies
Zoeken…
Je eerste extensie
C-extensies bestaan uit twee algemene stukken:
- De C-code zelf.
- Het extensieconfiguratiebestand.
Om te beginnen met je eerste extensie zet je het volgende in een bestand met de naam extconf.rb
:
require 'mkmf'
create_makefile('hello_c')
Een paar dingen om op te wijzen:
Ten eerste, de naam hello_c
is wat de uitvoer van uw gecompileerde extensie gaat hello_c
. Het zal zijn wat u gebruikt in combinatie met require
.
Ten tweede kan het extconf.rb
bestand eigenlijk alles worden genoemd, het is gewoon traditioneel wat wordt gebruikt om edelstenen met native code te bouwen, het bestand dat de extensie gaat compileren is het Makefile dat wordt gegenereerd bij het uitvoeren van ruby extconf.rb
. Het standaard Makefile dat wordt gegenereerd, compileert alle .c
bestanden in de huidige map.
Plaats het volgende in een bestand met de naam hello.c
en voer 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);
}
Een overzicht van de code:
De naam Init_hello_c
moet overeenkomen met de naam die is gedefinieerd in uw extconf.rb
bestand, anders kan Ruby bij het dynamisch laden van de extensie niet het symbool vinden om uw extensie op te starten.
De aanroep naar rb_define_module
maakt een Ruby-module met de naam HelloC
waarnaar we onze C-functies een naamruimte geven.
Ten slotte maakt de aanroep naar rb_define_singleton_method
een moduleniveaumethode die rechtstreeks is gekoppeld aan de HelloC
module die we vanuit ruby met HelloC.world
kunnen aanroepen.
Nadat we de extensie hebben gecompileerd met de oproep om te make
, kunnen we de code uitvoeren in onze C-extensie.
Start een console!
irb(main):001:0> require './hello_c'
=> true
irb(main):002:0> HelloC.world
Hello World!
=> nil
Werken met C Structs
Om met C structs als Ruby-objecten te kunnen werken, moet u ze omwikkelen met aanroepen naar Data_Wrap_Struct
en Data_Get_Struct
.
Data_Wrap_Struct
verpakt een C-gegevensstructuur in een Ruby-object. Het neemt een wijzer naar uw gegevensstructuur, samen met enkele aanwijzingen voor callback-functies, en retourneert een WAARDE. De macro Data_Get_Struct
neemt die VALUE en geeft u een pointer terug naar uw C-gegevensstructuur.
Hier is een eenvoudig voorbeeld:
#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);
}
En de extconf.rb
:
require 'mkmf'
create_makefile('example')
Na het compileren van de extensie:
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 schrijven - RubyInLine
RubyInline is een framework waarmee u andere talen kunt insluiten in uw Ruby-code. Het definieert de Module # inline-methode, die een builderobject retourneert. U geeft de bouwer een tekenreeks met code geschreven in een andere taal dan Ruby, en de bouwer transformeert deze in iets dat u van Ruby kunt noemen.
Wanneer de C- of C ++ -code wordt gegeven (de twee talen die worden ondersteund in de standaardinstallatie van RubyInline), schrijft de builderobjecten een kleine extensie naar schijf, compileert deze en laadt deze. U hoeft de compilatie niet zelf aan te pakken, maar u kunt de gegenereerde code en gecompileerde extensies zien in de .ruby_inline-submap van uw basismap.
Sluit C-code direct in uw Ruby-programma in:
- RubyInline (beschikbaar als de rubyinline- edelsteen) maakt automatisch een extensie
RubyInline werkt niet vanuit 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
C-functie copy_file
bestaat nu als een instantiemethode van Copier
:
open('source.txt', 'w') { |f| f << 'Some text.' }
Copier.new.copy_file('source.txt', 'dest.txt')
puts open('dest.txt') { |f| f.read }