Programmering i Ruby

Den Pragmatiske Programmerers Veiledning

Forrige < Innhold ^
Neste >

Utvide Ruby



Det er enkelt å utvide Ruby med ny funksjonalitet ved å skrive Ruby kode. Men når man begynner å legge til lavnivå kode skrevet i C, er det uendelig med muligheter.

Det er ganske enkelt å utvide Ruby med C. For eksempel, la oss anta at vi lager en spesialsydd, Internet-tilkoblet jukeboks for restauranten Sunset Diner and Grill. Jukeboksen skal spille MP3 lydfiler fra en harddisk eller musikk CDer. Vi ønsker å kontrollere maskinvaren i jukeboksen i fra et Ruby program. Maskinvareleverandøren gav oss en C header fil og et binært bibliotek som vi skal bruke. Vår jobb er å lage et Ruby objekt som utfører de tilsvarende funksjonskallene i C.

Men før vi kan få Ruby og C til å samarbeide, må vi ta en titt på hvordan verdenen til Ruby ser ut i fra C siden. [Mye av informasjonen i dette kapittelet er tatt i fra README.EXT filen som følger med distribusjonen. Dersom du planlegger å skrive en utvidelse til Ruby, bør du nok referere til den filen for ytterlige detaljer i tillegg til de siste endringene. ]

Ruby objekter i C

Det første vi må ta for oss, er hvordan Ruby datatyper representeres og aksesseres i fra C. Alle ting i Ruby er et objekt og alle variablene er referanser til objekter. I C betyr det at typen til alle Ruby variabler er VALUE, som enten er en peker til et Ruby objekt eller en umiddelbar verdi (slik som Fixnum).

Det er slik Ruby implementerer objekt-orientert kode i C. Et Ruby-objekt er en allokert struktur i minnet som inneholder en tabell over instansvariabler og informasjon om klassen. Klassen er i seg selv et annet objekt (en allokert struktur i minnet) som inneholder en tabell med metodene definert for den klassen. Dette er fundamentet til alt i Ruby.

VALUE som en peker

De gangene VALUE er en peker, peker den på en av de definerte objekt strukturene i Ruby---du kan ikke ha en VALUE som peker til en vilkårlig struktur. Strukturen til de innebygde klassene er definert i ``ruby.h'' og har navn på formen R Klassenavn, slik som RString og RArray.

Det finnes flere måter å sjekke hva slags type struktur en VALUE peker til. Makroen TYPE( obj ) returnerer en konstant som representerer C typen av det gitte objekt: T_OBJECT, T_STRING og så videre. Konstantene for de innebygde klassene er definert i ``ruby.h''. Legg merke til at type her referer til en implementasjonsdetalj---det er ikke det samme som klassen til et objekt.

Dersom du ønsker å være sikker på at en verdipeker (VALUE peker) inneholder en bestemt struktur, kan du bruke makroen Check_Type, som vil heve et TypeError unntak dersom value ikke er av den forventede type (som er en av konstantene T_STRING, T_FLOAT og så videre):

Check_Type(VALUE value, int type)

Dersom hurtighet er viktig, finnes det raskere makroer som spesifikt sjekker for de umiddelbare verdiene Fixnum og nil.

FIXNUM_P(value) -> ulik null hvis value er en Fixnum
NIL_P(value)    -> ulik null hvis value er nil
RTEST(value)    -> ulik null hvis value er hverken nil eller false

Legg nok en gang merke til at vi snaker om ``type'' som den C strukturen som representerer en spesifikk innebygd type. Klassen til et objekt er et helt annet beist. Klasseobjektene til de innebygde klassene er lagres i globale C variabler med navn på formen rb_c Klassenavn (for eksempel rb_cObject); moduler benevnes rb_m Modulnavn.

Det er ikke å anbefale å rote med data som finnes i disse strukturene direkte. Men, du kan se, men ikke røre, med mindre du er glad i avlusingsverktøy. Til vanlig bør du bare bruke de medfølgende C funksjonene for å manipulere Ruby data (vi kommer nærmere inn på dette om et øyeblikk).

Men i søken etter effektivitet kan du ha behov for å grave frem data fra disse strukturene. For å kunne derefere medlemsvariablene i disse C strukturene, må du omstøpe den generiske VALUE til den faktiske strukturtypen. I ruby.h finner du mange makroer som gjør denne omstøpingen for deg, slik at det er lettere å få tilgang til medlemsvariable i strukturene. Disse makroene har navn på formen RKLASSENAVN , som i RSTRING eller RARRAY. For eksempel:

VALUE str, arr;
RSTRING(str)->len -> lengden til Ruby strengen
RSTRING(str)->ptr -> peker til hvor strengen lagres
RARRAY(arr)->len  -> lengden til tabellen
RARRAY(arr)->capa -> kapasiteten til tabellen
RARRAY(arr)->ptr  -> peker til hvor tabellen lagres

VALUE som et umiddelbart objekt

Som nevnt tidligere, umiddelbare verdier er ikke pekere: Fixnum, Symbol, true, false, og nil er lagret direkte i VALUE.

Fixnum verdier lagres som 31-bit heltall[Eller 63-bit på CPU arkitekter med bredere ordlengde.] som formes ved å bitvis flytte det opprinnelige tallet 1 bit til venstre og deretter sette den minst signifikante biten (bit 0, kjent som LSB, som står for Least Significant Bit) til ``1''. Når VALUE brukes som en peker til en spesifikk Ruby struktur, er den garantert å alltid ha en LSB som er null; de andre umiddelbare verdiene har også null i LSB. Dermed kan en enkel bit-test fortelle deg om du har en Fixnum eller ikke.

Det finnes flere nyttige konverteringsmakroer for tall, så vel som andre standard datatyper, vist i tabell 17.1 på side 174(??).

De andre umiddelbare verdiene (true, false og nil) er representert i C som konstantene Qtrue, Qfalse og Qnil. Du kan sjekke VALUE variabler opp mot disse konstantene direkte, eller bruke konverteringsmakroene (som gjør den passende omstøpingen).

Skrive Ruby-kode i C

En av gledene ved Ruby er at du kan skrive Ruby programmer nesten direkte i C. Det vil si, du kan bruke de samme metodene og samme logikk, men med litt forskjellig syntaks for å tekkes C kompilatoren. For eksempel, her følger en liten, ganske kjedelig, testklasse skrevet i Ruby.

class Test
  def initialize
    @arr = Array.new
  end
  def add(anObject)
    @arr.push(anObject)
  end
end

Den tilsvarende koden skrevet i C ser litt kjent ut.

#include "ruby.h"

static VALUE t_init(VALUE self) {   VALUE arr;

  arr = rb_ary_new();   rb_iv_set(self, "@arr", arr);   return self; }

static VALUE t_add(VALUE self, VALUE anObject) {   VALUE arr;

  arr = rb_iv_get(self, "@arr");   rb_ary_push(arr, anObject);   return arr; }

VALUE cTest;

