Programmierung in Ruby

Der Leitfaden der Pragmatischen Programmierer

Ruby erweitern



Ruby laesst sich sehr einfach erweitern indem man Ruby-Code schreibt. Sobald man aber beginnt, low-level C-Code hinzuzufügen, sind die Möglichkeiten grenzenlos.

Das Erweitern von Ruby mit Hilfe von C ist recht einfach. Nehmen Sie beispielsweise an, wir erstellen eine massgeschneiderte, internet-fähige Jukebox für die Sonnenuntergangs-Grillfete. Sie soll MP3-Audiofiles von der Festplatte oder Audio-CDs aus einer CD-Jukebox spielen. Wir würden die Jukebox-Hardware gern mit einem Ruby-Programm kontrollieren. Der Hardwareverkäufer hat uns C-Header-Dateien zusammen mit einer binären Bibliothek zur Verfügung gestellt. Unsere Aufgabe besteht im Erstellen eines Ruby-Objektes, das die geeigneten C-Funktionen aufruft.

Aber bevor wir Ruby und C zur Zusammenarbeit bewegen können, müssen wir uns zuerst ansehen, wie sich die Ruby-Welt von der C-Seite aus darstellt. [Ein Großteil der Informationen in diesem Kapitel wurde der Datei README.EXT entnommen, die in der Distribution enthalten ist. Wenn Sie planen, eine Ruby Erweiterung zu schreiben, sollten Sie sich diese Datei ansehen, um mehr Details und die aktuellsten Änderungen zu erfahren. ]

Ruby Objekte in C

Das erste, das wir uns ansehen müssen, ist, wie man Ruby's Datentypen in C darstellt und benutzt. In Ruby ist alles ein Objekt und alle Variablen sind Referenzen auf Objekte. Das bedeutet, dass in C alle Ruby-Variablen den Typ VALUE haben, der entweder einen Zeiger auf ein Ruby-Objekt oder einen Immediate Wert (d.h. direkt repräsentierter Wert, der Übersetzer) (so wie bei Fixnum) darstellt.

Auf diese Weise implementiert Ruby objektorientierten Code in C: Ein Ruby-Objekt ist eine im Speicher angelegte Struktur, die eine Tabelle von Instanzvariablen und Informationen über die Klasse enthält. Die Klasse selbst ist ein anderes Objekt (ebenfalls eine im Speicher angelegte Struktur), die eine Tabelle der definierten Methoden dieser Klasse enthält. An dieser Grundlage hängt alles in Ruby.

VALUE als Zeiger

Wenn VALUE einen Zeiger darstellt, zeigt er auf eine der definierten Ruby-Objekt-Strukturen --- es gibt kein VALUE das auf eine beliebige Struktur zeigt. Die Strukturen für jede eingebaute Klasse sind in ``ruby.h'' definiert und heißen RKlassennamen, so wie in RString und RArray.

Man kann den Strukturtyp eines bestimmten VALUEs auf verschiedene Arten herausbekommen. Das Makro TYPE(obj) liefert eine Konstante, die den C-Datentyp des gegebenen Objektes repräsentiert: T_OBJECT, T_STRING und so weiter. Die Konstanten für die eingebauten Klassen sind in der Datei ``ruby.h'' definiert. Man beachte, dass der Typ auf den wir uns hier beziehen ein Implementierungsdetail darstellt --- der Typ ist etwas anderes als die Klasse des Objektes.

Wenn man sicherstellen will, dass der value Zeiger auf eine bestimmte Struktur zeigt, kann man das Makro Check_Type benutzen, das eine TypeError Ausnahmebedingung wirft, wenn value nicht den erwarteten Typ besitzt. Der Typ ist dabei eine der Konstanten T_STRING, T_FLOAT und so weiter:

Check_Type(VALUE value, int type)

Wenn es um Geschwindigkeit geht, gibt es auch schnellere Makros, die insbesondere auf die Immediate-Werte Fixnum und nil testen.

FIXNUM_P(value) -> ungleich 0 wenn value ein Fixnum ist
NIL_P(value)    -> ungleich 0 wenn value nil ist
RTEST(value)    -> ungleich 0 wenn value weder nil noch false ist

Nochmal, beachten Sie, wir sprechen über den ``Typ '' als die C-Struktur, die einen bestimmten eingebauten Datentyp repräsentiert. Die Klasse eines Objektes ist etwas völlig anderes. Die Klassenobjekte der eingebauten Klassen werden in globalen C-Variablen mit dem Namen rb_cKlassenname (beispielsweise rb_cObject) gespeichert; Module haben die Namen rb_mModulname.

Es ist nicht ratsam, diese Strukturen direkt anzufassen, wie auch immer, Sie können sie lesen, aber besser nicht beschreiben, außer Sie sind fit mit Ihrem Debugger. Normalerweise sollte man zur Manipulation von Ruby's Daten ausschliesslich die bereitgestellten C-Funktionen verwenden (wir werden in Kürze mehr darüber sagen).

Trotzalledem müssen Sie möglicherweise im Interesse der Geschwindigkeit in diesen Strukturen stochern, um an die Daten heranzukommen. Um diese C-Strukturen zu dereferenzieren, hat man den generischen VALUE auf die geeignete Struktur zu casten (d.h., den Typ umwandeln). Die Datei ruby.h enthält eine Anzahl Makros, die das für Sie erledigen, so dass Sie die einzelnen Strukturelemente leicht dereferenzieren können. Diese Makros heissen RKlassenname wie in RSTRING oder RARRAY. Hier ist ein Beispiel:

VALUE str, arr;
RSTRING(str)->len -> Länge des Ruby Strings
RSTRING(str)->ptr -> Zeiger auf dessen Inhalt
RARRAY(arr)->len  -> Länge des Ruby Feldes
RARRAY(arr)->capa -> Kapazität des Ruby Feldes
RARRAY(arr)->ptr  -> Zeiger auf den Datenbereich des Feldes

VALUE als Immediate-Objekt

Wie bereits weiter oben gesagt, sind Immediate-Values keine Zeiger: Fixnum, Symbol, true, false, and nil werden direkt (als Wert) im VALUE gespeichert.

Fixnum Values werden als 31-Bit Zahlen [bzw. 63-Bit auf breiteren CPU Architekturen] gespeichert. Sie werden gebildet, indem man die ursprüngliche Zahl um 1 Bit nach links schiebt und das niederwertigste Bit (Bit 0) auf ``1.'' setzt. Wenn VALUE als Pointer auf eine spezielle Ruby-Struktur benutzt wird, ist garantiert, dass sein niederwertigstes Bit Null ist; Die niederwertigsten Bits aller anderen Immediate-Values sind ebenfalls Null. Folglich verrät ein einfacher Bittest, ob ein Fixnum vorliegt oder eben nicht.

In der Tablle 17.1 auf Seite 176 sind einige nützliche Umwandlungs-Makros für Zahlen und andere Standard-Datentypen aufgelistet.

Die übrigen Immediate-Values (true, false, und nil) werden in C als die Konstanten Qtrue, Qfalse und Qnil dargestellt. Man kann eine VALUE Variable direkt auf diese Konstanten hin testen oder die Umwandlungs-Makros (die die geeignete Typumwandlung übernehmen) verwenden.

Schreiben von Ruby Code in C