void Init_Test() {   cTest = rb_define_class("Test", rb_cObject);   rb_define_method(cTest, "initialize", t_init, 0);   rb_define_method(cTest, "add", t_add, 1); }

La oss ta for oss dette eksempelet i detalj, siden det illustrerer mange av de viktigste konseptene i dette kapittelet. Til å begynne med, trenger vi å inkludere header-filen ``ruby.h'' for å få med oss de nødvendige definisjonene.

Ta nå en titt på den siste funksjonen, Init_Test. Hver klasse eller modul definerer en global C funksjon med et navn på formen Init_ Navn. Denne funksjonen vil bli kalt når fortolkeren først laster inn utvidelsen Navn (eller på oppstart for statisk lenkede utvidelser). Dette gjøres for å initialisere utvidelsen og innføre den i Ruby sine omgivelser. I dette tilfellet definerer vi en ny klasse kalt Test, som er en subklasse av Object (representert av det eksterne symbolet rb_cObject; se i ``ruby.h'' for flere).

Deretter setter vi opp to instansmetoder, add og initialize, i klassen Test. Kallene til rb_define_method etablerer en kobling mellom Ruby metodenavnet og C funksjonen som vil implementere den, slik at et kall til add metoden i Ruby vil kalle C funksjonen t_add med et argument.

Tilsvarende, når new blir kalt på denne klassen, vil Ruby først konstruere et enkelt objekt og deretter kalle initialize, som vi har definert her til å kalle C funksjonen t_init uten (Ruby) argumenter.

Flytt øynene tilbake på definisjonen til initialize. Selv om vi sa at den ikke skal ta noen argumenter, er det en parameter her! I tillegg til eventuelle Ruby argumenter blir hver metodekall gitt et første VALUE argument som inneholder mottakeren til metodekallet (det vil si det tilsvarende til self i Ruby kode).

Det første vi gjør i initialize er å lage en Ruby tabell og setter instansvariabelen @arr til å peke på den. Akkurat som du forventet når man skriver Ruby kode, blir instansvariabelen lagd når den referes til, dersom den ikke allerede eksisterer.

Dernest får t_add funksjonen tak i instansvariabelen @arr fra det nåværende objektet og kaller Array#push for å dytte den angitte parameteren inn i tabellen. Når man tar tak i instansvariabler på denne måten, er @-prefikset obligatorisk---hvis ikke blir variabelen laget, men kan ikke refereres til i fra Ruby.

Til tross for all den kjeitete syntaksen som C påtvinger, så skriver du fremdeles i Ruby---du kan manipulere objekter ved hjelp av alle de metodekallene du har lært å kjenne og å elske, kombinert med den fordelen at du kan skrive stram og kjapp kode ved behov.

ADVARSEL: Enhver C funksjon som kan kalles fra Ruby returnere en VALUE, selv om det bare er Qnil. Dersom du glemmer dette, vil en core dump (eller en GPF) være en sannsynlig konsekvens.

Vi kan bruke C versjonen av koden i Ruby ganske enkelt ved å kalle require for å laste den inn dynamisk ved kjøretid (på de fleste plattformer).

require "code/ext/Test"
t = Test.new
t.add("Bill Chase")
C/Ruby datatype konverteringsfunksjoner og -makroer
C datatyper til Ruby objekter:
INT2NUM(int) -> Fixnum eller Bignum
INT2FIX(int) -> Fixnum (raskere)
INT2NUM(long eller int) -> Fixnum eller Bignum
INT2FIX(long eller int) -> Fixnum (raskere)
CHR2FIX(char) -> Fixnum
rb_str_new2(char *) -> String
rb_float_new(double) -> Float
Ruby objekter til C datatyper:
int NUM2INT(Numeric) (Inkluderer typesjekk)
int FIX2INT(Fixnum) (Raskere)
unsigned int NUM2UINT(Numeric) (Inkluderer typesjekk)
unsigned int FIX2UINT(Fixnum) (Includes type check)
long NUM2LONG(Numeric) (Inkluderer typesjekk)
long FIX2LONG(Fixnum) (Raskere)
unsigned long NUM2ULONG(Numeric) (Inkluderer typesjekk)
char NUM2CHR(Numeric eller String) (Inkluderer typesjekk)
char * STR2CSTR(String)
char * rb_str2cstr(String, int *length) Returnerer også lengden
double NUM2DBL(Numeric)

Evaluering av Ruby uttrykk i C

Dersom du er midt inne i litt C kode og du ønsker å kjøre et vilkårlig Ruby uttrykk uten å skrive tonnevis av C, kan du alltids bruke C versjonen av eval. Anta at du har en samling av objekter som trenger å få et flagg fjernet.

rb_eval_string("anObject.each{|x| x.clearFlag }");

Dersom du bare ønsker å kalle en gitt metode (som er billigere enn å kjøre eval på en fullstendig streng) kan du bruke

rb_funcall(receiver, method_id, argc, ...)

Fullstendig beskrivelse av disse og andre ofte bruke C funksjoner starter på side 186(??).

Dele data mellom Ruby og C

Vi har nå dekket nok av det grunnleggende stoffet, slik at vi kan komme oss tilbake til jukeboks eksempelet---koble C kode med Ruby og dele data og oppførsel mellom de to verdene.

Direkte deling av variabler

Selv om du kunne vedlikeholde en C versjon av en variabel sammen med en separat Ruby versjon av den variabelen og streve med å holde dem synkronisert, [Et åpenbart brudd mot DRY---Don't Repeat Yourself---prinsippet som beskrevet i boka vår The Pragmatic Programmer .] ville det være mye bedre å dele en variabel direkte mellom Ruby og C. Du kan dele globale variabler ved å lage et Ruby objekt på C siden og deretter binde adressen dets til en global Ruby variabel. I dette tilfellet er $-prefikset valgfritt, men det hjelper å kommunisere at det er snakk om en global variabel.

VALUE hardware_list;
hardware_list = rb_ary_new();
rb_define_variable("$hardware", &hardware_list);
...
rb_ary_push(hardware_list, rb_str_new2("DVD"));
rb_ary_push(hardware_list, rb_str_new2("CDPlayer1"));
rb_ary_push(hardware_list, rb_str_new2("CDPlayer2"));

På Ruby siden kan man da få tak i C variabelen hardware_list som $hardware:

$hardware » ["DVD", "CDPlayer1", "CDPlayer2"]

Du kan også lage variabler som hektes fast til en angitt funksjon som vil bli kalt hver gang variabelen aksesseres, samt virtual variabler som bare kaller funksjoner---ingen variabel er involvert. Se i API avsnittet som begynner på side 189(??) for detaljer.

Dersm du lager et Ruby objekt fra C siden og lagrer det i en global C variabel uten å eksportere den til Ruby, må du iallefall si ifra til søppeltømmeren. Hvis ikke vil du få på pukkelen for din uaktsomhet:

VALUE obj;
obj = rb_ary_new();
rb_global_variable(obj);

Pakke inn C strukturer

Nå kommer vi til den virkelig morosomme biten. Vi har leverandøres bibliotek som kontrollerer lyd-CD jukeboksene og vi er rede til å plugge det inn i Ruby. Leverandæres header-fil ser slik ut:

typedef struct _cdjb {
  int statusf;
  int request;
  void *data;
  char pending;
  int unit_id;
  void *stats;
} CDJukebox;

// Allocate a new CDPlayer structure and bring it online CDJukebox *CDPlayerNew(int unit_id);

// Deallocate when done (and take offline) void CDPlayerDispose(CDJukebox *rec);

// Seek to a disc, track and notify progress void CDPlayerSeek(CDJukebox *rec,                   int disc,                   int track,                   void (*done)(CDJukebox *rec, int percent)); // ... others... // Report a statistic double CDPlayerAvgSeekTime(CDJukebox *rec);

Denne leverandøren kan faget sitt. Selv om han kanskje ikke vil innrømme det, så er koden skrevet med et lite hint objekt-orientering. Vi vet ikke hva alle de feltene i CDJukeBox strukturen betyr, men det gjør ikke noe---vi kan bare behandle det som vilkårlig haug med bits. Leverandørens kode vet hva som skal gjøres med dem, vi må bare drasse rundt på dem.

De gangene du har en ren C struktur som du ønsker å håndtere som et Ruby objekt, bør du pakke den inn i en spesiell, intern Ruby-klasse som heter DATA (av typen T_DATA). Det finnes to makroer for å gjøre denne innpakkingen, samt en for å hente strukturen din ut igjen.

Innpakking av C datatyper
VALUE  Data_Wrap_Struct(VALUE class, void (*mark)(), void (*free)(), void *ptr")
  Pakker den gitte C datatypen ptr, registrerer de to søppeltømmingsrutinene (se under), og returnerer en VALUE peker til et faktisk Ruby-objekt. C typen til det resulterende objektet er T_DATA og dens Ruby-klasse er class.
VALUE  Data_Make_Struct(VALUE class, c-type, void (*mark)(), void (*free)(), c-type *")
  Allokerer en struktur med den gitte typen først, deretter fortsetter på samme måte som Data_Wrap_Struct. c-type er navnet på C datatypen du pakker inn, ikke en variabel av den typen.
  Data_Get_Struct(VALUE obj,c-type,c-type *")
  Returnerer den opprinnelige pekeren. Denne makroen er en typesikker (type-safe) innekapsling rundt makroen DATA_PTR(obj), som finner selve verdien til pekeren.

Objektet som Data_Wrap_Struct lager er et vanlig Ruby-objekt, med det en ekstra C datatype som man ikke får tak i fra Ruby. Som du kan se i figur 17.1 på side 177(??), er denne C datatypen separat fra alle instansvariablene til objektet. Men siden den er en separat ting, hvordan blir vi kvitt den når søppeltømmeren kommer og tar dette objektet? Hva hvis du må gi slipp på en eller annen ressurs (lukke en fil, gi fra deg en lås, rydde opp i en IPC mekanisme osv)?
Figur 17.1: Pakker objekter rundt C datatyper

For å kunne bli med i Ruby sin "mark-and-sweep" søppeltømmingsprosess, må du definere en rutine som frigir din struktur, samt muligens en rutine som merker eventuelle referanser fra din struktur til andre. Begge rutinene tar en void-peker som referer til din struktur. Rutinen mark kalles av søppeltømmeren under ``markeringsfasen''. Dersom din struktur referer til andre Ruby objekter, må din mark funksjon identifisere disse objektene ved å bruke rb_gc_mark(value). Hvis strukturen ikke referer til andre Ruby objekter, kan du ganske enkelt gi inn 0 som funksjonspeker.

Når objektet behøver å fjernes, vil søppeltømmeren kalle free rutinen. Hvis du har allokert noe minne på egenhånd (for eksempel ved hjelp av Data_Make_Struct), må du angi en free-funksjon---selv om det bare er det vanlige C bibliotekets free rutine. For kompliserte strukturer du har allokert, kan free-funksjonen din ha behov for å traversere structuren for å frigi alt allokert minne.

Først ser vi på et enkelt eksempel uten noe spesiell håndtering. Gitt strukturdefinisjonen

typedef struct mp3info {
  char *title;
  char *artist;
  int  genre;
} MP3Info;

kan vi lage en struktur, fylle den med data og pakke den inn som et objekt. [ Vi jukser litt i dette eksempelet. Vår MP3Info struktur har et par char pekere i seg. I vår kode initialiserer vi dem fra to statiske strenger. Det betyr at vi slipper å figi disse sammen med MP3Info strukturen. Dersom vi hadde allokert disse strengene dynamisk, måtte vi ha skrevet en free-funksjon for å fjerne dem. ]

MP3Info *p;
VALUE info;

p = ALLOC(MP3Info); p->artist = "Maynard Ferguson"; p->title = "Chameleon"; ... info = Data_Wrap_Struct(cTest, 0, free, p);

info er av typen VALUE, et faktisk Ruby objekt av klassen Test (representert i C ved den innebygde typen T_DATA). Du kan dytte den inn i en tabell, ha en referanse til det i et annet objekt og så videre. På et senere punkt i koden, kan vi ønske å hente ut denne strukturen igjen utifra VALUE verdien:

VALUE doit(VALUE info) {
  MP3Info *p;
  Data_Get_Struct(info, MP3Info, p);
  ...
  p->artist    -> "Maynard Ferguson"
  p->title     -> "Chameleon"
  ...
}

For å følge konvensjonene, trenger du derimot et par ting til: støtte av en initialize metode og en ``C-konstruktør.'' Hvis du skrev Ruby kode, ville du allokert og initisialisert et objekt ved å kalle new. I C utvidelser er det tilsvarende kallet Data_Make_Struct. Men, selv om dette allokerer minne til objektet, kaller det ikke en initialize metode. Det må du gjøre selv:

info = Data_Make_Struct(cTest, MP3Info, 0, free, one);
rb_obj_call_init(info, argc, argv);

Dette har også den fordelen at subklasser i Ruby kan få lov å overstyre eller utvide den grunnleggende initialize metoden i klassen din. Fra innsiden av initialize har du lov (men det er ikke nødvendigvis å anbefale) å endre den eksisterende datapekeren, som kan aksesseres direkte med DATA_PTR(obj).

Og til sist ønsker du kanskje å definere en ``C-konstruktør''---det vil si, en globalt tilgjengelig C funksjon som lager objektet i et enkelt kall. Du kan bruke denne funksjonen i din egen kode eller tillate andre utvidelsesbiblioteker å bruke den. Alle de innebygde klassene støtter denne ideen med funksjoner som rb_str_new, rb_ary_new, og så videre. Vi kan lage vår egen:

VALUE mp3_info_new() {
  VALUE info;
  MP3Info *one;
  info = Data_Make_Struct(cTest, MP3Info, 0, free, one);
  ...
  rb_obj_call_init(info, 0, 0);
  return info;
}

Et eksempel

Ok, nå er vi klar for et komplett eksempel. Gitt vår leverandørs header-fil, som gitt tidligere, skriver vi følgende kode.

#include "ruby.h"
#include "cdjukebox.h"

VALUE cCDPlayer;

static void cd_free(void *p) {   CDPlayerDispose(p); }

static void progress(CDJukebox *rec, int percent) {   if (rb_block_given_p()) {     if (percent > 100) percent = 100;     if (percent < 0) percent = 0;     rb_yield(INT2FIX(percent));   } }

static VALUE cd_seek(VALUE self, VALUE disc, VALUE track) {   CDJukebox *ptr;   Data_Get_Struct(self, CDJukebox, ptr);

  CDPlayerSeek(ptr,                NUM2INT(disc),                NUM2INT(track),                progress);   return Qnil; }

static VALUE cd_seekTime(VALUE self) {   double tm;   CDJukebox *ptr;   Data_Get_Struct(self, CDJukebox, ptr);   tm = CDPlayerAvgSeekTime(ptr);   return rb_float_new(tm); }

static VALUE cd_unit(VALUE self) {   return rb_iv_get(self, "@unit"); }

static VALUE cd_init(VALUE self, VALUE unit) {   rb_iv_set(self, "@unit", unit);   return self; }

VALUE cd_new(VALUE class, VALUE unit) {   VALUE argv[1];   CDJukebox *ptr = CDPlayerNew(NUM2INT(unit));   VALUE tdata = Data_Wrap_Struct(class, 0, cd_free, ptr);   argv[0] = unit;   rb_obj_call_init(tdata, 1, argv);   return tdata; }

void Init_CDJukebox() {   cCDPlayer = rb_define_class("CDPlayer", rb_cObject);   rb_define_singleton_method(cCDPlayer, "new", cd_new, 1);   rb_define_method(cCDPlayer, "initialize", cd_init, 1);   rb_define_method(cCDPlayer, "seek", cd_seek, 2);   rb_define_method(cCDPlayer, "seekTime", cd_seekTime, 0);   rb_define_method(cCDPlayer, "unit", cd_unit, 0); }

Now we have the ability to control our jukebox from Ruby in a nice, object-oriented manner:

require "code/ext/CDJukebox"
p = CDPlayer.new(1)
puts "Unit is #{p.unit}"
p.seek(3, 16) {|x| puts "#{x}% done" }
puts "Avg. time was #{p.seekTime} seconds"
produserer:
Unit is 1
26% done
79% done
100% done
Avg. time was 1.2 seconds

Dette eksempelet demonstrerer det meste vi har snakket om til nå, med en ekstra kjekk fasilitet. Leverandørens bibliotek tilbøy en tilbakekallsrutine---en funksjonspeker som kalles med jevne mellomrom mens maskinvaren kverner ivei fra en plate til en annet. Vi har satt den opp slik at den kjører en kodeblokk gitt som argument til seek. I funksjonen progress ser vi om det er en iterator i den nåværende konteksten, og hvis så, kjør den med en prosentsats som angir progresjon, som argument.

Minneallokering

Noen ganger vil du kanskje ha behov for å allokere minne i en utvidelse som ikke vil bli brukt for å holde objekter---kanskje har du et stort filter bitmap, et bilde eller en mengde små strukturer som Ruby ikke bruker direkte.

For å fungere ordentlig sammen med søppeltømmeren, bør du bruke de følgende minneallokeringsrutinene. Disse rutinene gjør litt mer arbeid en den vanlige malloc. For eksempel, dersom ALLOC_N finner ut at den ikke kan allokere den ønskede mengden minne, vil den starte søppeltømmmeren for å prøve å få frigitt litt plass. Den vil kaste et NoMemError unntak dersom den ikke kan allokere minnet eller dersom den forespurte mendgen er ugyldig.

Minneallokering
type *  ALLOC_N(c-type, n")
  Allokerer n c-type objekter, hvor c-type er ordrett navnet på C typen, ikke en variabel av den typen.
type *  ALLOC(c-type")
  Allokerer en c-type og omstøper resultatet til en peker av den typen.
  REALLOC_N(var, c-type, n")
  Reallokerer n stykk c-typeer og tilordner resultatet til var, en peker til en c-type.
type *  ALLOCA_N(c-type, n")
  Allokerer minne for n objekter av typen c-type på stakken---dette minnet vil bli automatisk frigitt når funksjonen som kaller ALLOCA_N returnerer.

Lage en utvidelse

Når vi har skrevet kildekoden til en utvidelse, må vi deretter kompilere den slik at Ruby kan bruke den. Vi kan enten gjøre dette som et delt objekt (shared object), som lastes dynamisk inn i Ruby ved kjøretid, eller vi kan lenke utvidelsen statisk inn i selve Ruby fortolkeren. Hovedparten av prosedyren er lik for dem begge:
Figur 17.2: Kompilering og lenking av en utvidelse

Hvordan lage en Makefile med extconf.rb

Figur 17.2 på side 182(??) viser den overordnede gangen i hvordan en utvidelse kompilseres og lenkes. Nøkkelen til hele prosessen er extconf.rb programmet som du, som en utvikler, skriver. I extconf.rb skriver du et enkelt program som finner ut hvilke fasiliteter som er tilgjengelig på brukerens system og hvor disse er å finne. Kjøring av extconf.rb lager en skreddersydd Makefile tilpassed både din applikasjon og systemet det blir kompilert på. Når du deretter kjører make kommandoen med denne Makefile-fila, blir utvidelsen din kompilert, lenket og (eventuelt) installert.

Den enkleste extconf.rb trenger ikke være lengre enn to linjer, og for mange utvidelser holder det.

require 'mkmf'
create_makefile("Test")

Den første linjen henter inn biblioteksmodulen mkmf (dokumentasjon starter på side 451(??)). Den inneholder alle kommandoene vi kommer til å bruke. Den andre linjen lager en Makefile for en utvidelse kalt ``Test''. (Legg merke til at ``Test'' er navnet på utvidelsen, make-fila vil alltids hete ``Makefile''.) Test vil bli bygget utifra alle de C kildekode filene som er å den gjeldende katalogen.

La oss anta at vi kjører dette extconf.rb programmet i en katalog som inneholder en enkelt kildekodefil, main.c. Resultatet er en Makefile som kompilerer og lenker vår utvidelse. På vårt system inneholder den de følgende kommandoer.

gcc -fPIC -I/usr/local/lib/ruby/1.6/i686-linux -g -O2  \
  -c main.c -o main.o
gcc -shared -o Test.so main.o -lc

Resultatet av denne kompileringen er filen Test.so, som kan lenkes inn i Ruby dynamisk ved kjøretid ved å bruke ``require''. Legg merke til hvordan mkmf kommandoene har lokalisert platformspesifikke biblioteker og tatt i bruk kompilatorspesifikke opsjoner automatisk. Kjekt, eller hva?

Selv om dette grunnleggende programmet fungerer for mange enkle utvidelser, må du kanskje gjøre mer arbeid dersom utvidelsen din trenger header-filer eller bibliotek som ikke følger med som standard i kompileringsmiljøet, eller du gjør betinget kompilering basert på tilgjengeligheten av bibliotek og funksjoner.

En vanlig forutsetning for kompilering er å angi stien til include-filer og bibliotek som ikke er innstallert på standard plasseringer. Dette gjøres i to deler. Først skal extconf.rb inneholde en eller flere dir_config kommandoer. Dette angir en merkelapp til et sett med kataloger. Deretter, når du kjører extconf.rb programmet, forteller du mkmf hvor de tilsvarende fysiske katalogene er på det gjeldende systemet.

Dersom extconf.rb inneholder linjen dir_config( navn ), så gir du stien til de respektive katalogene med kommandoline-opsjoner:

--with-navn-include=directory

* Legg til directory/include katalogen til kompileringskommandoen.
--with-navn-lib=directory

* Legg til directory/lib katalogen til lenkekommandoen.

Dersom både include- og bibliotekskatalogene er subkataloger i en annen katalog, (noe som er ganske vanlig) og disse er kalt include og lib, (som også er vanlig) kan du ta en snarvei:

--with-navn-dir=directory

* Legg til directory/lib og directory/include til henholdsvis lenke- og kompileringskommandoen .

Det er en liten særegenhet her. I tillegg til å spesifisere alle disse --with opsjonene når du kjører extconf.rb, kan du også bruke de --with som ble angitt når Ruby ble bygd for din maskin. Dette betyr at du kan finne ut plasseringen til biblioteker som Ruby selv bruker.

For å gjøre alt dette litt mer håndfast, la oss anta at du trenker å bruke biblioteker og include-filer for CD jukeboksen som vi utvikler. Din extconf.rb fil kan da inneholde

require 'mkmf'
dir_config('cdjukebox')
# .. mer kode
create_makefile("CDJukeBox")

Deretter kunne du kjørt extconf.rb omtrent slik:

% ruby extconf.rb --with-cdjukebox-dir=/usr/local/cdjb

Den genererte Makefile vil anta at bibliotekene er å finne i /usr/local/cdjb/lib og include-filene i /usr/local/cdjb/include.

Kommandoen dir_config legger til nye steder for å søke etter biblioteker og include-filer. Men den lenker derimot ikke inn disse bibliotekene inn i din applikasjon. For å gjøre det, vil du trenge å bruke en eller flere kall til have_library eller find_library.

Metoden have_library leter etter et gitt inngangspunkt i et navngitt bibliotek. Hvis den finner inngangspunktet, legger den biblioteket til listen over de som skal linkes inn i din applikasjon. Metoden find_library oppfører seg lignende, men tillater deg å spesifisere en liste med kataloger som man kan søke etter biblioteket i.

require 'mkmf'
dir_config('cdjukebox')
have_library('cdjb', 'CDPlayerNew')
create_makefile("CDJukeBox")

På noen platformer kan et populært bibliotek være i en av flere forskjellige steder. X Window systemet, for eksempel, er beryktet for å eksistere i forskjellige kataloger på forskjellige systemer. Kommandoen find_library vil søke i en angitt liste med kataloger for å finne den riktige (dette er forskjellig fra have_library, som kun bruker konfigurasjonsinformasjon for å finne det). For å lage en Makefile som bruker både X Windows og et JPEG bibliotek, kunne extconf.rb for eksempel inneholde

require 'mkmf'

if have_library("jpeg","jpeg_mem_init") and    find_library("X11", "XOpenDisplay", "/usr/X11/lib",                 "/usr/X11R6/lib", "/usr/openwin/lib") then     create_makefile("XThing") else     puts "No X/JPEG support available" end

Vi har lagt til ekstra funksjonalitet i dette programmet. Alle mkmf kommandoene returnerer false dersom de feiler. Det betyr at vi kan skrive en extconf.rb som bare genererer en Makefile dersom alt som trengs er tilstede. Ruby distribusjonen gjør dette slik at den vil bare prøve å kompilere de utvidelsene som er støttet av systemet ditt.

Du har kanskje også behov for at utvidelsen din skal kunne konfigurere seg i forhold til miljøet den kompileres for. Et eksempel kunne være at vår CD jukeboks kunne brukt en MP3 dekoder med høy ytelse dersom brukeren har en installert. Vi kan sjekke ved å lete etter dens header-fil.

require 'mkmf'
dir_config('cdjukebox')
have_library('cdjb', 'CDPlayerNew')
have_header('hp_mp3.h')
create_makefile("CDJukeBox")

Vi kan også se etter om miljøet har en bestemt funksjon i et av bibliotekene vi kommer til å bruke. For eksempel er setpriority metoden nyttig, men ikke alltids tilgjengelig. Vi kan sjekke om den er tilgjengelig med:

require 'mkmf'
dir_config('cdjukebox')
have_func('setpriority')
create_makefile("CDJukeBox")

Både have_header og have_func definerer preprosesseringskonstanter dersom de finner det som søkes etter. Navnene konstrueres ved å gjøre bokstavene i det som søkes etter om til store bokstaver og å prefikse med ``HAVE_''. Din C kode kan benytte seg av dette:

#if defined(HAVE_HP_MP3_H)
#  include <hp_mp3.h>
#endif

#if defined(HAVE_SETPRIORITY)   err = setpriority(PRIOR_PROCESS, 0, -10) #endif

Dersom du har spesielle behov som ikke dekkes av alle disse mkmf kommandoene, kan programmet ditt legge ting direkte inn i de globale variablene $CFLAGS og $LFLAGS, som sendes til henholdsvis kompilatoren og lenkeren.

Statisk lenking

Dersom systemet ditt ikke støtter dynamisk lenking eller du har en utvidelsesmodul som du ønsker å ha statisk lenket inn i selve Ruby, kan du endre filen ext/Setup i distribusjonen og legg din katalog til listen over utvidelser og så bygge Ruby på nytt. Utvidelsene listet i Setup vil bli statisk lenket inn i Ruby sin kjørbare programfil. Hvis du ønsker å slå av all dynamisk lenking og lenke alle utvidelser statisk, legg til følgende opsjon i ext/Setup.

option nodynamic

Innebygging av en Ruby fortolker

I tillegg til å utvide Ruby ved å legge til C kode, kan du også snu på flisa og bygge selve Ruby inn i din applikajson. Her følger et eksempel.

#include "ruby.h"

main() {   /* ... our own application stuff ... */   ruby_init();   ruby_script("embedded");   rb_load_file("start.rb");   while (1) {     if (need_to_do_ruby) {       ruby_run();     }     /* ... run our app stuff */   } }

For å initialisere Ruby fortolkeren, må du kalle ruby_init(). Men på noen platformer kan du trenge å gå igjennom spesielle steg først:

#if defined(NT)
  NtInitialize(&argc, &argv);
#endif
#if defined(__MACOS__) && defined(__MWERKS__)
  argc = ccommand(&argv);
#endif

Titt i main.c som er å finne i Ruby distribusjonen for andre spesifikke definisjoner eller oppsett som kreves for din platform.

Innebygd Ruby API
void  ruby_init(")
  Gjør oppsett og initialisering av fortolkeren. Denne funksjonen bør kalles før noen andre Ruby-relaterte funksjoner.
void  ruby_options(int argc, char **argv")
  Gir kommandoline opsjonene videre til Ruby fortolkeren.
void  ruby_script(char *name")
  Setter navnet til Ruby skriptet (og $0) til name.
void  rb_load_file(char *file")
  Laster den angitte filen inn i fortolkeren.
void  ruby_run(")
  Kjører fortolkeren.

Du må ta litt spesielt hensyn til unntakshåndtering; alle Ruby kall du gjør på toppnivå bør beskyttes slik at de fanger opp unntakk og håndterer dem ordentlig. rb_protect, rb_rescue, og relaterte funksjoner er dokumentert på side 192(??).

Titt på eruby for et eksempel av innebygging av en Ruby fortolker inne i et annet program. Beskrivelsen starter på side 147(??).

Bygge broer mellom Ruby og andre språk

Så langt har vi sett på hvordam man kan utvide Ruby ved å legge til rutiner skrevet i C. Men du kan skrive utvidelser i så å si hvilket som helst språk, så lenge det er mulig å bruke C som en bro mellom de to språkene. Nesten alt er mulig, inklusiv litt knotete partnerskap mellom Ruby og C++, Ruby og Java og så videre.

Men du kan også oppnå de samme effektene uten å ty til C kode. For eksempel kan du bruke mellomvare, slik som CORBA eller COM, for å bygge bro til andre språk. Se avsnittet om Windows automatisering som starter på side 164(??) for mer om dette.

Ruby sin C API

Sist men ikke minst, her følger flere funksjoner på C-nivå som du kan finne nyttige når du skriver en utvidelse.

Noen funksjoner krever en ID: du kan få tak i en ID til en streng ved å bruke rb_intern, samt rekonstruere navnet fra en ID ved å bruke rb_id2name.

Siden mesteparten av disse C funksjonene tilsvarer Ruby metoder som allerede er dokumenter andre steder i denne boka, vil beskrivelsene her være korte.

Merk at den følgende listen ikke er fullstendig. Det er mange andre funksjoner tilgjengelig---alt for mange til å kunne dokumentere dem alle, viser det seg. Hvis du trenger en metode du ikke finner her, ta en titt i ``ruby.h'' eller ``intern.h''. I tillegg finner du ved eller nær bunnen av hver kildekodefil et sett med metodedefinisjoner som beskriver bindingen fra Ruby metoder til Cfunksjoner. Du kan kanskje kalle C funksjonen direkte eller søke etter en innepakkende funksjon som kaller funksjonen du er ute etter. Følgende liste, basert på listen i README.EXT, viser de viktigste kildekodefilene i fortolkeren.

Ruby sin språkkjerne

class.c error.c eval.c gc.c object.c parse.y variable.c
Nyttefunksjoner

dln.c regex.c st.c util.c
Ruby fortolker

dmyext.c inits.c keywords main.c ruby.c version.c
Grunnbibliotek

array.c bignum.c compar.c dir.c enum.c file.c hash.c io.c marshal.c math.c numeric.c pack.c prec.c process.c random.c range.c re.c signal.c sprintf.c string.c struct.c time.c

Definere objekter
VALUE  rb_define_class(char *navn, VALUE superklasse")
  Definerer en ny klasse på toppnivå med det gitte navn og superklasse (for klassen Object, bruk rb_cObject).
VALUE  rb_define_module(char *navn")
  Definerer en my modul på toppnivå med det gitte navn.
VALUE  rb_define_class_under(VALUE under, char *name, VALUE superclass")
  Definerer en nøstet klasse under klassen eller modulen under.
VALUE  rb_define_module_under(VALUE under, char *name")
  Definerer en nøstet modul under klassen eller modulen under.
void  rb_include_module(VALUE forelder, VALUE modul")
  Inkluderer den gitte modul inn i klassen eller modulen forelder
void  rb_extend_object(VALUE obj, VALUE modul")
  Utvider obj med modul.
VALUE  rb_require(const char *name")
  Ekvivalent med ``require name.'' Returnerer Qtrue eller Qfalse.

I noen av funksjonsdefinisjonene som følger, spesifiserer parameteren argc hvor mange argumenter en Ruby metode mottar. Den kan ha følgende verdier.

argc Funksjonsdeklarasjon
0..17 VALUE func(VALUE self, VALUE arg...)
C funksjonen vil kalles med så mange faktiske argumenter.
-1 VALUE func(int argc, VALUE *argv, VALUE self)
C funksjonen vil bli gitt et varierende antall argumenter i form av en C tabell.
-2 VALUE func(VALUE self, VALUE args)
C funksjonen vil bli gitt et varierende antall argumenter i form av en Ruby tabell.

I en funksjon som har blitt gitt en variabel mengde argumenter, kan du bruke C funksjonen rb_scan_args for å finne ut av ting (se under).

Definere metoder
void  rb_define_method(VALUE classmod, char *navn, VALUE(*func)(), int argc")
  Definerer en instansmetode i klassen eller modulen classmod med det gitte navn, implementert av C funksjonen func og tar argc argumenter.
void  rb_define_module_function(VALUE classmod, char *navn, VALUE(*func)(), int argc)")
  Definerer en metode i klassen classmod med det gitte navn, implementert av C funksjonen func og tar argc argumenter.
void  rb_define_global_function(char *navn, VALUE(*func)(), int argc")
  Definerer en global funksjon (en privat metode i Kernel med det gitte navn, implementert av C funksjonen func og tar argc argumenter.
void  rb_define_singleton_method(VALUE classmod, char *name, VALUE(*func)(), int argc")
  Definerer en singleton-metode i klassen classmod med det gitte navn, implementert av C funksjonen func og tar argc argumenter.
int  rb_scan_args(int argcount, VALUE *argv, char *fmt, ...")
  Går over argumentlisten og tilordner til variabler omtrent som scanf: fmt er en streng som inneholder null, en eller to sifre fulgt av noen flaggtegn. Det første sifferet angir antallet obligatoriske argumenter. Det andre er antallet frivillige argumenter. En ``*'' betyr at resten av argumentene skal pakkes inn i en Ruby tabell. En ``&'' betyr at en assosiert kodeblokk vil tatt imot og tilordet til den gitte variabelen. (Hvis ingen kodeblokk ble gitt, vil variabelen inneholde Qnil) Etter fmt strengen følger pekere (som med til scanf) til VALUE strukturer som argumentene skal tilordnes til.

VALUE name, one, two, rest;
rb_scan_args(argc, argv, "12", &name, &one, &two);
rb_scan_args(argc, argv, "1*", &name, &rest);
void  rb_undef_method(VALUE classmod, const char *name")
  Fjerner definisjonen til den gitte metoden name i den gitte klassen eller modulen classmod.
void  rb_define_alias(VALUE classmod, const char *newname, const char *oldname")
  Definerer et alias for oldname i klasssen eller modulen classmod.

Definere variabler og konstanter
void  rb_define_const(VALUE classmod, char *navn, VALUE verdi")
  Definerer en konstant i klassen eller modulen classmod, med angitt navn og verdi.
void  rb_define_global_const(char *name, VALUE value")
  Definerer en global konstant med angitt navn og verdi.
void  rb_define_variable(const char *navn, VALUE *objekt")
  Eksporterer adressen til det gitte objekt som ble konstruert i C til Ruby sitt navnerom som navn. Fra Ruby vil dette være en global variabel, så navn bør begynne med et dollartegn. Sjekk at reglene for tillatte variabelnavn følger; variabler med ulovlige navn vil ikke være tilgjengelig fra Ruby.
void  rb_define_class_variable(VALUE klasse, const char *navn, VALUE verdi")
  Definerer en klassevariabel navn (som må spesifiseres med et ``@@'' prefiks) i den gitte klasse, med initiell verdi.
void  rb_define_virtual_variable(const char *navn, VALUE(*getter)(), void(*setter)()")
  Eksporterer en virtuell variabel til Ruby sitt navnerom som den globale variablenen $navn. Ingen reell lagringsplass for variabelen eksisterer. Forsøk på å hente og sette variabelen vil kalle de gitte funksjonene med prototypene:

VALUE getter(ID id, VALUE *data,
             struct global_entry *entry);
void setter(VALUE value, ID id, VALUE *data,
            struct global_entry *entry);

Du vil sannsynligvis ikke ha behov for entry parameteren og kan trygt utelate den fra dine funksjonsdeklarasjoner.
void  rb_define_hooked_variable(const char *name, VALUE *variabel, VALUE(*getter)(), void(*setter)()")
  Definerer funksjoner som skal kalles når det leses fra eller skrives til variabel. Se også rb_define_virtual_variable.
void  rb_define_readonly_variable(const char *name, VALUE *value")
  Samme som rb_define_variable, men virker skrivebeskyttet fra Ruby.
void  rb_define_attr(VALUE variabel, const char *navn, int read, int write")
  Lager aksesseringsmetoder for den angitte variabel, med det gitte navn. Hvis read er ulik null, lages en lesemetode. Hvis write er ulik null, lages en skrivemetode.
void  rb_global_variable(VALUE *obj")
  Registrerer den angitte adressen hos søppeltømmeren.

Kalle metoder
VALUE  rb_funcall(VALUE recv, ID id, int argc, ...")
  Kaller metoden angitt ved id i objektet recv med argc antall argumenter, samt selve argumentene (potensielt ingen).
VALUE  rb_funcall2(VALUE recv, ID id, int argc, VALUE *args")
  Kaller metoden angitt ved i objektet recv med argc antall argumenter, samt selve argumentene gitt i C tabellen args.
VALUE  rb_funcall3(VALUE recv, ID id, int argc, VALUE *args")
  Samme som rb_funcall2, men kaller ikke private metoder.
VALUE  rb_apply(VALUE recv, ID name, int argc, VALUE args")
  Kaller metoden angitt ved i objektet recv med argc antall argumenter, samt selve argumentene gitt i et Ruby Array-objekt; args.
ID  rb_intern(char *navn")
  Returnerer en ID for et gitt navn. Hvis navnet ikke eksisterer, blir det laget et symbol, og lagt til symboltabellen.
char *  rb_id2name(ID id")
  Returnerer navnet til den gitte id.
VALUE  rb_call_super(int argc, VALUE *args")
  Kaller den gjeldende metoden i superklassen til det gjeldende objekt.

Exceptions
void  rb_raise(VALUE exception, const char *fmt, ...")
  Hever en exception. Den gitte strengen fmt og gjenværende argumenter blir fortolket som med printf.
void  rb_fatal(const char *fmt, ...")
  Heve et Fatal unntak, som terminerer prosessen. Ingen rescue-blokker blir kalt, men ensure-blokker blir utført. Den gitte strengen fmt og gjenværende argumenter blir fortolket som med printf.
void  rb_bug(const char *fmt, ...")
  Terminerer prosessen umiddelbart---ingen håndteringsmetoder av noen som helst type blir kalt. Den gitte strengen fmt og gjenværende argumenter blir fortolket som med printf. Du bør bare kalle denne funksjonen dersom en fatal bug har vist seg. Du skriver ikke fatale bugs, gjør du vel?
void  rb_sys_fail(const char *msg")
  Hever et platformspesifikt unntak tilsvarende til den siste kjente systemfeilen, med beskjeden msg.
VALUE  rb_rescue(VALUE (*body)(), VALUE args, VALUE(*rescue)(), VALUE rargs")
  Utfører body med de gitte argumentene args. Hvis et StandardError unntak heves, vil rescue bli utført med de gitte argumentene rargs.
VALUE  rb_ensure(VALUE(*body)(), VALUE args, VALUE(*ensure)(), VALUE eargs")
  Utfører body med de gitte argumentene args. Uansett om et unntak heves eller ikke, vil ensure bli utført med de gitte argumentene eargs.
VALUE  rb_protect(VALUE (*body)(), VALUE args, int *result")
  Utfører body med de gitte argumentene args og returnerer ikke-null i result dersom et unntak ble hevet.
void  rb_notimplement(")
  Hever et NotImpError unntak for å indikere at den omsluttende funksjonen ikke er implementert ennå, eller ikke er tilgjengelig på denne platformen.
void  rb_exit(int status")
  Avslutter Ruby med den gitte status. Hever et SystemExit unntak og kaller registrerte exit- og finalizer-funksjoner.
void  rb_warn(const char *fmt, ...")
  Sender en advarsel til standard feil-utput, uansett innstillinger. Den gitte strengen fmt og gjenværende argumenter blir fortolket som med printf.
void  rb_warning(const char *fmt, ...")
  Sender en advarsel til standard feil-utput, forutsatt at Ruby ble kalt med -w flagget. Den gitte strengen fmt og gjenværende argumenter blir fortolket som med printf.

Iteratorer
void  rb_iter_break(")
  Bryter ut av den omliggende iterator blokken.
VALUE  rb_each(VALUE obj")
  Kaller each på det gitte objekt obj.
VALUE  rb_yield(VALUE arg")
  Kaller iterator blokken i den gjeldende kontekst, med arg som argument. Flere verdier kan sendes inn ved hjelp av en tabell.
int  rb_block_given_p(")
  Returnerer true hvis yield ville utført en kodeblokk i den gjeldende kontekst---det vil si, hvis en kodeblokk ble sendt med den nåværende metoden og er tilgjengelig for utføring.
VALUE  rb_iterate(VALUE (*method)(), VALUE args, VALUE (*block)(), VALUE arg2")
  Kaller method med argument args og en blokk, block. En yield fra den metoden vil kalle block med argumentet gitt til yield, og et ekstra argument, arg2.
VALUE  rb_catch(const char *tag, VALUE (*proc)(), VALUE value")
  Tilsvarer Ruby sin catch.
void  rb_throw(const char *tag , VALUE value")
  Tilsvarer Ruby sin throw.

Tilgang til variabler
VALUE  rb_iv_get(VALUE obj, char *name")
  Returnerer instansvariabelen name (som må spesifiseres med et ``@'' prefiks) fra det gitte objekt, obj.
VALUE  rb_ivar_get(VALUE obj, ID name")
  Returnerer instansvariabelen name fra det gitte objekt, obj.
VALUE  rb_iv_set(VALUE obj, char *name, VALUE value")
  Setter instansvariabelen name (som må spesifiseres med et ``@'' prefiks) i det det gitte objekt, obj, til verdien value. Returnerer value.
VALUE  rb_ivar_set(VALUE obj, ID name, VALUE value")
  Setter instansvariabelen name i det det gitte objekt, obj, til verdien value. Returnerer value.
VALUE  rb_gv_set(const char *name, VALUE value")
  Setter den globale variabelen name (``$'' prefikset er valgfritt) til verdien value. Returnerer value.
VALUE  rb_gv_get(const char *name")
  Henter verdien til den globale variabelen name (``$'' prefikset er valgfritt).
void  rb_cvar_set(VALUE class, ID name, VALUE val")
  Setter klassevariabelen name i klassen class til verdien value.
VALUE  rb_cvar_get(VALUE class, ID name")
  Returnerer klassevariabelen name fra klassen class.
int  rb_cvar_defined(VALUE class, ID name")
  Returnerer Qtrue hvis klassevariabelen name er definert for klassen class. Hvis ikke, returneres Qfalse.
void  rb_cv_set(VALUE class, const char *name, VALUE val")
  Setter klassevariabelen name (som må spesifiseres med et ``@@'' prefiks) i klassen class til verdien value.
VALUE  rb_cv_get(VALUE class, const char *name")
  Returnerer klassevariabelen name (som må spesifiseres med et ``@@'' prefiks) fra klassen class.

Objekt status
  OBJ_TAINT(VALUE obj")
  Markerer objektet obj som tainted.
int  OBJ_TAINTED(VALUE obj")
  Returnerer ikke-null hvis objektet obj er tainted.
  OBJ_FREEZE(VALUE obj")
  Markerer objektet obj som frosset.
int  OBJ_FROZEN(VALUE obj")
  Returnerer ikke-null hvis objektet obj er frosset.
  Check_SafeStr(VALUE str")
  Hever en SecurityError hvis det gjeldende sikkerhetsnivå (safe level) er større enn null og str er tainted, eller en TypeError hvis str ikke er en T_STRING.
int  rb_safe_level(")
  Returnerer det gjeldende sikkerhetsnivå (safe level).
void  rb_secure(int level")
  Hever en SecurityError hvis level <= gjeldende sikkerhetsnivå (safe level).
void  rb_set_safe_level(int newlevel")
  Setter sikkerhetsnivået til et nytt nivå, newlevel.

Ofte brukte metoder
VALUE  rb_ary_new(")
  Returnerer en ny Array med standard størrelse.
VALUE  rb_ary_new2(long length")
  Returnerer en ny Array med lengde length.
VALUE  rb_ary_new3(long length, ...")
  Returnerer en ny Array med lengde length og fylt med de gjenværende argumentene.
VALUE  rb_ary_new4(long length, VALUE *values")
  Returnerer en ny Array med lengde length og fylt med verdiene i C tabellen values.
void  rb_ary_store(VALUE self, long index, VALUE value")
  Lagrere verdien value på indeks index i tabellen self.
VALUE  rb_ary_push(VALUE self, VALUE value")
  Dytter value inn på slutten av tabellen self. Returnerer value.
VALUE  rb_ary_pop(VALUE self")
  Fjerner og returnerer siste element fra tabellen self.
VALUE  rb_ary_shift(VALUE self")
  Fjerner og returnerer første element fra tabellen self.
VALUE  rb_ary_unshift(VALUE self, VALUE value")
  Dytter value inn på begynnelsen av tabellen self. Returnerer value.
VALUE  rb_ary_entry(VALUE self, long index")
  Returnerer elementet i tabellen self som finnes på indeks index.
int  rb_respond_to(VALUE self, ID method")
  Returnerer ikke-null dersom self svarer på metoden method.
VALUE  rb_thread_create(VALUE (*func)(), void *data")
  Kjører func i en ny tråd, og sender inn data som et argument.
VALUE  rb_hash_new(")
  Returnerer en ny, tom Hash.
VALUE  rb_hash_aref(VALUE self, VALUE key")
  Returnerer elementet tilsvarende nøkkelen key i self.
VALUE  rb_hash_aset(VALUE self, VALUE key, VALUE value")
  Setter verdien for nøkkelen key til verdien value i self. Returnerer value.
VALUE  rb_obj_is_instance_of(VALUE obj, VALUE klass")
  Returnerer Qtrue hvis obj er en instans av klass.
VALUE  rb_obj_is_kind_of(VALUE obj, VALUE klass")
  Returnerer Qtrue hvis klass er klassen tilobj eller klass er en av superklassene til obj sin klasse.
VALUE  rb_str_new(const char *src, long length")
  Returnerer en ny String intialisert med length tegn i fra src.
VALUE  rb_str_new2(const char *src")
  Returnerer en ny String intialisert med den null-terminerte C strengen src.
VALUE  rb_str_dup(VALUE str")
  Returnerer et nytt String-objekt som er en duplikat av str.
VALUE  rb_str_cat(VALUE self, const char *src, long length")
  Legger til (konkatenerer) length tegn fra srcString-objektet self. Returnerer self.
VALUE  rb_str_concat(VALUE self, VALUE other")
  Legger til (konkatenerer) otherString-objektet self. Returnerer self.
VALUE  rb_str_split(VALUE self, const char *delim")
  Returnerer en tabell av String-objekter laget ved å dele opp self ved delim.

( In progress translation to Norwegian by NorwayRUG. $Revision: 1.22 $ )
$Log: ext_ruby.xml,v $
Revision 1.22  2002/11/24 09:05:30  kent
Første kladd ferdig.

Revision 1.21  2002/11/23 13:46:54  kent
Frem til iterators i den siste tabellen.


Forrige < Innhold ^
Neste >

Extracted from the book "Programming Ruby - The Pragmatic Programmer's Guide".
Translation to norwegian by Norway Ruby User Group.
Copyright for the english original authored by David Thomas and Andrew Hunt:
Copyright © 2001 Addison Wesley Longman, Inc.
This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 or later (the latest version is presently available at
http://www.opencontent.org/openpub/).

(Please note that the license for the original has changed from the above. The above is the license of the original version that was used as a foundation for the translation efforts.)

Copyright for the norwegian translation:
Copyright © 2002 Norway Ruby User Group.
This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 or later (the latest version is presently available at
http://www.opencontent.org/openpub/).
Distribution of substantively modified versions of this document is prohibited without the explicit permission of the copyright holder.
Distribution of the work or derivative of the work in any standard (paper) book form is prohibited unless prior permission is obtained from the copyright holder.