Einer der Vorteile von Ruby besteht darin, dass man Ruby-Programme nahezu direkt in C schreiben kann. Das heisst, man kann die gleichen Methoden und die selbe Logik verwenden mit nur leicht abgewandelter Syntax, um das ganze an C anzupassen. Hier ist beispielsweise eine kleine, ziemlich langweilige Test-Klasse in Ruby.

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

Der dazu äquivalente C Code sollte ihnen etwas vertraut vorkommen.

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

Gehen wir dieser Beispiel im Detail durch, da es viele der wichtigen Konzepte in diesem Kapitel illustriert. Zuallererst müssen wir die Headerdatei ``ruby.h'' einbinden um an die nötigen Definitionen zu gelangen.

Sehen wir uns jetzt die letzte Funktion Init_Test an. Jede Klasse und jedes Modul definiert eine C-globale Funktion mit Namen Init_Namen. Diese Funktion wird immer dann aufgerufen, wenn der Interpreter zum ersten Mal die Erweiterung Namen lädt (oder beim Programmstart im Falle statisch gelinkter Erweiterungen). Sie wird dazu benutzt, die Erweiterung zu initialisieren und sie in der Ruby-Umgebung bekannt zu machen. In diesem Fall definieren wir eine neue Klasse mit dem Namen Test, die eine Unterklasse von Object ist (dargestellt durch das externe Symbol rb_cObject; siehe auch ``ruby.h'' für die Alternativen).

Als nächstes legen wir die zwei Instanz-Methoden add und initialize der Klasse Test an. Die Aufrufe von rb_define_method erzeugen eine Bindung zwischen dem Ruby-Methodennamen und der C-Funktion, die sie implementiert. Auf diese Weise bewirkt ein Aufruf der add-Methode in Ruby den Aufruf der C-Funktion t_add mit einem Argument.

Auf ähnliche Weise konstruiert Ruby bei einem Aufruf von new für diese Klasse ein Basisobjekt und ruft danach initialize auf, das wir hier als die C-Funktion t_init ohne (Ruby-) Argument definiert haben.

Gehen wir nun zurück zur Definition von initialize. Auch wenn wir gesagt haben, dass die Funktion keine Argumente hat, bekommt sie hier einen Parameter! Zusätzlich zu eventuellen Ruby-Argumenten wird an jede Methode ein erstes VALUE-Argument übergeben, das den Empfänger für diese Methode (das Äquivalent zu self im Ruby-Code) enthält.

Das erste, das wir in initialize machen, ist die Konstruktion eines Ruby-Feldes und das Setzen der Instanz-Variablen @arr, die auf das Feld zeigt. Wie Sie wohl erwarten, wenn Sie Ruby-Quellcode schreiben, erzeugt das Referenzieren einer nicht existierenden Instanz-Variablen diese.

Schliesslich holt die Funktion t_add die Instanz-Variable @arr aus dem aktuellen Objekt und ruft Array#push auf, um den übergebenen Wert an das Feld anzuhängen. Wenn man Instanz-Variablen auf diese Weise verwendet, ist das @-Prefix verpflichtend --- andernfalls wird die Variable erzeugt, kann aber von Ruby aus nicht referenziert werden.

Trotz der zusätzlichen, kantigen Syntax, die C verlangt, schreiben Sie immer noch in Ruby --- Zusätzlich zu dem Vorteil, falls erforderlich kompakten und schnellen Code zu erzeugen, können Sie Objekte mit allen Methoden manipulieren, die sie kennen und lieb gewonnen haben.

WARNUNG: Jede C-Funktion, die von Ruby aus aufrufbar ist, muss ein VALUE zurückliefern, auch dann, wenn es nur nur Qnil ist. Andernfalls erhalten Sie wahrscheinlich einen Core Dump (bzw. allgemeine Schutzverletzung).

Wir können die C-Version des Codes in Ruby einfach dadurch nutzen, dass wir sie dynamisch zur Laufzeit (auf den meisten Plattformen) requiren.

require "code/ext/Test"
t = Test.new
t.add("Bill Chase")
C/Ruby Datentyp Umwandlungs-Funktionen und Makros
C Datentypen auf Ruby Objekte:
INT2NUM(int) -> Fixnum oder Bignum
INT2FIX(int) -> Fixnum (schneller)
INT2NUM(long oder int) -> Fixnum oder Bignum
INT2FIX(long oder int) -> Fixnum (schneller)
CHR2FIX(char) -> Fixnum
rb_str_new2(char *) -> String
rb_float_new(double) -> Float
Ruby Objekte auf C Datentypen:
int NUM2INT(Numeric) (Enthält Typprüfung)
int FIX2INT(Fixnum) (Schneller)
unsigned int NUM2UINT(Numeric) (Enthält Typprüfung)
unsigned int FIX2UINT(Fixnum) (Enthält Typprüfung)
long NUM2LONG(Numeric) (Enthält Typprüfung)
long FIX2LONG(Fixnum) (Schneller)
unsigned long NUM2ULONG(Numeric) (Enthält Typprüfung)
char NUM2CHR(Numeric oder String) (Enthält Typprüfung)
char * STR2CSTR(String)
char * rb_str2cstr(String, int *length) Liefert auch die Länge zurück
double NUM2DBL(Numeric)

Auswertung von Ruby Ausdrücken in C

Wenn Sie gerade inmitten von C-Code sind und einen beliebigen Ruby-Ausdruck auswerten wollen ohne einen Haufen C-Code schreiben zu müssen, können Sie immer die C-Version von eval verwenden. Nehmen wir an, wie hätten eine Menge von Objekten, in denen ein Flag gelöscht werden soll.

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

Wenn Sie nur eine spezielle Methode aufrufen wollen (was billiger als ein eval des Strings ist), können Sie

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

Die volle Beschreibung dieser und anderer oft benutzter C Funktionen beginnt auf Seite 189.

Direkte Verwendung von Daten in Ruby und C

Jetzt haben wir genügend Grundlagen behandelt um zu unserem Jukebox-Beispiel zurückzukehren --- die Bedienung von C-Code von Ruby aus und das gemeinsame Verwenden von Daten und Verhalten zwischen den zwei Welten.

Gemeinsames Verwenden von Variablen

Obwohl man versuchen könnte, eine C-Version einiger Variablen immer synchron zu einer eigenen Ruby-Version dieser Variablen zu pflegen, [Eine klare Verletzung des DRY--Wiederhole Dich nicht ---Prinzips, das in unserem Buch The Pragmatic Programmer beschrieben ist.] ist es deutlich besser die Variable direkt von Ruby und C gemeinsam zu verwenden. Man kann globale Variablen gemeinsam nutzen, indem man ein Ruby-Objekt in C erzeugt und dann seine Adresse an eine globale Ruby-Variable bindet. In diesem Fall ist das $-Prefix optional, aber es hilft klarzustellen, dass es sich um eine globale Variable handelt.

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"));

Die Ruby-Seite kann dann die C-Variable hardware_list mittels $hardware ansprechen:

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

Man kann auch hooked (d.h. eingehaengte) Variablen erzeugen, die eine bestimmte Funtion aufrufen, wenn man die Variable verwendet sowie virtuelle Variablen, die nur die hooks aufrufen --- dabei ist keine tatsächliche Variable im Spiel. Für die Details sei auf die API Beschreibung, die auf Seite 192 beginnt, verwiesen.

Wenn Sie ein Ruby-Objekt von C aus erzeugen und es in einer globalen C-Variablen speichern ohne sie an Ruby zu exportieren, müssen Sie sie zumindest dem Garbage-Collector (Speicheraufräumer) bekannt machen, sonst wird sie unbeabsichtigt abgesäbelt:

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

C Strukturen einpacken

Jetzt zum wirklich spassigen Zeug. Wir habe die Bibliothek des Herstellers, die die Audio CD Jukebox kontrolliert und wir sind nun bereit, sie in Ruby zu stecken. Die Headerdateien des Herstellers sehen folgendermassen aus:

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

// Allokiere eine neue CDPlayer-Struktur und bringe sie online CDJukebox *CDPlayerNew(int unit_id);

// Deallokiere wenn alles fertig ist (und bringe sie offline) void CDPlayerDispose(CDJukebox *rec);

// Gehe zu einer CD, track und benachrichtige einen Prozess void CDPlayerSeek(CDJukebox *rec,                   int disc,                   int track,                   void (*done)(CDJukebox *rec, int percent)); // ... anderes Zeugs ... // Statistik Ausgabe double CDPlayerAvgSeekTime(CDJukebox *rec);

Dieser Hersteller weiss, was sich gehört; obwohl er es nicht zugeben würde, ist der Code in einem objektorientierten Stil geschrieben. Wir haben keine Ahnung, was alle diese Felder in der CDJukeBox Struktur bedeuten, aber das ist kein Problem---wir können es wie einen undurchsichtigen Haufen von Bits behandeln. Der Code des Herstellers weiß ja, was er damit anstellen soll und wir müssen es nur mit uns herumschleppen.

Jedesmal, wenn eine reine C-Struktur gegeben ist, die man als Ruby-Objekt behandeln will, sollte man sie in eine spezielle interne Ruby-Klasse mit Namen DATA (mit dem Typ T_DATA) einpacken. Es gibt zwei Makros die genau das machen und ein weiteres um die Struktur wieder auszupacken.

Einpacken von C Datentypen
VALUE  Data_Wrap_Struct(VALUE class, void (*mark)(), void (*free)(), void *ptr")
  Packt den gegebenen C-Datentyp ptr ein, registriert die beiden Garbage-Collection-Routinen (siehe unten), und gibt einen VALUE-Zeiger auf das neue Ruby-Objekt zurück. Der C-Typ des resultierenden Objekts lautet T_DATA und seine Ruby-Klasse ist class.
VALUE  Data_Make_Struct(VALUE class, c-type, void (*mark)(), void (*free)(), c-type *")
  Allokiert zuerst eine Struktur des angegebenen Typs, und macht danach weiter wie Data_Wrap_Struct. c-type ist der Name des C-Datentyps, den wir einpacken, nicht eine Variable dieses Typs.
  Data_Get_Struct(VALUE obj,c-type,c-type *")
  Liefert den ursprünglichen Zeiger. Dieses Makro stellt eine typsichere Version des Makros DATA_PTR(obj) dar, das den Zeiger auswertet.

Das von Data_Wrap_Struct erzeugte Objekt ist ein ganz normales Ruby-Objekt ausser, dass es noch einen zusätzlichen C-Datentyp enthält, der von Ruby aus nicht ansprechbar ist. Wie man der Abb. 17.1 auf Seite 179 entnehmen kann, ist dieser C-Datentyp von allen Instanzvariablen des Objekts getrennt. Wie wird man es jedoch wieder los, nachdem es ja ein seperates Objekt ist, wenn der Garbage-Collector das Objekt entsorgen will? Was ist, wenn man Resourcen freigeben muss (Dateien schliessen, Sperren oder Inter-Prozess-Mechanismen freigeben, oder etwas in der Art)?

C-Datentypen in Objekte wrappen

Figur 17.1 C-Datentypen in Objekte wrappen

Um an Rubys Mark-and-Sweep Garbage-Collection-Prozess teilzunehmen, muss man eine Funktion definieren, die die Struktur wieder frei gibt und möglicherweise auch eine Routine, die alle von ihr referenzierten anderen Strukturen markiert (die dürfen dann nämlich nicht entsorgt werden, der Übersetzer). Beide Routinen bekommen einen void-Zeiger, also eine Referenz auf unsere Struktur als Parameter. Die mark-Routine wird vom Garbage-Collector während seiner ``Markierungsphase'' aufgerufen. Wenn unsere Struktur andere Ruby-Objekte referenziert, dann muss die mark-Routine alle diese Objekte identifizieren, indem sie rb_gc_mark(value) für jedes von ihnen aufruft. Referenziert die Struktur keine weiteren Ruby-Objekte, kann man einfach 0 als Funktionszeiger übergeben.

Wenn das Objekt entsorgt werden soll, ruft der Garbage-Collector die free-Routine auf, um es freizugeben. Wenn Sie irgendwelchen Speicher angefordert haben (beispielsweise mittels Data_Make_Struct), müssen Sie eine free-Funktion bereitstellen---auch dann, wenn es nur die free-Funktion aus der Standard C Bibliothek ist. Wurden komplexe Strukturen allokiert, muss die free-Funktion die Struktur abklappern, und den kompletten angeforderten Speicher wieder freigeben.

Zuerst einmal ein einfaches Beispiel ohne jede Spezialbehandlungen. Mit der Struktur-Definition

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

können wir eine Struktur erzeugen, sie ausfüllen und in ein Objekt einpacken. [In diesem Beispiel schummeln wir ein bisschen. Unsere MP3Info-Struktur enthält ein paar char-Zeiger. In unserem Code initialisieren wir sie aus zwei statischen Strings. Das impliziert, dass wir sie nicht freigeben müssen, wenn die MP3Info-Struktur freigegeben wird. Wenn wir sie dynamisch allokieren würden, müssten wir auch eine free-Funktion zu ihrer Entsorgung bereitstellen.]

MP3Info *p;
VALUE info;

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

info ist ein VALUE-Datentyp, ein generisches Ruby-Objekt der Klasse Test (in C dargestellt durch den eingebauten Typ T_DATA). Man kann ihn in ein Feld stecken, eine Referenz auf ihn in einem Objekt halten und so weiter. An einer späteren Stelle im Code möchten wir die Struktur wieder benutzen, wenn wir den zugehörigen VALUE haben:

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

Um der Konvention zu genügen braucht man unter Umständen noch ein paar andere Dinge: Die Unterstützung einer initialize-Methode sowie einen ``C-Konstruktor.'' Wenn wir Ruby-Quellcode schreiben würden, würden wir ein Objekt mittels eines new-Aufrufs allokieren und initialisieren. Der korrespondierende Aufruf in C-Erweiterungen lautet Data_Make_Struct. Obwohl er den Speicher für das Objekt anfordert, ruft er nicht automatisch eine initialize-Routine auf. Das muss man schon selbst machen:

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

Das hat den Vorteil, dass man damit Unterklassen in Ruby erlaubt, die ursprüngliche initialize-Methode dieser Klasse zu überschreiben oder zu ergänzen. Innerhalb initialize ist es erlaubbar (aber nicht notwendigerweise ratsam) den Datenzeiger direkt über DATA_PTR(obj) zu verwenden .

Schliesslich werden Sie einen ``C-Konstruktor'' definieren---eine global benutzbare C-Funktion, die ein Objekt mit einem einzigen bequemen Aufruf erzeugt. Man kann diese Funktion innerhalb des eigenen Codes verwenden oder anderen Bibliotheks-Erweiterungen die Benutzung erlauben. Alle eingebauten Klassen unterstützen diese Idee mit Funktionen wie rb_str_new, rb_ary_new, u.s.w. Wir können unsere eigene kreieren:

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

Ein Beispiel

O.k., jetzt sind wir reif für ein vollständiges Beispiel. Mit den obigen Headerdateien unseres Herstellers schreiben wir den folgenden Code.

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

Jetzt können wir unsere Jukebox aus Ruby in einer schönen, objektorientierten Weise kontrollieren:

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"
erzeugt:
Unit is 1
26% done
79% done
100% done
Avg. time was 1.2 seconds

Dieses Beispiel verdeutlicht vieles, über das wir bisher gesprochen haben zusammen mit einem zusätzlichen Feature. Die Bibliothek des Herstellers stellte eine Callback-Routine zur Verfügung--- einen Zeiger auf eine Funktion, die jedesmal aufgerufen wird, wenn die Hardware sich der nächsten CD zuwendet. Wir haben diese Funktion so implementiert, dass ein Code-Block, der als Argument an seek übergeben wird, aufgerufen wird. Innerhalb der progress-Funktion schauen wir nach, ob innerhalb des Kontextes ein Iterator existiert und wenn ja, dann lassen wir ihn mit dem aktuellen done-Prozent-Wert als Argument laufen.

Speicher Allokation

Manchmal möchten Sie möglicherweise in einer Erweiterung Speicher anfordern, der nicht als Speicher für irgendein Objekt genutzt werden soll--- vielleicht haben Sie eine gigantische Bitmap für einen Bloom-Filter oder ein Bild oder einen ganzen Haufen kleiner Strukturen, die Ruby nicht direkt benutzt.

Um korrekt mit dem Garbage-Collector zusammenzuarbeiten, sollte man die folgenden Speicher-Allokationsroutinen verwenden. Sie machen ein bisschen mehr als das Standard-malloc. Wenn beispielsweise ALLOC_N feststellt, dass es die angeforderte Menge an Speicher nicht allokieren kann, dann ruft es den Garbage-Collector auf, um unbenutzen Speicher neu zu verwenden. Die Routine produziert einen NoMemError wenn sie die Speicheranforderung nicht erfüllen kann oder wenn die Anforderung nicht gültig ist.

Speicher Anforderung
type *  ALLOC_N(c-type, n")
  Allokiert nc-type Objekte, wobei c-type der literale Name des C-Typs ist, keine Variable dieses Typs.
type *  ALLOC(c-type")
  Allokiert einen c-type und wandelt das Resultat in eine Zeiger auf diesen Typ um.
  REALLOC_N(var, c-type, n")
  Reallokiert nc-types und weist das Ergebnis var zu, einem Zeiger auf ein c-type.
type *  ALLOCA_N(c-type, n")
  Allokiert Stack-Speicher für n Objekte des Typs c-type ---dieser Speicher wird automatisch beim Verlassen der Funktion freigegeben, die ALLOCA_N aufruft.

Erzeugen einer Erweiterung

Nachdem wir den Quellcode einer Erweiterung geschrieben haben, müssen wir ihn nun übersetzen, damit ihn Ruby verwenden kann. Entweder übersetzen wir sie als shared object (also eine dynamische Bibliothek), die dynamisch zur Laufzeit geladen wird, oder wir binden die Erweiterung statisch in den Ruby-Interpreter ein. Die grundlegende Methode dabei ist die gleiche:

Eine Erweiterung erstellen

Figur 17.2 Eine Erweiterung erstellen

Erzeugen eines Makefiles mit Hilfe von extconf.rb

Die Abbildung 17.2 auf Seite 184 zeigt den grundlegenden Ablauf beim Erzeugen einer Erweiterung. Den Schlüssel zum ganzen stellt das extconf.rb-Programm dar, das Sie als der Entwickler erzeugen. In die Datei extconf.rb schreiben sie ein einfaches Programm, das feststellt, welche Features auf dem System des Benutzers vorhanden sind und wo diese abgelegt sein könnten. Die Ausführung von extconf.rb produziert ein angepasstes Makefile, zugeschnitten sowohl auf Ihre Applikation als auch an das System, auf dem es übersetzt wird. Wenn Sie das make Kommando mit diesem Makefile laufen lassen, wird die Erweiterung gebaut und (optional) installiert.

Die einfachste extconf.rb-Datei kann gerade einmal zwei Zeilen lang sein und ist für viele Erweiterungen auch ausreichend.

require 'mkmf'
create_makefile("Test")

Die erste Zeile bindet das mkmf-Bibliotheksmodul (dokumentiert auf den Seiten 455ff) ein. Es enthält alle Befehle, die wir verwenden. Die zweite Zeile erzeugt ein Makefile für eine Erweiterung mit dem Namen ``Test''. (Man beachte, dass ``Test'' der Name der Erweiterung ist; das Makefile heisst immer ``Makefile.'') Test wird aus allen C-Quelldateien im aktuellen Verzeichnis gebaut.

Sagen wir mal, wir lassen dieses extconf.rb-Programm in einem Verzeichnis laufen, das nur eine einzige Quelldatei main.c enthält. Das Ergebnis ist ein Makefile, das unsere Erweiterung bildet. Auf unserem System enthält diese Datei die folgenden Befehle.

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

Das Ergebnis dieser Übersetzung ist Test.so, das wir zur Laufzeit dynamisch mittels ``require'' in Ruby einbinden können. Beachten Sie, wie die mkmf-Befehle plattform-spezifische Bibliotheken und Compiler-Schalter automatisch bestimmt haben. Ganz nett, was?

Wenn dieses Basisprogramm auch für viele einfache Erweiterungen funktioniert, muss man dennoch manchmal etwas mehr Arbeit investieren, beispielsweise, wenn die Erweiterung Headerdateien oder Bibliotheken benötigt, die nicht zur Standardumgebung des Compilers gehören oder wenn man Code abhängig vom Vorhandensein bestimmter Bibliotheken oder Funktionen übersetzen will.

Häufig benötigt wird die Angabe von nicht-standard Verzeichnissen, in denen Includedateien und Bibliotheken gefunden werden können. Dies stellt einen zweistufiger Prozess dar. Zuerst sollte extconf.rb eine oder mehrere dir_config-Kommandos enthalten. Diese spezifizieren einen Bezeichner für eine Menge von Verzeichnissen. Später, beim Start von extconf.rb, sagt man mkmf, wo die korrespondierenden physikalisch vorhandenen Verzeichnisse auf dem jeweiligen System liegen.

Wenn extconf.rb die Zeile dir_config(name) enthält, gibt man ihm die Pfadangabe mit der Kommandozeilenoption

--with-name-include=directory

* Füge directory/include zum Übersetzungskommando hinzu.
--with-name-lib=directory

* Füge directory/lib zu den Linkerkommandos hinzu.

Wenn (wie es meistens der Fall ist) Ihre Include- und Bibliotheks-Verzeichnisse beide Unterverzeichniss eines anderen Verzeichnisses sind und (was ebenfalls gebräuchlich ist) include und lib heissen, gibt es eine Abkürzung:

--with-name-dir=directory

* Füge jeweils directory/lib und directory/include zum Link- bzw. Übersetzungskommando hinzu.

Genauso gut wie sie alle diese --with-Optionen beim Start von extconf.rb angeben können, können sie auch die --with-Optionen verwenden, die angegeben wurden, als Ruby für diese Maschine gebaut wurde. Das bedeutet, dass man die Orte aller Bibliotheken herausfinden kann, die von Ruby selbst verwendet werden.

Um das alles zu konkretisieren, sagen wir einmal, sie möchten die Bibliotheken und Includedateien für die CD-Jukebox nutzen, die wir entwickeln. Ihr extconf.rb-Programm könnte dies enthalten

require 'mkmf'
dir_config('cdjukebox')
# .. anderes Zeugs
create_makefile("CDJukeBox")

Danach könnte man extconf.rb etwa so aufrufen:

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

Das erzeugte Makefile geht dann davon aus, dass die Bibliotheken in /usr/local/cdjb/lib und die Includedateien in /usr/local/cdjb/include liegen.

Der dir_config-Aufruf fügt zur Suchliste für Bibliotheken und Includedateien die Verzeichnisse hinzu. Er kümmert sich jedoch nicht darum, die Bibliotheken zu Ihrer Anwendung hinzuzubinden. Um das zu bewerkstelligen, muss man eines oder mehrere have_library- bzw. find_library-Kommandos verwenden.

have_library sucht nach einem Einsprungpunkt in einer gegebenen Bibliothek. Wenn er gefunden wurde, wird die Bibliothek an die Liste der zu ihrer Applikation anzubindenen Bibliotheken angehängt. find_library arbeitet ähnlich, erlaubt Ihnen aber noch die zusätzliche Angabe einer Liste von Verzeichnissen, in denen nach der Bibliothek gesucht werden soll.

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

Auf einigen Plattformen können Bibliotheken an einer von mehreren möglichen Stellen liegen. Das X-Window-System ist beispielsweise bekannt dafür, auf unterschiedlichen Systemen in verschiedenen Verzeichnissen zu liegen. Der find_library-Aufruf sucht eine Liste aller angegebenen Verzeichnisse nach dem richtigen Eintrag ab. (Dies ist ein Unterschied zu have_library, das ausschliesslich Konfigurations-Informationen für die Suche benutzt). Um beispielsweise ein Makefile zu erzeugen, das X-Windows und eine jpeg-Bibliothek verwendet, könnte extconf.rb folgendes enthalten:

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

Zu diesem Programm wurden noch etwas zusätzliche Funktionalität hinzugefügt. Alle mkmf-Aufrufe liefern false, wenn sie nicht erfolgreich waren. Das bedeutet, dass wir ein extconf.rb schreiben können, das nur dann ein Makefile erzeugt, wenn alle benötigten Teile vorhanden sind. Die Ruby-Distribution benutzt dies auf die Weise, dass nur diejenigen Erweiterungen übersetzt werden, die vom System auch unterstützt werden.

Möglicherweise soll Ihre Erweiterungen an die Gegebenheiten des Zielsystems anpassbar sein. Beispielsweise könnte unsere CD-Jukebox einen hoch performanten MP3-Dekoder verwenden, wenn der Endbenutzer einen solchen installiert hat. Dies lässt sich durch eine Suche nach seinen Includedateien überprüfen.

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

Genauso können wir nachsehen, ob das Zielsystem eine bestimmte Funktion in einer der Bibliotheken, die wir benutzen, zur Verfügung stellt. Beispielsweise wäre die setpriority-Funktion nützlich, aber sie ist nicht immer vorhanden. Mit folgendem Code lässt sich dies überprüfen:

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

Die beiden Funktionen have_header und have_func definieren Preprozessor-Konstanten, wenn sie ihre Ziele finden. Die Konstanten werden gebildet, indem das Ziel in Großbuchstaben umgewandelt und an ``HAVE_'' angehängt wird. Der C-Code kann daraus Nutzen ziehen mit Konstruktionen wie:

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

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

Wenn spezielle Erfordernisse auftreten, die nicht mit all diesen mkmf-Befehlen erschlagen werden können, kann das Programm auch direkt Strings an die globalen Variablen $CFLAGS und $LFLAGS anhängen, die an den Compiler und den Linker übergeben werden. %%--------------------------

Dynamisches Binden

Der oben beschriebene Arbeitsablauf erlaubt es einem, eine Erweiterung von Ruby in einem willkürlichen Ordner zu erzeugen, was wir auch bei den meisten unwerer Projekte machen, und was du auch machen solltest.

Erweiterungs-Verzeichnis unter ext/ in der Ruby-Distribution. Wenn man einfach nur die Dteien extconf.rb und MANIFEST in seinem Verzeichnis hat, so läuft die Ruby-Build-Prozedur automatisch und wird die daraus resultierende Makefile-Datei benutzen, um die Erweiterung zu erzeugen und im Ruby-Bibliotheks-Pfad zu installieren (das ist ein versions- und maschinen- abhängiger Pfad, bei uns ist das /usr/local/lib/ruby/1.6/i686-linux.

Statisches Binden

Schlussendlich, wenn Ihr System kein dynamisches Binden erlaubt oder wenn Sie Ihre Erweiterung statisch zu Ruby hinzulinken wollen, editieren Sie die Datei ext/Setup in der Distribution und fügen Sie Ihr Verzeichnis zur Liste der Erweiterungen in ihm hinzu, bevor Sie Ruby neu erzeugen. Die Erweiterungen, die in Setup aufgeführt sind, werden statisch zu Ruby gebunden. Sollten Sie das dynamische Binden abschalten und alle Erweiterungen statisch binden wollen, editieren Sie ext/Setup so, dass es die folgende Option enthält.

option nodynamic

Einbetten eines Ruby Interpreters

Zusätzlich zum Erweitern von Ruby durch Hinzufügen von C-Code kann man auch das Problem umdrehen und Ruby selbst in seine Applikation einbauen. Hier ist ein Beispiel.

#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 */   } }

Zur Initialisierung des Ruby-Interpreters müssen Sie ruby_init() aufrufen. Allerdings sind auf manchen Plattformen vorher noch spezielle Schritte nötig:

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

In main.c der Ruby-Distribution kann man andere spezielle Defines oder Initialisierungen finden, die für Ihre Plattform benötigt werden.

Schnittstellenroutinen für eingebettetes Ruby
void  ruby_init(")
  Initialisiert den Interpreter. Diese Funktion sollte vor allen anderen Ruby-bezogenen Funktionen aufgerufen werden.
void  ruby_options(int argc, char **argv")
  Setzt die Kommandozeilen-Optionen für den Ruby-Interpreter.
void  ruby_script(char *name")
  Setzt den Namen des Ruby-Skripts (sowie $0) auf name.
void  rb_load_file(char *file")
  Lädt die angegebene Datei in den Interpreter.
void  ruby_run(")
  Startet den Interpreter.

Man sollte besondere Sorgfalt bei der Behandlung von Ausnahmen walten lassen; alle Ruby-Aufrufe, die auf dieser hohen Stufe gemacht werden, sollten geschützt werden indem sie Ausnahmen abfangen und sie sauber behandeln. rb_protect, rb_rescue und verwandte Funktionen werden auf Seite 194 dokumentiert.

Als Beispiel für die Einbettung eines Ruby-Interpreters in ein anderes Programm dient auch eruby, das ab Seite 149 beschrieben ist.

Ruby an andere Sprachen anbinden

Bisher haben wir die Erweiterung von Ruby mithilfe on C-Routinen behandelt. Es ist aber möglich, Erweiterung in nahezu jeder Sprache zu schreiben, solange man die Sprachen mit C-Mitteln zusammenbringen kann. Es ist nahezu alles mÖglich, dies schliesst auch das heikle Verheiraten von Ruby und C++, Ruby und Java und so weiter ein.

Man kann das selbe jedoch auch ohne den Rückgriff auf C-Code erledigen. Zum Beispiel könnte man Brücken zu anderen Sprachen mittels Middleware wie CORBA oder COM schlagen. Für die Details siehe den Abschnitt über Windows Automatisierung beginnend auf Seite 166.

Ruby C API

Zuguterletzt aber auf keinem Fall unwichtig sind hier einige für das Schreiben von Erweiterungen nützliche C-Funktionen.

Einige Funktionen erfordern eine ID: Man erhält eine ID für einen String aus rb_intern und die Rekonstruktion des Names aus einer ID durch rb_id2name.

Nachdem nahezu alle dieser C-Funktionen Ruby-Äquivalente besitzen, die anderswo in diesem Buch detailiert beschrieben sind, sind die Erklärungen hier kurz gehalten.

Beachten Sie ausserdem, dass die folgende Liste nicht vollständig ist. Es gibt viel mehr Funktionen---zu viele um sie alle zu dokumentieren, wie sich herausstellt. Wenn Sie eine Methode benötigen, die Sie hier nicht finden, fahnden Sie in ``ruby.h'' oder ``intern.h'' nach wahrscheinlichen Kandidaten. Am Ende oder nahe am Ende jeder Quelldatei steht auch eine Menge von Methoden-Definitionen, die die Anbindung von Ruby-Methoden an C-Funktionen beschreiben. Sie können die C-Funktionen direkt aufrufen oder nach Wrapper-Funktionen suchen, die dies intern tut. Die folgende Tabelle, basierend auf der Tabelle in README.EXT, zeigt die wichtigsten Quelldateien im Interpreter.

Grundlegende Ruby Sprachkonstrukte

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

dln.c regex.c st.c util.c
Ruby-Interpreter

dmyext.c inits.c keywords main.c ruby.c version.c
Basis-Bibliothek

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

Definieren von Objekten
VALUE  rb_define_class(char *name, VALUE superclass")
  Definiert eine neue Klasse auf oberstem Level mit dem angegebenen Namen name sowie der Oberklasse superclass. (Für die Klasse Object benutzt man rb_cObject).
VALUE  rb_define_module(char *name")
  Definiert ein neues Modul auf oberstem Level mit dem angegebenen Namen name.
VALUE  rb_define_class_under(VALUE under, char *name, VALUE superclass")
  Definiert eine eingebettete Klasse unterhalb der Klasse oder des Moduls under.
VALUE  rb_define_module_under(VALUE under, char *name")
  Definiert ein eingebettetes Modul unterhalb der Klasse oder des Moduls under.
void  rb_include_module(VALUE parent, VALUE module")
  Fügt das angegebenene Modul module in die Klasse oder das Modul parent ein.
void  rb_extend_object(VALUE obj, VALUE module")
  Erweitert das Objekt obj durch das Modul module.
VALUE  rb_require(const char *name")
  Gleichbedeutend mit ``require name.'' Liefert Qtrue oder Qfalse zurück.

In einigen der folgenden Funktionen spezifiziert der Parameter argc die Anzahl der Argumente, die eine Ruby-Methode erwartet. Er kann die folgenden Werte annehmen.

argc Funktions-Prototyp
0..17 VALUE func(VALUE self, VALUE arg...)
Die C-Funktion wird mit dieser Anzahl tatsächlicher Argumente gerufen.
-1 VALUE func(int argc, VALUE *argv, VALUE self)
Die C-Funktion bekommt eine variable Anzahl Argumente, die als C-Feld übergeben werden.
-2 VALUE func(VALUE self, VALUE args)
Die C-Funktion bekommt eine variable Anzahl Argumente, die als Ruby-Feld übergeben werden.

Innerhalb einer Funktion, an die eine variable Anzahl Argumente übergeben wird, kann man die C-Funktion rb_scan_args nutzen, um an die eigentlichen Argumente zu gelangen (siehe unten).

Definition von Methoden
void  rb_define_method(VALUE classmod, char *name, VALUE(*func)(), int argc")
  Definiert eine Instanzmethode namens name in der Klasse oder dem Modul classmod, die durch die C-Funktion func implementiert wird, welche argc Argumente erwartet.
void  rb_define_module_function(VALUE classmod, char *name, VALUE(*func)(), int argc)")
  Definiert eine Methode namens name in der Klasse classmod, implementiert durch die C-Funktion func, die argc Argumente erwartet.
void  rb_define_global_function(char *name, VALUE(*func)(), int argc")
  Definiert eine globale Funktion (eine private Methode von Kernel) mit Namen name, die durch die C-Funktion func implementiert wird und die argc Argumente erwartet.
void  rb_define_singleton_method(VALUE classmod, char *name, VALUE(*func)(), int argc")
  Definiert eine Singleton-Methode in der Klasse classmod mit dem angegeben Namen name, implementiert durch die C-Funktion func, die argc Argumente erwartet.
int  rb_scan_args(int argcount, VALUE *argv, char *fmt, ...")
  Durchläuft die Argumentliste und weist das Ergebnis Variablen zu ähnlich wie scanf: fmt ist eine Zeichenkette bestehend aus Null, einem oder zwei Ziffern gefolgt von einigen Spezifikationsbuchstaben. Die erste Ziffer gibt die Anzahl der unbedingt nötigen Argumente an; die zweite ist die Anzahl optionaler Argumente. Das Zeichen ``*'' bedeutet, dass der Rest der Argumente in ein Ruby-Feld gepackt werden soll. Ein ``&'' heißt, dass ein angehängter Code-Block übernommen und der angegebenen Variablen zugewiesen wird (Wurde kein Code-Block angegeben, wird Qnil zugewiesen). Nach dem fmt-String werden (genau wie bei scanf) Zeiger auf VALUE erwartet, denen dann die Argumente zugewiesen werden.

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")
  Löscht die angegebene Methode name aus der angegebenen Klasse oder dem Modul classmod.
void  rb_define_alias(VALUE classmod, const char *newname, const char *oldname")
  Definiert einen Alias für oldname in der Klasse oder dem Modul classmod.

Definition von Variablen und Konstanten
void  rb_define_const(VALUE classmod, char *name, VALUE value")
  Definiert eine Konstante namens name mit dem Wert value in der Klasse oder dem Modul classmod.
void  rb_define_global_const(char *name, VALUE value")
  Definiert eine globale Konstante namens name mit dem Wert value.
void  rb_define_variable(const char *name, VALUE *object")
  Exportiert die Adresse des in C erzeugten angegeben Objektes object in den Ruby-Namensraum als name. Aus Rubysicht ist es eine globale Variable, folglich sollte der Namen name mit einem führenden Dollar-Zeichen beginnen. Stellen Sie sicher, dass sie Ruby's Regeln für gültige Variablennamen berücksichtigen; Variablen mit illegalem Namen sind von Ruby aus nicht ansprechbar.
void  rb_define_class_variable(VALUE class, const char *name, VALUE val")
  Definiert eine Klassenvariable namens name (die ein ``@@'' Prefix enthalten muss) in der angegeben Klasse class und initialisiert sie mit dem Wert value.
void  rb_define_virtual_variable(const char *name, VALUE(*getter)(), void(*setter)()")
  Exportiert eine virtuelle Variable in den Ruby-Namensraum als globale Variable $name. Es existiert kein Speicherplatz für diese Variable. Versuche, sie zu lesen bzw. schreiben rufen die angegebenen Funktionen mit den folgenden Prototypen auf:

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

Wahrscheinlich werden Sie den entry-Parameter nicht benötigen und können ihn problemlos in ihren Funktionsdeklarationen auslassen.
void  rb_define_hooked_variable(const char *name, VALUE *variable, VALUE(*getter)(), void(*setter)()")
  Definiert Funktionen, die aufgerufen werden, wenn eine Variable variable gelesen oder geschrieben wird. Siehe auch rb_define_virtual_variable.
void  rb_define_readonly_variable(const char *name, VALUE *value")
  Genau wie rb_define_variable, aber die Variable ist von Ruby aus nur lesbar.
void  rb_define_attr(VALUE variable, const char *name, int read, int write")
  Erzeugt Zugriffsmethoden für die angegebene Variable variable, mit Namen name. Wenn read ungleich null ist, erzeuge eine Lesemethode; Wenn write ungleich null ist, erzeuge eine Schreibmethode.
void  rb_global_variable(VALUE *obj")
  Registriert die angegebene Adresse beim Garbage-Collector.

Aufruf von Methoden
VALUE  rb_funcall(VALUE recv, ID id, int argc, ...")
  Ruft die Methode auf, die durch id gegeben ist im Objekt recv mit der angegebenen Variablenanzahl argc und den spezifizierten Argumenten (möglicherweise auch gar keinem).
VALUE  rb_funcall2(VALUE recv, ID id, int argc, VALUE *args")
  Ruft die Methode auf, die durch id gegeben ist im Objekt recv mit der angegebenen Variablenanzahl argc und den Argumenten im C Feld args.
VALUE  rb_funcall3(VALUE recv, ID id, int argc, VALUE *args")
  Wie rb_funcall2, ruft aber keine privaten Methoden auf.
VALUE  rb_apply(VALUE recv, ID name, int argc, VALUE args")
  Ruft die Methode auf, die durch id gegeben ist im Objekt recv mit der angegebenen Variablenanzahl argc und den Argumenten im Ruby Arrayargs.
ID  rb_intern(char *name")
  Liefert die ID zu einem gegebenen Namen name. Wenn der Namen nicht existiert wird ein Eintrag in der Symboltabelle für ihn erzeugt.
char *  rb_id2name(ID id")
  Liefert den Namen zu einer gegebenen id.
VALUE  rb_call_super(int argc, VALUE *args")
  Ruft die aktuelle Methode in der Oberklasse des aktuellen Objekts auf.

Exceptions (Ausnahmen)
void  rb_raise(VALUE exception, const char *fmt, ...")
  Wirft eine Ausnahmebedingung exception. Die angegebene Zeichenkette fmt sowie die übrigen Argumente werden genauso interpretiert wie bei printf.
void  rb_fatal(const char *fmt, ...")
  Wirft eine Fatal-Ausnahme, die den Prozess beendet. Es werden keine rescue- aber ensure-Blöcke abgearbeitet. Die angegebene Zeichenkette fmt sowie die übrigen Argumente werden genauso interpretiert wie bei printf.
void  rb_bug(const char *fmt, ...")
  Beendet den Prozess sofort---es werden keinerlei Behandlungsroutinen aufgerufen. Die angegebene Zeichenkette fmt sowie die übrigen Argumente werden genauso interpretiert wie von printf. Sie sollten diese Funktion nur dann aufrufen, wenn ein fataler Fehler entdeckt wurde. Sie schreiben aber keine Programme mit fatalen Fehlern, oder doch?
void  rb_sys_fail(const char *msg")
  Wirft eine plattform-spezifische Ausnahmebedingung, die zum letzten bekannten Systemfehler gehört, zusammen mit der Meldung msg.
VALUE  rb_rescue(VALUE (*body)(), VALUE args, VALUE(*rescue)(), VALUE rargs")
  Führt den Block body mit den Argumenten args aus. Wenn eine StandardError-Ausnahme auftritt, führe auch den Block rescue mit den Argumenten rargs aus.
VALUE  rb_ensure(VALUE(*body)(), VALUE args, VALUE(*ensure)(), VALUE eargs")
  Ruft den Block body mit den Argumenten args aus. Egal, ob eine Ausnahme auftritt oder nicht, führe immer den ensure Block mit den Argumenten rargs aus, sobald body beendet ist.
VALUE  rb_protect(VALUE (*body)(), VALUE args, int *result")
  Ruft den Block body mit den Argumenten args auf und liefert etwas ungleich Null im Ergebnis result, wenn dabei irgendeine Ausnahme auftrat.
void  rb_notimplement(")
  Wirft eine NotImpError-Ausnahme um anzuzeigen, dass die eingeschlossene Funktion noch nicht implementiert oder auf dieser Plattform nicht verfügbar ist.
void  rb_exit(int status")
  Beendet Ruby mit dem Status status. Wirft eine SystemExit-Ausnahme und ruft alle registrierten exit Funktionen sowie finalize-Funktionen auf.
void  rb_warn(const char *fmt, ...")
  Produziert (immer) eine Warnmeldung auf dem Standardfehlerausgang. Die Zeichenkette fmt sowie die übrigen Argumente werden wie bei printf interpretiert.
void  rb_warning(const char *fmt, ...")
  Produziert nur dann eine eine Warnmeldung auf dem Standardfehlerausgang, wenn Ruby mit dem -w flag aufgerufen wurde. Die Zeichenkette fmt sowie die übrigen Argumente werden wie bei printf interpretiert.

Iteratoren
void  rb_iter_break(")
  Springt aus dem umgebenden Iterator-Block heraus.
VALUE  rb_each(VALUE obj")
  Ruft die each Methode für das Objekt obj auf.
VALUE  rb_yield(VALUE arg")
  Übergibt die Ausführung an den Iterator-Block im momentanen Kontext und übergibt das Argumente arg an ihn. Mehrere Werte können als Feld übergeben werden.
int  rb_block_given_p(")
  Liefert true (wahr) wenn yield einen Block im momentanen Kontext ausführen würde---d.h., wenn ein Code-Block an die aktuelle Methode übergeben wurde und seine Ausführung möglich ist.
VALUE  rb_iterate(VALUE (*method)(), VALUE args, VALUE (*block)(), VALUE arg2")
  Ruft die Methode method mit den Argumenten args und dem Block block auf. Ein yield aus dieser Methode ruft block mit den Argumenten args und einem weiteren Argument arg2 auf.
VALUE  rb_catch(const char *tag, VALUE (*proc)(), VALUE value")
  Gleichbedeutend mit Ruby's catch.
void  rb_throw(const char *tag , VALUE value")
  Äquivalent zu Ruby's throw.

Zugang zu Variablen
VALUE  rb_iv_get(VALUE obj, char *name")
  Liefert die Instanz-Variable namens name (der mit einem ``@''-Prefix beginnen muss) des Objekts obj.
VALUE  rb_ivar_get(VALUE obj, ID name")
  Liefert die Instanz-Variable name des Objekts obj.
VALUE  rb_iv_set(VALUE obj, char *name, VALUE value")
  Setzt den Wert der Instanz-Variablen namens name (der mit einem ``@''-Prefix beginnen muss) im Objekt obj auf value, der auch zurückgeliefert wird.
VALUE  rb_ivar_set(VALUE obj, ID name, VALUE value")
  Setzt den Wert der Instanz-Variablen namens name (der mit einem ``@''-Prefix beginnen muss) im Objekt obj auf value, der auch zurückgeliefert wird.
VALUE  rb_gv_set(const char *name, VALUE value")
  Setzt die globale Variable namens name (das ``$''-Prefix ist optional) auf value. Liefert value zurück.
VALUE  rb_gv_get(const char *name")
  Liefert den Wert der globalen Variable name (das ``$''-Prefix ist optional).
void  rb_cvar_set(VALUE class, ID name, VALUE val")
  Setzt die Klassenvariable name in der Klasse class auf value.
VALUE  rb_cvar_get(VALUE class, ID name")
  Liefert den Wert der Klassenvariablen name der Klasse class.
int  rb_cvar_defined(VALUE class, ID name")
  Liefert Qtrue wenn die Klassenvariable name in class bereits definiert wurde; ansonsten liefert es Qfalse.
void  rb_cv_set(VALUE class, const char *name, VALUE val")
  Setzt die Klassenvariable namens name (der mit einem ``@@''-Prefix beginnen muss) in der Klasse class auf den Wert value.
VALUE  rb_cv_get(VALUE class, const char *name")
  Liefert den Wert der Klassenvariablen namens name (der mit einem ``@@''-Prefix beginnen muss) der Klasse class.

Status von Objekten
  OBJ_TAINT(VALUE obj")
  Markiert das gegebene Objekt obj als tainted (verdorben).
int  OBJ_TAINTED(VALUE obj")
  Liefert etwas ungleich Null, wenn obj als tainted markiert ist.
  OBJ_FREEZE(VALUE obj")
  Markiert das Objekt obj als frozen (eingefroren/unveränderbar).
int  OBJ_FROZEN(VALUE obj")
  Liefert etwas ungleich Null, wenn obj als frozen markiert ist.
  Check_SafeStr(VALUE str")
  Wirft eine SecurityError-Ausnahme, wenn der derzeitige Sicherheitslevel > 0 und str als tainted markiert ist, oder eine TypeError-Ausnahme, wenn str kein T_STRING ist.
int  rb_safe_level(")
  Liefert den momentanen Sicherheitslevel zurück.
void  rb_secure(int level")
  Wirft eine SecurityError-Ausnahme, wenn level <= derzeitiger Sicherheitslevel.
void  rb_set_safe_level(int newlevel")
  Setzt den derzeitigen Sicherheitslevel auf newlevel.

Häufig eingesetzte Methoden
VALUE  rb_ary_new(")
  Liefert ein neues Array der Defaultgröße.
VALUE  rb_ary_new2(long length")
  Liefert ein neues Array der angegebenen Länge length.
VALUE  rb_ary_new3(long length, ...")
  Liefert ein neues Array der angegebenen Länge length, das mit den restlichen Argumenten bevölkert wird.
VALUE  rb_ary_new4(long length, VALUE *values")
  Liefert ein neues Array der angegebenen Länge length, das mit den Werten des C-Feldes values besetzt wird.
void  rb_ary_store(VALUE self, long index, VALUE value")
  Speichert den Wert value an die Indexposition index des Feldes self.
VALUE  rb_ary_push(VALUE self, VALUE value")
  Hängt den Wert value ans Ende des Feldes self an. Liefert value.
VALUE  rb_ary_pop(VALUE self")
  Entfernt und liefert das letzte Element des Feldes self.
VALUE  rb_ary_shift(VALUE self")
  Entfernt und liefert das erste Element des Feldes self.
VALUE  rb_ary_unshift(VALUE self, VALUE value")
  Hängt den Wert value am Beginn des Feldes self ein. Liefert value.
VALUE  rb_ary_entry(VALUE self, long index")
  Liefert das index.te Elemente des Feldes self.
int  rb_respond_to(VALUE self, ID method")
  Liefert einen Wert ungleich Null, wenn self auf die Methode method antwortet.
VALUE  rb_thread_create(VALUE (*func)(), void *data")
  Startet die Funktion func in einem neuen Thread, wobei data als Argument übergeben wird.
VALUE  rb_hash_new(")
  Liefert einen neuen, leeren Hash (assoziatives Feld).
VALUE  rb_hash_aref(VALUE self, VALUE key")
  Liefert das zum Schlüssel key in self gehörende Element.
VALUE  rb_hash_aset(VALUE self, VALUE key, VALUE value")
  Setzt in self den Wert des zum Schlüssel key gehörenden Elements auf value. Liefert value.
VALUE  rb_obj_is_instance_of(VALUE obj, VALUE klass")
  Liefert Qtrue, falls obj eine Instanz der Klasse klass ist.
VALUE  rb_obj_is_kind_of(VALUE obj, VALUE klass")
  Liefert Qtrue, falls klass die Klasse oder eine der Oberklassen von obj ist.
VALUE  rb_str_new(const char *src, long length")
  Liefert einen neuen String, der mit den length Zeichen aus src initialisiert wird.
VALUE  rb_str_new2(const char *src")
  Liefert einen neuen String, der mit dem nullterminierten C string src initialisiert wird.
VALUE  rb_str_dup(VALUE str")
  Liefert ein neues String Objekt, das eine Kopie von str enthält.
VALUE  rb_str_cat(VALUE self, const char *src, long length")
  Hängt die length Zeichen von src an den Stringself an. Liefert self.
VALUE  rb_str_concat(VALUE self, VALUE other")
  Hängt other an den Stringself an. Liefert self.
VALUE  rb_str_split(VALUE self, const char *delim")
  Liefert ein Feld von String-Objekten, die durch Auftrennen von self an den Stellen delim entstehen.


Extracted from the book "Programming Ruby - The Pragmatic Programmer's Guide"
Übersetzung: Martin Kahlert
Für das englische Original:
© 2000 Addison Wesley Longman, Inc. Released under the terms of the Open Publication License V1.0. That reference is available for download.
Diese Lizenz sowie das Original vom Herbst 2001 bilden die Grundlage der Übersetzung
Es wird darauf hingewiesen, dass sich die Lizenz des englischen Originals inzwischen geändert hat.
Für die deutsche Übersetzung:
© 2002 Jürgen Katins
Der Copyright-Eigner stellt folgende Lizenzen zur Verfügung:
Nicht-freie Lizenz:
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.
Freie Lizenz:
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".