Erweiterungsbibliotheken für Ruby erstellen
Dieses Dokument erklärt, wie man Erweiterungsbibliotheken für Ruby erstellt.
Grundkenntnisse
In C haben Variablen Typen und Daten haben keine Typen. Im Gegensatz dazu haben Ruby-Variablen keinen statischen Typ, und Daten selbst haben Typen, sodass Daten zwischen den Sprachen konvertiert werden müssen.
Objekte in Ruby werden durch den C-Typ 'VALUE' repräsentiert. Jede VALUE-Daten hat ihren Datentyp.
Um C-Daten aus einem VALUE abzurufen, müssen Sie
-
den Datentyp des VALUE identifizieren
-
das VALUE in C-Daten konvertieren
Die Konvertierung in den falschen Datentyp kann zu schwerwiegenden Problemen führen.
Ruby-Datentypen
Der Ruby-Interpreter hat die folgenden Datentypen
- T_NIL
-
nil
- T_OBJECT
-
gewöhnliches Objekt
- T_CLASS
-
Klasse
- T_MODULE
-
Modul
- T_FLOAT
-
Gleitkommazahl
- T_STRING
-
Zeichenkette
- T_REGEXP
-
regulärer Ausdruck
- T_ARRAY
-
Array
- T_HASH
-
assoziatives Array
- T_STRUCT
-
(Ruby) Struktur
- T_BIGNUM
-
mehrere Genauigkeits-Ganzzahl
- T_FIXNUM
-
Fixnum (31-Bit- oder 63-Bit-Ganzzahl)
- T_COMPLEX
-
komplexe Zahl
- T_RATIONAL
-
rationale Zahl
- T_FILE
- T_TRUE
-
wahr
- T_FALSE
-
falsch
- T_DATA
-
Daten
- T_SYMBOL
-
Symbol
Zusätzlich gibt es mehrere andere Typen, die intern verwendet werden
- T_ICLASS
-
eingebundenes Modul
- T_MATCH
-
MatchDataObjekt - T_UNDEF
-
undefiniert
- T_NODE
-
Syntaxbaum-Knoten
- T_ZOMBIE
-
Objekt wartet auf Finalisierung
Die meisten Typen werden durch C-Strukturen repräsentiert.
Typ der VALUE-Daten überprüfen
Das Makro TYPE() in ruby.h zeigt den Datentyp des VALUE an. TYPE() gibt die Konstante T_XXXX zurück, die oben beschrieben wurde. Um Datentypen zu handhaben, wird Ihr Code etwa so aussehen
switch (TYPE(obj)) {
case T_FIXNUM:
/* process Fixnum */
break;
case T_STRING:
/* process String */
break;
case T_ARRAY:
/* process Array */
break;
default:
/* raise exception */
rb_raise(rb_eTypeError, "not valid value");
break;
}
Es gibt die Funktion zur Überprüfung des Datentyps
void Check_Type(VALUE value, int type)
die eine Ausnahme auslöst, wenn das VALUE nicht den angegebenen Typ hat.
Es gibt auch schnellere Prüfmakros für Fixnums und nil.
FIXNUM_P(obj) NIL_P(obj)
VALUE in C-Daten konvertieren
Die Daten für die Typen T_NIL, T_FALSE, T_TRUE sind nil, false bzw. true. Sie sind Singletons für den Datentyp. Die entsprechenden C-Konstanten sind: Qnil, Qfalse, Qtrue. RTEST() gibt true zurück, wenn ein VALUE weder Qfalse noch Qnil ist. Wenn Sie Qfalse von Qnil unterscheiden müssen, testen Sie speziell gegen Qfalse.
Die T_FIXNUM-Daten sind feste Ganzzahlen mit 31 oder 63 Bit Länge. Diese Größe hängt von der Größe von long ab: wenn long 32 Bit beträgt, ist T_FIXNUM 31 Bit, wenn long 64 Bit beträgt, ist T_FIXNUM 63 Bit. T_FIXNUM kann mit dem Makro FIX2INT() oder FIX2LONG() in eine C-Ganzzahl konvertiert werden. Obwohl Sie überprüfen müssen, dass die Daten wirklich FIXNUM sind, bevor Sie sie verwenden, sind sie schneller. FIX2LONG() löst niemals Ausnahmen aus, aber FIX2INT() löst RangeError aus, wenn das Ergebnis größer oder kleiner als die Größe von int ist. Es gibt auch NUM2INT() und NUM2LONG(), die beliebige Ruby-Zahlen in C-Ganzzahlen konvertieren. Diese Makros enthalten eine Typenprüfung, sodass eine Ausnahme ausgelöst wird, wenn die Konvertierung fehlschlägt. NUM2DBL() kann auf die gleiche Weise verwendet werden, um den Double-Float-Wert abzurufen.
Sie können die Makros StringValue() und StringValuePtr() verwenden, um ein char* aus einem VALUE zu erhalten. StringValue(var) ersetzt den Wert von var durch das Ergebnis von „var.to_str()“. StringValuePtr(var) tut dasselbe und gibt die char*-Darstellung von var zurück. Diese Makros überspringen die Ersetzung, wenn var ein String ist. Beachten Sie, dass die Makros nur das lvalue als Argument nehmen, um den Wert von var an Ort und Stelle zu ändern.
Sie können auch das Makro namens StringValueCStr() verwenden. Dies ist dasselbe wie StringValuePtr(), fügt aber immer ein NUL-Zeichen am Ende des Ergebnisses hinzu. Wenn das Ergebnis ein NUL-Zeichen enthält, löst dieses Makro die ArgumentError Ausnahme aus. StringValuePtr() garantiert nicht die Existenz eines NUL am Ende des Ergebnisses, und das Ergebnis kann NUL enthalten.
Andere Datentypen haben entsprechende C-Strukturen, z. B. struct RArray für T_ARRAY usw. Das VALUE des Typs, der die entsprechende Struktur hat, kann gecastet werden, um den Zeiger auf die Struktur abzurufen. Das Casting-Makro hat die Form RXXXX für jeden Datentyp; zum Beispiel RARRAY(obj). Siehe „ruby.h“. Wir empfehlen jedoch nicht, RXXXX-Daten direkt zuzugreifen, da diese Datenstrukturen komplex sind. Verwenden Sie die entsprechenden rb_xxx()-Funktionen, um auf die interne Struktur zuzugreifen. Um beispielsweise auf einen Eintrag eines Arrays zuzugreifen, verwenden Sie rb_ary_entry(ary, offset) und rb_ary_store(ary, offset, obj).
Es gibt einige Zugriffs-Makros für Strukturmitglieder, zum Beispiel 'RSTRING_LEN(str)', um die Größe des Ruby- String Objekts zu erhalten. Der zugewiesene Bereich kann über 'RSTRING_PTR(str)' zugegriffen werden.
Hinweis: Ändern Sie den Wert der Struktur nicht direkt, es sei denn, Sie sind für das Ergebnis verantwortlich. Dies führt zu interessanten Fehlern.
C-Daten in VALUE konvertieren
Um C-Daten in Ruby-Werte zu konvertieren
- FIXNUM
-
links um 1 Bit verschieben und das niedrigste Bit (LSB) einschalten.
- Andere Zeigerwerte
-
nach VALUE casten.
Sie können feststellen, ob ein VALUE ein Zeiger ist oder nicht, indem Sie seinen LSB überprüfen.
Hinweis: Ruby erlaubt keine beliebigen Zeigerwerte als VALUE. Sie sollten Zeiger auf Strukturen sein, die Ruby kennt. Die bekannten Strukturen sind in <ruby.h> definiert.
Um C-Zahlen in Ruby-Werte zu konvertieren, verwenden Sie diese Makros
- INT2FIX()
-
für Ganzzahlen bis 31 Bit.
- INT2NUM()
-
für Ganzzahlen beliebiger Größe.
INT2NUM() konvertiert eine Ganzzahl in ein Bignum, wenn sie außerhalb des FIXNUM-Bereichs liegt, ist aber etwas langsamer.
Ruby-Objekte manipulieren
Wie bereits erwähnt, wird nicht empfohlen, die interne Struktur eines Objekts zu ändern. Verwenden Sie zur Manipulation von Objekten die vom Ruby-Interpreter bereitgestellten Funktionen. Einige (nicht alle) nützlichen Funktionen sind unten aufgeführt
String-Funktionen
- rb_str_new(const char *ptr, long len)
-
Erstellt eine neue Ruby-Zeichenkette.
- rb_str_new2(const char *ptr)
- rb_str_new_cstr(const char *ptr)
-
Erstellt eine neue Ruby-Zeichenkette aus einer C-Zeichenkette. Dies ist äquivalent zu rb_str_new(ptr, strlen(ptr)).
- rb_str_new_literal(const char *ptr)
-
Erstellt eine neue Ruby-Zeichenkette aus einem C-Zeichenketten-Literal.
- rb_sprintf(const char *format, …)
- rb_vsprintf(const char *format, va_list ap)
-
Erstellt eine neue Ruby-Zeichenkette mit printf(3)-Format.
Hinweis: In der Formatzeichenkette kann „%“PRIsVALUE für die Ausgabe von
Object#to_s(oderObject#inspect, wenn das '+'-Flag gesetzt ist) verwendet werden (und das zugehörige Argument muss ein VALUE sein). Da dies mit „%i“ für Ganzzahlen in Formatzeichenketten kollidiert, verwenden Sie „%d“. - rb_str_append(VALUE str1, VALUE str2)
-
Hängt die Ruby-Zeichenkette str2 an die Ruby-Zeichenkette str1 an.
- rb_str_cat(VALUE str, const char *ptr, long len)
-
Hängt len Bytes Daten von ptr an die Ruby-Zeichenkette an.
- rb_str_cat2(VALUE str, const char* ptr)
- rb_str_cat_cstr(VALUE str, const char* ptr)
-
Hängt die C-Zeichenkette ptr an die Ruby-Zeichenkette str an. Diese Funktion ist äquivalent zu rb_str_cat(str, ptr, strlen(ptr)).
- rb_str_catf(VALUE str, const char* format, …)
- rb_str_vcatf(VALUE str, const char* format, va_list ap)
-
Hängt die C-Zeichenkette format und nachfolgende Argumente an die Ruby-Zeichenkette str gemäß einem printf-ähnlichen Format an. Diese Funktionen sind äquivalent zu rb_str_append(str, rb_sprintf(format, …)) bzw. rb_str_append(str, rb_vsprintf(format, ap)).
- rb_enc_str_new(const char *ptr, long len, rb_encoding *enc)
- rb_enc_str_new_cstr(const char *ptr, rb_encoding *enc)
-
Erstellt eine neue Ruby-Zeichenkette mit der angegebenen Kodierung.
- rb_enc_str_new_literal(const char *ptr, rb_encoding *enc)
-
Erstellt eine neue Ruby-Zeichenkette aus einem C-Zeichenketten-Literal mit der angegebenen Kodierung.
- rb_usascii_str_new(const char *ptr, long len)
- rb_usascii_str_new_cstr(const char *ptr)
-
Erstellt eine neue Ruby-Zeichenkette mit der Kodierung US-ASCII.
- rb_usascii_str_new_literal(const char *ptr)
-
Erstellt eine neue Ruby-Zeichenkette aus einem C-Zeichenketten-Literal mit der Kodierung US-ASCII.
- rb_utf8_str_new(const char *ptr, long len)
- rb_utf8_str_new_cstr(const char *ptr)
-
Erstellt eine neue Ruby-Zeichenkette mit der Kodierung UTF-8.
- rb_utf8_str_new_literal(const char *ptr)
-
Erstellt eine neue Ruby-Zeichenkette aus einem C-Zeichenketten-Literal mit der Kodierung UTF-8.
- rb_str_resize(VALUE str, long len)
-
Ändert die Größe einer Ruby-Zeichenkette auf len Bytes. Wenn str nicht modifizierbar ist, löst diese Funktion eine Ausnahme aus. Die Länge von str muss im Voraus gesetzt werden. Wenn len kleiner als die alte Länge ist, werden die Inhalte jenseits von len Bytes verworfen, andernfalls, wenn len größer als die alte Länge ist, werden die Inhalte jenseits der alten Bytes nicht beibehalten und sind Müll. Beachten Sie, dass RSTRING_PTR(str) sich durch Aufruf dieser Funktion ändern kann.
- rb_str_set_len(VALUE str, long len)
-
Setzt die Länge einer Ruby-Zeichenkette. Wenn str nicht modifizierbar ist, löst diese Funktion eine Ausnahme aus. Diese Funktion behält den Inhalt bis zu len Bytes bei, unabhängig von RSTRING_LEN(str). len darf die Kapazität von str nicht überschreiten.
- rb_str_modify(VALUE str)
-
Bereitet eine Ruby-Zeichenkette zur Modifikation vor. Wenn str nicht modifizierbar ist, löst diese Funktion eine Ausnahme aus, oder wenn der Puffer von str geteilt wird, weist diese Funktion einen neuen Puffer zu, um ihn ungeteilt zu machen. Sie MÜSSEN diese Funktion immer aufrufen, bevor Sie den Inhalt mit RSTRING_PTR und/oder rb_str_set_len ändern.
Array-Funktionen
- rb_ary_new()
-
Erstellt ein Array ohne Elemente.
- rb_ary_new2(long len)
- rb_ary_new_capa(long len)
-
Erstellt ein Array ohne Elemente und weist einen internen Puffer für len Elemente zu.
- rb_ary_new3(long n, …)
- rb_ary_new_from_args(long n, …)
-
Erstellt ein n-Element-Array aus den Argumenten.
- rb_ary_new4(long n, VALUE *elts)
- rb_ary_new_from_values(long n, VALUE *elts)
-
Erstellt ein n-Element-Array aus einem C-Array.
- rb_ary_to_ary(VALUE obj)
-
Konvertiert das Objekt in ein Array. Entspricht Object#to_ary.
Es gibt viele Funktionen zur Operation mit Arrays. Sie können Abstürze verursachen, wenn andere Typen übergeben werden.
- rb_ary_aref(int argc, const VALUE *argv, VALUE ary)
-
Entspricht
Array#[]. - rb_ary_entry(VALUE ary, long offset)
-
ary[offset]
- rb_ary_store(VALUE ary, long offset, VALUE obj)
-
ary[offset] = obj
- rb_ary_subseq(VALUE ary, long beg, long len)
-
ary[beg, len]
- rb_ary_push(VALUE ary, VALUE val)
- rb_ary_pop(VALUE ary)
- rb_ary_shift(VALUE ary)
- rb_ary_unshift(VALUE ary, VALUE val)
-
ary.push, ary.pop, ary.shift, ary.unshift
- rb_ary_cat(VALUE ary, const VALUE *ptr, long len)
-
Hängt len Elemente von Objekten aus ptr an das Array an.
Ruby mit C erweitern
Neue Funktionen zu Ruby hinzufügen
Sie können dem Ruby-Interpreter neue Funktionen (Klassen, Methoden usw.) hinzufügen. Ruby stellt APIs zur Verfügung, um die folgenden Dinge zu definieren
-
Klassen, Module
-
Methoden, Singleton-Methoden
-
Constants
Klassen- und Moduldefinition
Um eine Klasse oder ein Modul zu definieren, verwenden Sie die folgenden Funktionen
VALUE rb_define_class(const char *name, VALUE super) VALUE rb_define_module(const char *name)
Diese Funktionen geben die neu erstellte Klasse oder das neu erstellte Modul zurück. Sie möchten diese Referenz möglicherweise in einer Variablen speichern, um sie später zu verwenden.
Um verschachtelte Klassen oder Module zu definieren, verwenden Sie die folgenden Funktionen
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super) VALUE rb_define_module_under(VALUE outer, const char *name)
Methoden- und Singleton-Methoden-Definition
Um Methoden oder Singleton-Methoden zu definieren, verwenden Sie diese Funktionen
void rb_define_method(VALUE klass, const char *name,
VALUE (*func)(ANYARGS), int argc)
void rb_define_singleton_method(VALUE object, const char *name,
VALUE (*func)(ANYARGS), int argc)
Das 'argc' stellt die Anzahl der Argumente an die C-Funktion dar, die kleiner als 17 sein muss. Aber ich bezweifle, dass Sie so viele benötigen werden.
Wenn 'argc' negativ ist, gibt es die Aufrufsequenz an, nicht die Anzahl der Argumente.
Wenn argc -1 ist, wird die Funktion aufgerufen als
VALUE func(int argc, VALUE *argv, VALUE obj)
wobei argc die tatsächliche Anzahl der Argumente ist, argv das C-Array der Argumente ist und obj der Empfänger ist.
Wenn argc -2 ist, werden die Argumente in einem Ruby-Array übergeben. Die Funktion wird aufgerufen wie
VALUE func(VALUE obj, VALUE args)
wobei obj der Empfänger ist und args das Ruby-Array mit den tatsächlichen Argumenten ist.
Es gibt noch einige weitere Funktionen zur Definition von Methoden. Eine nimmt eine ID als Namen der zu definierenden Methode. Siehe auch ID oder Symbol unten.
void rb_define_method_id(VALUE klass, ID name,
VALUE (*func)(ANYARGS), int argc)
Es gibt zwei Funktionen zur Definition von privaten/geschützten Methoden
void rb_define_private_method(VALUE klass, const char *name,
VALUE (*func)(ANYARGS), int argc)
void rb_define_protected_method(VALUE klass, const char *name,
VALUE (*func)(ANYARGS), int argc)
Schließlich definiert rb_define_module_function eine Modulfunktion, die private UND Singleton-Methoden des Moduls sind. Zum Beispiel ist sqrt eine Modulfunktion, die im Math Modul definiert ist. Sie kann wie folgt aufgerufen werden
Math.sqrt(4)
oder
include Math sqrt(4)
Um Modulfunktionen zu definieren, verwenden Sie
void rb_define_module_function(VALUE module, const char *name,
VALUE (*func)(ANYARGS), int argc)
Außerdem können funktionsähnliche Methoden, die private Methoden im Kernel Modul sind, mit verwendet werden
void rb_define_global_function(const char *name, VALUE (*func)(ANYARGS), int argc)
Um einen Alias für die Methode zu definieren,
void rb_define_alias(VALUE module, const char* new, const char* old);
Um einen Reader/Writer für ein Attribut zu definieren,
void rb_define_attr(VALUE klass, const char *name, int read, int write)
Um die 'allocate'-Klassenmethode zu definieren und aufzuheben,
void rb_define_alloc_func(VALUE klass, VALUE (*func)(VALUE klass)); void rb_undef_alloc_func(VALUE klass);
func muss die klass als Argument nehmen und eine neu zugewiesene Instanz zurückgeben. Diese Instanz sollte so leer wie möglich sein, ohne teure (einschließlich externer) Ressourcen.
Wenn Sie eine bestehende Methode eines Vorfahren Ihrer Klasse überschreiben, können Sie sich auf verlassen
VALUE rb_call_super(int argc, const VALUE *argv)
Um anzugeben, ob Schlüsselwortargumente beim Aufruf von super übergeben werden
VALUE rb_call_super_kw(int argc, const VALUE *argv, int kw_splat)
kw_splat kann die folgenden möglichen Werte haben (verwendet von allen Methoden, die ein kw_splat Argument akzeptieren)
- RB_NO_KEYWORDS
-
Keine Schlüsselwörter übergeben
- RB_PASS_KEYWORDS
-
Schlüsselwörter übergeben, das letzte Argument sollte ein Hash von Schlüsselwörtern sein
- RB_PASS_CALLED_KEYWORDS
-
Schlüsselwörter übergeben, wenn die aktuelle Methode mit Schlüsselwörtern aufgerufen wurde, nützlich für die Argumentdelegation
Um den Empfänger des aktuellen Gültigkeitsbereichs zu erreichen (wenn kein anderer Weg verfügbar ist), können Sie verwenden
VALUE rb_current_receiver(void)
Konstantendefinition
Wir haben 2 Funktionen, um Konstanten zu definieren
void rb_define_const(VALUE klass, const char *name, VALUE val) void rb_define_global_const(const char *name, VALUE val)
Ersteres dient zur Definition einer Konstante unter einer angegebenen Klasse/Modul. Letzteres dient zur Definition einer globalen Konstante.
Ruby-Funktionen aus C verwenden
Es gibt mehrere Möglichkeiten, Ruby-Funktionen aus C-Code aufzurufen.
Ruby-Programme in einem String auswerten
Der einfachste Weg, Ruby-Funktionalität aus einem C-Programm zu verwenden, ist die Auswertung des Strings als Ruby-Programm. Diese Funktion erledigt die Arbeit
VALUE rb_eval_string(const char *str)
Die Auswertung erfolgt im aktuellen Kontext, daher können auf aktuelle lokale Variablen der innersten Methode (die von Ruby definiert ist) zugegriffen werden.
Beachten Sie, dass die Auswertung eine Ausnahme auslösen kann. Es gibt eine sicherere Funktion
VALUE rb_eval_string_protect(const char *str, int *state)
Sie gibt nil zurück, wenn ein Fehler aufgetreten ist. Außerdem ist *state null, wenn str erfolgreich ausgewertet wurde, andernfalls ungleich null.
ID oder Symbol
Sie können Methoden direkt aufrufen, ohne den String zu parsen. Zuerst muss ich etwas über ID erklären. ID ist die Ganzzahl, die Rubys Identifikatoren wie Variablennamen repräsentiert. Der Ruby-Datentyp, der ID entspricht, ist Symbol. Er kann aus Ruby in der Form aufgerufen werden
:Identifier
oder
:"any kind of string"
Sie können den ID-Wert aus einem String im C-Code abrufen, indem Sie verwenden
rb_intern(const char *name) rb_intern_str(VALUE name)
Sie können die ID aus einem Ruby-Objekt (Symbol oder String) abrufen, das als Argument übergeben wird, indem Sie verwenden
rb_to_id(VALUE symbol) rb_check_id(volatile VALUE *name) rb_check_id_cstr(const char *name, long len, rb_encoding *enc)
Diese Funktionen versuchen, das Argument in einen String zu konvertieren, wenn es kein Symbol oder String war. Die zweite Funktion speichert das konvertierte Ergebnis in *name und gibt 0 zurück, wenn der String kein bekanntes Symbol ist. Nach diesem Funktionsaufruf ist *name immer ein Symbol oder ein String, andernfalls ist es ein String, wenn das Ergebnis 0 ist. Die dritte Funktion nimmt einen NUL-terminierten C-String, nicht einen Ruby VALUE.
Sie können ein Symbol aus einem Ruby-Objekt (Symbol oder String) abrufen, das als Argument übergeben wird, indem Sie verwenden
rb_to_symbol(VALUE name) rb_check_symbol(volatile VALUE *namep) rb_check_symbol_cstr(const char *ptr, long len, rb_encoding *enc)
Diese Funktionen sind ähnlich wie die obigen Funktionen, außer dass sie ein Symbol anstelle einer ID zurückgeben.
Sie können eine C-ID in ein Ruby- Symbol konvertieren, indem Sie verwenden
VALUE ID2SYM(ID id)
und um ein Ruby- Symbol Objekt in eine ID zu konvertieren, verwenden Sie
ID SYM2ID(VALUE symbol)
Ruby-Methode aus C aufrufen
Um Methoden direkt aufzurufen, können Sie die folgende Funktion verwenden
VALUE rb_funcall(VALUE recv, ID mid, int argc, ...)
Diese Funktion ruft eine Methode auf dem recv auf, wobei der Methodenname durch das Symbol mid angegeben wird.
Zugriff auf Variablen und Konstanten
Sie können Klassenvariablen und Instanzvariablen über Zugriffsfunktionen abrufen. Globale Variablen können auch zwischen beiden Umgebungen geteilt werden. Es gibt keine Möglichkeit, auf Rubys lokale Variablen zuzugreifen.
Die Funktionen zum Zugreifen/Modifizieren von Instanzvariablen sind unten aufgeführt
VALUE rb_ivar_get(VALUE obj, ID id) VALUE rb_ivar_set(VALUE obj, ID id, VALUE val)
id muss das Symbol sein, das durch rb_intern() abgerufen werden kann.
Um auf die Konstanten der Klasse/des Moduls zuzugreifen
VALUE rb_const_get(VALUE obj, ID id)
Siehe auch Konstantendefinition oben.
Informationsaustausch zwischen Ruby und C
Ruby-Konstanten, auf die von C zugegriffen werden kann
Wie in Abschnitt 1.3 angegeben, können die folgenden Ruby-Konstanten von C referenziert werden.
- Qtrue
- Qfalse
-
Boolesche Werte. Qfalse ist auch in C false (d.h. 0).
- Qnil
-
Ruby nil im C-Kontext.
Globale Variablen, die zwischen C und Ruby geteilt werden
Informationen können zwischen den beiden Umgebungen über gemeinsam genutzte globale Variablen ausgetauscht werden. Um sie zu definieren, können Sie die unten aufgeführten Funktionen verwenden
void rb_define_variable(const char *name, VALUE *var)
Diese Funktion definiert die Variable, die von beiden Umgebungen gemeinsam genutzt wird. Der Wert der globalen Variablen, auf die von 'var' verwiesen wird, kann über die Ruby-Globale Variable namens 'name' zugegriffen werden.
Sie können schreibgeschützte (von Ruby aus, natürlich) Variablen mit der folgenden Funktion definieren.
void rb_define_readonly_variable(const char *name, VALUE *var)
Sie können gehookte Variablen definieren. Die Accessor-Funktionen (Getter und Setter) werden beim Zugriff auf die gehookten Variablen aufgerufen.
void rb_define_hooked_variable(const char *name, VALUE *var,
VALUE (*getter)(), void (*setter)())
Wenn Sie nur einen Setter oder Getter angeben müssen, setzen Sie 0 für den nicht benötigten Hook. Wenn beide Hooks 0 sind, verhält sich rb_define_hooked_variable() genauso wie rb_define_variable().
Die Prototypen der Getter- und Setter-Funktionen sind wie folgt
VALUE (*getter)(ID id, VALUE *var); void (*setter)(VALUE val, ID id, VALUE *var);
Sie können auch eine Ruby-Globale Variable ohne eine entsprechende C-Variable definieren. Der Wert der Variable wird nur über Hooks gesetzt/geholt.
void rb_define_virtual_variable(const char *name,
VALUE (*getter)(), void (*setter)())
Die Prototypen der Getter- und Setter-Funktionen sind wie folgt
VALUE (*getter)(ID id); void (*setter)(VALUE val, ID id);
C-Daten in ein Ruby-Objekt einkapseln
Manchmal müssen Sie Ihre struct in der C-Welt als Ruby-Objekt verfügbar machen. In einer solchen Situation können Sie durch die TypedData_XXX Makro-Familie den Zeiger auf die struct und das Ruby-Objekt gegenseitig konvertieren.
C-Struktur zu Ruby-Objekt
Sie können sval, einen Zeiger auf Ihre struct, mit dem nächsten Makro in ein Ruby-Objekt konvertieren.
TypedData_Wrap_Struct(klass, data_type, sval)
TypedData_Wrap_Struct() gibt ein erstelltes Ruby-Objekt als VALUE zurück.
Das klass-Argument ist die Klasse für das Objekt. Die klass sollte von rb_cObject abgeleitet sein, und der Allokator muss durch Aufruf von rb_define_alloc_func oder rb_undef_alloc_func gesetzt werden.
data_type ist ein Zeiger auf eine const rb_data_type_t, die beschreibt, wie Ruby die struct verwalten soll.
rb_data_type_t ist wie folgt definiert. Werfen wir einen Blick auf jedes Mitglied der Struktur.
typedef struct rb_data_type_struct rb_data_type_t;
struct rb_data_type_struct {
const char *wrap_struct_name;
struct {
void (*dmark)(void*);
void (*dfree)(void*);
size_t (*dsize)(const void *);
void (*dcompact)(void*);
void *reserved[1];
} function;
const rb_data_type_t *parent;
void *data;
VALUE flags;
};
wrap_struct_name ist ein Bezeichner dieser Instanz der Struktur. Er wird hauptsächlich zum Sammeln und Ausgeben von Statistiken verwendet. Der Bezeichner muss also im Prozess eindeutig sein, muss aber kein gültiger C- oder Ruby-Bezeichner sein.
Diese dmark / dfree Funktionen werden während der GC-Ausführung aufgerufen. Keine Objektzuweisungen sind währenddessen erlaubt, also weisen Sie keine Ruby-Objekte innerhalb davon zu.
dmark ist eine Funktion, um Ruby-Objekte zu markieren, auf die von Ihrer struct verwiesen wird. Sie muss alle Referenzen von Ihrer struct mit rb_gc_mark oder seiner Familie markieren, wenn Ihre struct solche Referenzen speichert.
dfree ist eine Funktion zum Freigeben der Zeigerzuweisung. Wenn dies RUBY_DEFAULT_FREE ist, wird der Zeiger einfach freigegeben.
dsize berechnet den Speicherverbrauch in Bytes durch die Struktur. Sein Parameter ist ein Zeiger auf Ihre Struktur. Sie können 0 als dsize übergeben, wenn es schwierig ist, eine solche Funktion zu implementieren. Es wird jedoch immer noch empfohlen, 0 zu vermeiden.
dcompact wird aufgerufen, wenn die Speicherkomprimierung stattgefunden hat. Referenzierte Ruby-Objekte, die von rb_gc_mark_movable() markiert wurden, können hier pro rb_gc_location() aktualisiert werden.
Sie müssen reserviert mit 0 füllen.
parent kann auf eine andere C-Typdefinition zeigen, von der das Ruby-Objekt geerbt wird. Dann akzeptiert TypedData_Get_Struct() auch abgeleitete Objekte.
Sie können „data“ mit einem beliebigen Wert für Ihren Gebrauch füllen. Ruby tut nichts mit diesem Mitglied.
flags ist ein bitweises OR der folgenden Flag-Werte. Da sie ein tiefes Verständnis des Garbage Collectors in Ruby erfordern, können Sie flags einfach auf 0 setzen, wenn Sie sich nicht sicher sind.
- RUBY_TYPED_FREE_IMMEDIATELY
-
Dieses Flag bewirkt, dass der Garbage Collector dfree() sofort während der
GC-Ausführung aufruft, wenn Ihre struct freigegeben werden muss. Sie können dieses Flag angeben, wenn dfree niemals die interne Sperre von Ruby (GVL) entsperrt.Wenn dieses Flag nicht gesetzt ist, verzögert Ruby die Ausführung von dfree() und ruft dfree() gleichzeitig mit Finalizern auf.
- RUBY_TYPED_WB_PROTECTED
-
Es zeigt an, dass die Implementierung des Objekts Schreibbarrieren unterstützt. Wenn dieses Flag gesetzt ist, kann Ruby die Garbage Collection des Objekts besser durchführen.
Wenn es gesetzt ist, sind Sie jedoch dafür verantwortlich, Schreibbarrieren in allen Implementierungen von Methoden dieses Objekts angemessen anzubringen. Andernfalls kann Ruby beim Ausführen abstürzen.
Weitere Informationen zu Schreibbarrieren finden Sie unter Generational GC.
- RUBY_TYPED_FROZEN_SHAREABLE
-
Dieses Flag zeigt an, dass das Objekt ein teilbares Objekt ist, wenn das Objekt eingefroren ist. Weitere Details finden Sie unter Ractor support.
Wenn dieses Flag nicht gesetzt ist, kann das Objekt mit der Methode
Ractor.make_shareable()kein teilbares Objekt werden.
Beachten Sie, dass dieses Makro eine Ausnahme auslösen kann. Wenn sval, das gewrappt werden soll, eine Ressource hält, die freigegeben werden muss (z. B. zugewiesener Speicher, Handle einer externen Bibliothek usw.), müssen Sie rb_protect verwenden.
Sie können die Struktur zuweisend und wrappen in einem Schritt, auf eine vorzuziehenere Weise.
TypedData_Make_Struct(klass, type, data_type, sval)
Dieses Makro gibt ein zugewiesenes T_DATA-Objekt zurück, das den Zeiger auf die ebenfalls zugewiesene Struktur wrappt. Dieses Makro funktioniert wie
(sval = ZALLOC(type), TypedData_Wrap_Struct(klass, data_type, sval))
Sie sollten jedoch dieses Makro anstelle von „Zuweisung dann Wrap“ wie im obigen Code verwenden, wenn es einfach zugewiesen wird, da letzteres eine NoMemoryError auslösen kann und sval in diesem Fall Speicherlecks verursacht.
Die Argumente klass und data_type funktionieren wie ihre Gegenstücke in TypedData_Wrap_Struct(). Ein Zeiger auf die zugewiesene Struktur wird sval zugewiesen, das ein Zeiger des angegebenen Typs sein sollte.
Deklaratives Markieren/Kompaktieren von Strukturreferenzen
In Fällen, in denen Ihre struct Ruby-Objekte referenziert, die einfache Werte sind und nicht in bedingter Logik oder komplexen Datenstrukturen gekapselt sind, wird ein alternativer Ansatz zum Markieren und Aktualisieren von Referenzen bereitgestellt, indem Offset-Referenzen zu den VALUES in Ihrer struct deklariert werden.
Dies ermöglicht es dem Ruby GC, das Markieren dieser Referenzen und die GC-Kompaktierung zu unterstützen, ohne dass dmark- und dcompact-Callbacks definiert werden müssen.
Sie müssen eine statische Liste von VALUE-Zeigern zu den Offsets innerhalb Ihrer struct definieren, wo sich die Referenzen befinden, und das Mitglied „data“ auf diese Referenzliste verweisen lassen. Die Referenzliste muss mit RUBY_END_REFS enden.
Einige Makros wurden bereitgestellt, um die Randreferenzierung zu erleichtern
-
RUBY_TYPED_DECL_MARKING= Ein Flag, das aufruby_data_type_tgesetzt werden kann, um anzuzeigen, dass Referenzen als Edges deklariert werden. -
RUBY_REFERENCES(ref_list_name)- Definiert ref_list_name als eine Liste von Referenzen -
RUBY_REF_END- Die Endmarkierung der Referenzliste. -
RUBY_REF_EDGE(struct, member)- Deklariert member als einen VALUE-Edge von struct. Verwenden Sie dies nachRUBY_REFERENCES_START -
RUBY_REFS_LIST_PTR- Zwingt die Referenzliste in ein Format, das vom vorhandenendmark-Interface akzeptiert werden kann.
Das folgende Beispiel stammt aus Dir (definiert in dir.c)
// The struct being wrapped. Notice this contains 3 members of which the second
// is a VALUE reference to another ruby object.
struct dir_data {
DIR *dir;
const VALUE path;
rb_encoding *enc;
}
// Define a reference list `dir_refs` containing a single entry to `path`.
// Needs terminating with RUBY_REF_END
RUBY_REFERENCES(dir_refs) = {
RUBY_REF_EDGE(dir_data, path),
RUBY_REF_END
};
// Override the "dmark" field with the defined reference list now that we
// no longer need a marking callback and add RUBY_TYPED_DECL_MARKING to the
// flags field
static const rb_data_type_t dir_data_type = {
"dir",
{RUBY_REFS_LIST_PTR(dir_refs), dir_free, dir_memsize,},
0, NULL, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_DECL_MARKING
};
Das Deklarieren einfacher Referenzen auf diese Weise ermöglicht es dem GC, das zugrunde liegende Objekt sowohl zu markieren als auch zu verschieben und die Referenz darauf während der Kompaktierung automatisch zu aktualisieren.
Ruby-Objekt zu C-Struktur
Um den C-Zeiger aus dem T_DATA-Objekt abzurufen, verwenden Sie das Makro TypedData_Get_Struct().
TypedData_Get_Struct(obj, type, &data_type, sval)
Ein Zeiger auf die Struktur wird der Variablen sval zugewiesen.
Siehe das folgende Beispiel für Details.
Beispiel - Erstellen der dbm-Erweiterung
OK, hier ist das Beispiel zum Erstellen einer Erweiterungsbibliothek. Dies ist die Erweiterung für den Zugriff auf DBMs. Der vollständige Quellcode ist im Verzeichnis ext/ im Ruby-Quellcode enthalten.
Verzeichnis erstellen
% mkdir ext/dbm
Erstellen Sie ein Verzeichnis für die Erweiterungsbibliothek im Verzeichnis ext.
Designen Sie die Bibliothek
Sie müssen die Bibliotheksfunktionen entwerfen, bevor Sie sie erstellen.
Schreiben Sie den C-Code
Sie müssen C-Code für Ihre Erweiterungsbibliothek schreiben. Wenn Ihre Bibliothek nur eine Quelldatei hat, ist die Wahl von „LIBRARY.c“ als Dateiname vorzuziehen. Wenn Ihre Bibliothek mehrere Quelldateien hat, vermeiden Sie es, „LIBRARY.c“ als Dateiname zu wählen. Es kann auf einigen Plattformen mit einer Zwischen-Datei „LIBRARY.o“ kollidieren. Beachten Sie, dass einige Funktionen in der mkmf-Bibliothek, die unten beschrieben werden, eine Datei „conftest.c“ zum Überprüfen der Kompilierung generieren. Sie sollten „conftest.c“ nicht als Namen einer Quelldatei wählen.
Ruby führt die Initialisierungsfunktion namens „Init_LIBRARY“ in der Bibliothek aus. Zum Beispiel wird „Init_dbm()“ beim Laden der Bibliothek ausgeführt.
Hier ist ein Beispiel für eine Initialisierungsfunktion.
#include <ruby.h>
void
Init_dbm(void)
{
/* define DBM class */
VALUE cDBM = rb_define_class("DBM", rb_cObject);
/* Redefine DBM.allocate
rb_define_alloc_func(cDBM, fdbm_alloc);
/* DBM includes Enumerable module */
rb_include_module(cDBM, rb_mEnumerable);
/* DBM has class method open(): arguments are received as C array */
rb_define_singleton_method(cDBM, "open", fdbm_s_open, -1);
/* DBM instance method close(): no args */
rb_define_method(cDBM, "close", fdbm_close, 0);
/* DBM instance method []: 1 argument */
rb_define_method(cDBM, "[]", fdbm_aref, 1);
/* ... */
/* ID for a instance variable to store DBM data */
id_dbm = rb_intern("dbm");
}
Die dbm-Erweiterung wrappt die dbm-Struktur in der C-Umgebung mithilfe von TypedData_Make_Struct.
struct dbmdata {
int di_size;
DBM *di_dbm;
};
static const rb_data_type_t dbm_type = {
"dbm",
{0, free_dbm, memsize_dbm,},
0, 0,
RUBY_TYPED_FREE_IMMEDIATELY,
};
static VALUE
fdbm_alloc(VALUE klass)
{
struct dbmdata *dbmp;
/* Allocate T_DATA object and C struct and fill struct with zero bytes */
return TypedData_Make_Struct(klass, struct dbmdata, &dbm_type, dbmp);
}
Dieser Code wrappt die dbmdata-Struktur in ein Ruby-Objekt. Wir vermeiden, DBM* direkt zu wrappen, da wir Grösseninformationen cachen wollen. Da Object.allocate einen gewöhnlichen T_OBJECT-Typ (anstelle von T_DATA) zuweist, ist es wichtig, entweder rb_define_alloc_func() aufzurufen, um ihn zu überschreiben, oder rb_undef_alloc_func() aufzurufen, um ihn zu löschen.
Um die dbmdata-Struktur aus einem Ruby-Objekt abzurufen, definieren wir das folgende Makro
#define GetDBM(obj, dbmp) do {\
TypedData_Get_Struct((obj), struct dbmdata, &dbm_type, (dbmp));\
if ((dbmp) == 0) closed_dbm();\
if ((dbmp)->di_dbm == 0) closed_dbm();\
} while (0)
Diese Art von kompliziertem Makro führt das Abrufen und Schließen der Überprüfung für DBM durch.
Es gibt drei Arten, Methodenargumente zu empfangen. Erstens empfangen Methoden mit einer festen Anzahl von Argumenten Argumente wie folgt
static VALUE
fdbm_aref(VALUE obj, VALUE keystr)
{
struct dbmdata *dbmp;
GetDBM(obj, dbmp);
/* Use dbmp to access the key */
dbm_fetch(dbmp->di_dbm, StringValueCStr(keystr));
/* ... */
}
Das erste Argument der C-Funktion ist das self, die restlichen sind die Argumente der Methode.
Zweitens empfangen Methoden mit einer beliebigen Anzahl von Argumenten Argumente wie folgt
static VALUE
fdbm_s_open(int argc, VALUE *argv, VALUE klass)
{
/* ... */
if (rb_scan_args(argc, argv, "11", &file, &vmode) == 1) {
mode = 0666; /* default value */
}
/* ... */
}
Das erste Argument ist die Anzahl der Methodenargumente, das zweite Argument ist das C-Array der Methodenargumente und das dritte Argument ist der Empfänger der Methode.
Sie können die Funktion rb_scan_args() verwenden, um die Argumente zu überprüfen und abzurufen. Das dritte Argument ist ein String, der angibt, wie Methodenargumente erfasst und den folgenden VALUE-Referenzen zugewiesen werden sollen.
Sie können nur die Argumentnummer mit rb_check_arity() überprüfen, was praktisch ist, wenn Sie die Argumente als Liste behandeln möchten.
Das Folgende ist ein Beispiel für eine Methode, die Argumente über ein Ruby-Array empfängt
static VALUE
thread_initialize(VALUE thread, VALUE args)
{
/* ... */
}
Das erste Argument ist der Empfänger, das zweite ist das Ruby-Array, das die Argumente für die Methode enthält.
Hinweis: GC muss über globale Variablen informiert werden, die Ruby-Objekte referenzieren, aber nicht in die Ruby-Welt exportiert werden. Sie müssen sie schützen durch
void rb_global_variable(VALUE *var)
oder die Objekte selbst durch
void rb_gc_register_mark_object(VALUE object)
extconf.rb vorbereiten
Wenn die Datei extconf.rb existiert, wird sie ausgeführt, um Makefile zu generieren.
extconf.rb ist die Datei zur Überprüfung von Kompilierungsbedingungen usw. Sie müssen
require 'mkmf'
am Anfang der Datei einfügen. Sie können die folgenden Funktionen verwenden, um verschiedene Bedingungen zu prüfen.
append_cppflags(array-of-flags[, opt]): append each flag to $CPPFLAGS if usable append_cflags(array-of-flags[, opt]): append each flag to $CFLAGS if usable append_ldflags(array-of-flags[, opt]): append each flag to $LDFLAGS if usable have_macro(macro[, headers[, opt]]): check whether macro is defined have_library(lib[, func[, headers[, opt]]]): check whether library containing function exists find_library(lib[, func, *paths]): find library from paths have_func(func[, headers[, opt]): check whether function exists have_var(var[, headers[, opt]]): check whether variable exists have_header(header[, preheaders[, opt]]): check whether header file exists find_header(header, *paths): find header from paths have_framework(fw): check whether framework exists (for MacOS X) have_struct_member(type, member[, headers[, opt]]): check whether struct has member have_type(type[, headers[, opt]]): check whether type exists find_type(type, opt, *headers): check whether type exists in headers have_const(const[, headers[, opt]]): check whether constant is defined check_sizeof(type[, headers[, opts]]): check size of type check_signedness(type[, headers[, opts]]): check signedness of type convertible_int(type[, headers[, opts]]): find convertible integer type find_executable(bin[, path]): find executable file path create_header(header): generate configured header create_makefile(target[, target_prefix]): generate Makefile
Siehe MakeMakefile für die vollständige Dokumentation dieser Funktionen.
Der Wert der folgenden Variablen beeinflusst die Makefile.
$CFLAGS: included in CFLAGS make variable (such as -O) $CPPFLAGS: included in CPPFLAGS make variable (such as -I, -D) $LDFLAGS: included in LDFLAGS make variable (such as -L) $objs: list of object file names
Compiler-/Linker-Flags sind normalerweise nicht portabel. Sie sollten stattdessen append_cppflags, append_cpflags und append_ldflags verwenden, anstatt die obigen Variablen direkt anzuhängen.
Normalerweise wird die Liste der Objektdateien automatisch durch Suche nach Quelldateien generiert. Sie müssen sie jedoch explizit definieren, wenn während des Builds Quellcode generiert wird.
Wenn eine Kompilierungsbedingung nicht erfüllt ist, sollten Sie „create_makefile“ nicht aufrufen. Die Makefile wird nicht generiert, die Kompilierung wird nicht durchgeführt.
Abhängigkeiten vorbereiten (Optional)
Wenn die Datei namens depend existiert, wird Makefile diese Datei einschließen, um Abhängigkeiten zu prüfen. Sie können diese Datei erstellen, indem Sie aufrufen
% gcc -MM *.c > depend
Es ist harmlos. Bereiten Sie es vor.
Makefile generieren
Versuchen Sie, die Makefile zu generieren durch
ruby extconf.rb
Wenn die Bibliothek im Verzeichnis vendor_ruby anstelle von site_ruby installiert werden soll, verwenden Sie die Option –vendor wie folgt.
ruby extconf.rb --vendor
Sie benötigen diesen Schritt nicht, wenn Sie die Erweiterungsbibliothek unter dem Verzeichnis ext des Ruby-Quellcodes ablegen. In diesem Fall erledigt die Kompilierung des Interpreters diesen Schritt für Sie.
make ausführen
Geben Sie
make
ein, um Ihre Erweiterung zu kompilieren. Sie benötigen diesen Schritt ebenfalls nicht, wenn Sie die Erweiterungsbibliothek unter dem Verzeichnis ext des Ruby-Quellcodes abgelegt haben.
Debuggen
Möglicherweise müssen Sie die Erweiterung mit rb_debug debuggen. Erweiterungen können statisch verknüpft werden, indem der Verzeichnisname in der Datei ext/Setup hinzugefügt wird, sodass Sie die Erweiterung mit dem Debugger inspizieren können.
Fertig! Jetzt haben Sie die Erweiterungsbibliothek
Sie können mit Ihrer Bibliothek alles tun, was Sie möchten. Der Autor von Ruby erhebt keine Einschränkungen für Ihren Code, abhängig von der Ruby-API. Nutzen, modifizieren, verteilen oder verkaufen Sie Ihr Programm frei.
Anhang A. Übersicht über Ruby-Header- und Quelldateien
Ruby-Headerdateien
Alles unter $repo_root/include/ruby wird mit make install installiert. Es sollte pro #include <ruby.h> von C-Erweiterungen eingeschlossen werden. Alle Symbole sind öffentliche APIs, mit Ausnahme von Symbolen, die mit rbimpl_ oder RBIMPL_ präfixiert sind. Dies sind Implementierungsdetails und sollten nicht von C-Erweiterungen verwendet werden.
Nur $repo_root/include/ruby/*.h, deren entsprechende Makros in der Headerdatei $repo_root/include/ruby.h definiert sind, dürfen von C-Erweiterungen eingeschlossen werden.
Headerdateien unter $repo_root/internal/ oder direkt unter der Wurzel $repo_root/*.h werden nicht mit make installiert. Es handelt sich um interne Header mit nur internen APIs.
Ruby-Sprachkern
- class.c
-
Klassen und Module
- error.c
-
Ausnahmeklassen und Ausnahmechanismus
- gc.c
-
Speicherverwaltung
- load.c
-
Bibliotheksladung
- object.c
-
Objekte
- variable.c
-
Variablen und Konstanten
Ruby-Syntaxparser
- parse.y
-
Grammatikdefinition
- parse.c
-
automatisch generiert aus parse.y
- defs/keywords
-
reservierte Schlüsselwörter
- lex.c
-
automatisch generiert aus Schlüsselwörtern
Ruby-Evaluator (auch YARV genannt)
compile.c eval.c eval_error.c eval_jump.c eval_safe.c insns.def : definition of VM instructions iseq.c : implementation of VM::ISeq thread.c : thread management and context switching thread_win32.c : thread implementation thread_pthread.c : ditto vm.c vm_dump.c vm_eval.c vm_exec.c vm_insnhelper.c vm_method.c defs/opt_insns_unif.def : instruction unification defs/opt_operand.def : definitions for optimization -> insn*.inc : automatically generated -> opt*.inc : automatically generated -> vm.inc : automatically generated
Regulärer Ausdrucks-Engine (Onigumo)
regcomp.c regenc.c regerror.c regexec.c regparse.c regsyntax.c
Dienstprogramme
- debug.c
-
Debug-Symbole für C-Debugger
- dln.c
-
dynamisches Laden
- st.c
-
Allzweck-Hash-Tabelle
- strftime.c
-
Formatieren von Zeiten
- util.c
-
verschiedene Dienstprogramme
Implementierung des Ruby-Interpreters
dmyext.c dmydln.c dmyencoding.c id.c inits.c main.c ruby.c version.c gem_prelude.rb prelude.rb
Klassenbibliothek
- array.c
- bignum.c
-
Bignum
- compar.c
- complex.c
- cont.c
- dir.c
- enum.c
- enumerator.c
- file.c
- hash.c
- io.c
- marshal.c
- math.c
- numeric.c
- pack.c
- proc.c
- process.c
-
Process
- random.c
-
zufällige Zahl
- range.c
- rational.c
- re.c
- signal.c
- sprintf.c
- string.c
- struct.c
- time.c
- defs/known_errors.def
-
Errno::* Ausnahmeklassen
- -> known_errors.inc
-
automatisch generiert
Mehrsprachigkeit
- encoding.c
- transcode.c
- enc/*.c
-
Kodierungsklassen
- enc/trans/*
-
Codepunkt-Zuordnungstabellen
goruby Interpreter-Implementierung
goruby.c golf_prelude.rb : goruby specific libraries. -> golf_prelude.c : automatically generated
Anhang B. Ruby Extension API Referenz
Typen
- VALUE
-
Der Typ für das Ruby-Objekt. Tatsächliche Strukturen sind in ruby.h definiert, wie z. B. struct RString usw. Um auf die Werte in Strukturen zuzugreifen, verwenden Sie Castings-Makros wie RSTRING(obj).
Variablen und Konstanten
- Qnil
-
nil-Objekt
- Qtrue
-
wahr-Objekt (standardmäßiger wahrer Wert)
- Qfalse
-
falsch-Objekt
C-Pointer-Umhüllung
- Data_Wrap_Struct(VALUE klass, void (*mark)(), void (*free)(), void *sval)
-
Umschließt einen C-Pointer in ein Ruby-Objekt. Wenn das Objekt Verweise auf andere Ruby-Objekte hat, sollten diese durch die Markierungsfunktion während des
GC-Prozesses markiert werden. Andernfalls sollte Mark NULL sein. Wenn dieses Objekt nicht mehr referenziert wird, wird der Zeiger von der Free-Funktion verworfen. - Data_Make_Struct(klass, type, mark, free, sval)
-
Dieses Makro allokiert Speicher mittels malloc(), weist ihn der Variablen sval zu und gibt die DATA zurück, die den Zeiger auf den Speicherbereich kapselt.
- Data_Get_Struct(data, type, sval)
-
Dieses Makro ruft den Zeigerwert aus DATA ab und weist ihn der Variablen sval zu.
Prüfen von VALUE-Typen
- RB_TYPE_P(value, type)
-
Ist
valueein interner Typ (T_NIL, T_FIXNUM usw.)? - TYPE(value)
-
Interner Typ (T_NIL, T_FIXNUM usw.)
- FIXNUM_P(value)
-
Ist
valueein Fixnum? - NIL_P(value)
-
Ist
valuenil? - RB_INTEGER_TYPE_P(value)
-
Ist
valueeinInteger? - RB_FLOAT_TYPE_P(value)
-
Ist
valueeinFloat? - void Check_Type(VALUE value, int type)
-
Stellt sicher, dass
valuevom gegebenen internentypeist oder löst einenTypeErroraus
VALUE-Typkonvertierung
- FIX2INT(value), INT2FIX(i)
-
Fixnum <-> Integer
- FIX2LONG(value), LONG2FIX(l)
-
Fixnum <-> long
- NUM2INT(value), INT2NUM(i)
-
Numeric<-> Integer - NUM2UINT(value), UINT2NUM(ui)
-
Numeric<-> vorzeichenloser Integer - NUM2LONG(value), LONG2NUM(l)
-
Numeric<-> long - NUM2ULONG(value), ULONG2NUM(ul)
-
Numeric<-> vorzeichenloser long - NUM2LL(value), LL2NUM(ll)
-
Numeric<-> long long - NUM2ULL(value), ULL2NUM(ull)
-
Numeric<-> vorzeichenloser long long - NUM2OFFT(value), OFFT2NUM(off)
-
Numeric<-> off_t - NUM2SIZET(value), SIZET2NUM(size)
-
Numeric<-> size_t - NUM2SSIZET(value), SSIZET2NUM(ssize)
-
Numeric<-> ssize_t - rb_integer_pack(value, words, numwords, wordsize, nails, flags), rb_integer_unpack(words, numwords, wordsize, nails, flags)
-
Numeric<-> Arbiträr große Integer-Puffer - NUM2DBL(value)
-
Numeric-> double - rb_float_new(f)
-
double ->
Float - RSTRING_LEN(str)
- RSTRING_PTR(str)
-
String-> Zeiger aufString-Daten Hinweis: Der resultierende Zeiger ist möglicherweise nicht NULL-terminiert - StringValue(value)
- StringValuePtr(value)
- StringValueCStr(value)
-
Objectmit #to_str -> Zeiger aufString-Daten ohne NULL-Bytes Es ist garantiert, dass die resultierenden Daten NULL-terminiert sind - rb_str_new2(s)
-
char * ->
String
Klassen und Module definieren
- VALUE rb_define_class(const char *name, VALUE super)
-
Definiert eine neue Ruby-Klasse als Unterklasse von super.
- VALUE rb_define_class_under(VALUE module, const char *name, VALUE super)
-
Erstellt eine neue Ruby-Klasse als Unterklasse von super im Namensraum des Moduls.
- VALUE rb_define_module(const char *name)
-
Definiert ein neues Ruby-Modul.
- VALUE rb_define_module_under(VALUE module, const char *name)
-
Definiert ein neues Ruby-Modul im Namensraum des Moduls.
- void rb_include_module(VALUE klass, VALUE module)
-
Schließt das Modul in die Klasse ein. Wenn die Klasse es bereits einschließt, wird es ignoriert.
- void rb_extend_object(VALUE object, VALUE module)
-
Erweitert das Objekt um die Attribute des Moduls.
Globale Variablen definieren
- void rb_define_variable(const char *name, VALUE *var)
-
Definiert eine globale Variable, die zwischen C und Ruby geteilt wird. Wenn der Name ein Zeichen enthält, das nicht Teil des Symbols sein darf, ist er für Ruby-Programme nicht sichtbar.
- void rb_define_readonly_variable(const char *name, VALUE *var)
-
Definiert eine schreibgeschützte globale Variable. Funktioniert genauso wie rb_define_variable(), außer dass die definierte Variable schreibgeschützt ist.
- void rb_define_virtual_variable(const char *name, VALUE (*getter)(), void (*setter)())
-
Definiert eine virtuelle Variable, deren Verhalten durch ein Paar von C-Funktionen definiert ist. Die Getter-Funktion wird aufgerufen, wenn die Variable referenziert wird. Die Setter-Funktion wird aufgerufen, wenn die Variable auf einen Wert gesetzt wird. Der Prototyp für Getter-/Setter-Funktionen ist
VALUE getter(ID id) void setter(VALUE val, ID id)
Die Getter-Funktion muss den Wert für den Zugriff zurückgeben.
- void rb_define_hooked_variable(const char *name, VALUE *var, VALUE (*getter)(), void (*setter)())
-
Definiert eine Hooked-Variable. Es ist eine virtuelle Variable mit einer C-Variablen. Der Getter wird aufgerufen als
VALUE getter(ID id, VALUE *var)
und gibt einen neuen Wert zurück. Der Setter wird aufgerufen als
void setter(VALUE val, ID id, VALUE *var)
- void rb_global_variable(VALUE *var)
-
Teilt dem
GCmit, dass die globale C-Variable, die Ruby-Werte enthält, markiert werden muss. - void rb_gc_register_mark_object(VALUE object)
-
Teilt dem
GCmit, dasobjectzu schützen, das möglicherweise nirgends referenziert wird.
Konstantendefinition
- void rb_define_const(VALUE klass, const char *name, VALUE val)
-
Definiert eine neue Konstante unter der Klasse/dem Modul.
- void rb_define_global_const(const char *name, VALUE val)
-
Definiert eine globale Konstante. Dies ist dasselbe wie
rb_define_const(rb_cObject, name, val)
Methodendefinition
- rb_define_method(VALUE klass, const char *name, VALUE (*func)(ANYARGS), int argc)
-
Definiert eine Methode für die Klasse. func ist der Funktionszeiger. argc ist die Anzahl der Argumente. Wenn argc -1 ist, erhält die Funktion 3 Argumente: argc, argv und self. Wenn argc -2 ist, erhält die Funktion 2 Argumente, self und args, wobei args ein Ruby-Array der Methodenargumente ist.
- rb_define_private_method(VALUE klass, const char *name, VALUE (*func)(ANYARGS), int argc)
-
Definiert eine private Methode für die Klasse. Die Argumente sind dieselben wie bei rb_define_method().
- rb_define_singleton_method(VALUE klass, const char *name, VALUE (*func)(ANYARGS), int argc)
-
Definiert eine Singleton-Methode. Die Argumente sind dieselben wie bei rb_define_method().
- rb_check_arity(int argc, int min, int max)
-
Prüft die Anzahl der Argumente, argc liegt im Bereich von min..max. Wenn max UNLIMITED_ARGUMENTS ist, wird die Obergrenze nicht geprüft. Wenn argc außerhalb des Bereichs liegt, wird ein
ArgumentErrorausgelöst. - rb_scan_args(int argc, VALUE *argv, const char *fmt, …)
-
Ruft Argumente aus argc und argv in gegebene VALUE-Referenzen ab, gemäß der Formatzeichenkette. Das Format kann in ABNF wie folgt beschrieben werden
scan-arg-spec := param-arg-spec [keyword-arg-spec] [block-arg-spec] param-arg-spec := pre-arg-spec [post-arg-spec] / post-arg-spec / pre-opt-post-arg-spec pre-arg-spec := num-of-leading-mandatory-args [num-of-optional-args] post-arg-spec := sym-for-variable-length-args [num-of-trailing-mandatory-args] pre-opt-post-arg-spec := num-of-leading-mandatory-args num-of-optional-args num-of-trailing-mandatory-args keyword-arg-spec := sym-for-keyword-arg block-arg-spec := sym-for-block-arg num-of-leading-mandatory-args := DIGIT ; The number of leading ; mandatory arguments num-of-optional-args := DIGIT ; The number of optional ; arguments sym-for-variable-length-args := "*" ; Indicates that variable ; length arguments are ; captured as a ruby array num-of-trailing-mandatory-args := DIGIT ; The number of trailing ; mandatory arguments sym-for-keyword-arg := ":" ; Indicates that keyword ; argument captured as a hash. ; If keyword arguments are not ; provided, returns nil. sym-for-block-arg := "&" ; Indicates that an iterator ; block should be captured if ; givenZum Beispiel bedeutet „12“, dass die Methode mindestens ein Argument benötigt und höchstens drei (1+2) Argumente empfängt. Die Formatzeichenkette muss daher von drei Variablenreferenzen gefolgt werden, die den erfassten Argumenten zugewiesen werden. Für ausgelassene Argumente werden Variablen auf Qnil gesetzt. NULL kann anstelle einer Variablenreferenz gesetzt werden, was bedeutet, dass das entsprechende erfasste Argument verworfen werden soll.
Die Anzahl der angegebenen Argumente, abzüglich eines optionalen Hashes oder eines Iteratorblocks, wird zurückgegeben.
- rb_scan_args_kw(int kw_splat, int argc, VALUE *argv, const char *fmt, …)
-
Das gleiche wie
rb_scan_args, außer dass das Argumentkw_splatangibt, ob Schlüsselwortargumente bereitgestellt werden (anstatt durch den Aufruf von Ruby an die C-Funktion bestimmt zu werden).kw_splatsollte einer der folgenden Werte sein- RB_SCAN_ARGS_PASS_CALLED_KEYWORDS
-
Gleiches Verhalten wie
rb_scan_args. - RB_SCAN_ARGS_KEYWORDS
-
Das letzte Argument sollte ein Hash sein, der als Schlüsselwörter behandelt wird.
- RB_SCAN_ARGS_LAST_HASH_KEYWORDS
-
Behandelt ein letztes Argument als Schlüsselwörter, wenn es sich um einen Hash handelt, und andernfalls nicht.
- int rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optional, VALUE *values)
-
Ruft Argument-VALUEs ab, die an Schlüsselwörter gebunden sind, dirigiert durch
tableinvaluesund löscht abgerufene Einträge auskeyword_hash. Die erstenrequiredIDs, auf die vontableverwiesen wird, sind obligatorisch, und die nachfolgendenoptional(-optional- 1, wennoptionalnegativ ist) IDs sind optional. Wenn ein obligatorischer Schlüssel nicht inkeyword_hashenthalten ist, wird „missing keyword“ArgumentErrorausgelöst. Wenn ein optionaler Schlüssel nicht inkeyword_hashvorhanden ist, wird das entsprechende Element invaluesaufQundefgesetzt. Wennoptionalnegativ ist, werden die restlichenkeyword_hashignoriert, andernfalls wird „unknown keyword“ArgumentErrorausgelöst.Seien Sie gewarnt, die Behandlung von Schlüsselwortargumenten in der C-API ist weniger effizient als die Behandlung in Ruby. Erwägen Sie, eine Ruby-Wrapper-Methode um eine C-Funktion ohne Schlüsselwörter zu verwenden. ref: bugs.ruby-lang.org/issues/11339
- VALUE rb_extract_keywords(VALUE *original_hash)
-
Extrahiert Paare, deren Schlüssel ein Symbol ist, in einen neuen Hash aus einem Hash-Objekt, auf das von
original_hashverwiesen wird. Wenn der ursprüngliche Hash Nicht-Symbol-Schlüssel enthält, werden diese in einen anderen Hash kopiert und der neue Hash wird überoriginal_hashgespeichert, andernfalls wird 0 gespeichert.
Ruby-Methoden aufrufen
- VALUE rb_funcall(VALUE recv, ID mid, int narg, …)
-
Ruft eine Methode auf. Um mid aus einem Methodennamen abzurufen, verwenden Sie rb_intern(). Kann auch private/geschützte Methoden aufrufen.
- VALUE rb_funcall2(VALUE recv, ID mid, int argc, VALUE *argv)
- VALUE rb_funcallv(VALUE recv, ID mid, int argc, VALUE *argv)
-
Ruft eine Methode auf und übergibt Argumente als Array von Werten. Kann auch private/geschützte Methoden aufrufen.
- VALUE rb_funcallv_kw(VALUE recv, ID mid, int argc, VALUE *argv, int kw_splat)
-
Wie rb_funcallv, wobei
kw_splatangibt, ob Schlüsselwortargumente übergeben werden. - VALUE rb_funcallv_public(VALUE recv, ID mid, int argc, VALUE *argv)
-
Ruft eine Methode auf und übergibt Argumente als Array von Werten. Kann nur öffentliche Methoden aufrufen.
- VALUE rb_funcallv_public_kw(VALUE recv, ID mid, int argc, VALUE *argv, int kw_splat)
-
Wie rb_funcallv_public, wobei
kw_splatangibt, ob Schlüsselwortargumente übergeben werden. - VALUE rb_funcall_passing_block(VALUE recv, ID mid, int argc, const VALUE* argv)
-
Wie rb_funcallv_public, außer dass der aktuell aktive Block als Block beim Aufruf der Methode übergeben wird.
- VALUE rb_funcall_passing_block_kw(VALUE recv, ID mid, int argc, const VALUE* argv, int kw_splat)
-
Wie rb_funcall_passing_block, wobei
kw_splatangibt, ob Schlüsselwortargumente übergeben werden. - VALUE rb_funcall_with_block(VALUE recv, ID mid, int argc, const VALUE *argv, VALUE passed_procval)
-
Wie rb_funcallv_public, außer dass
passed_procvalden Block angibt, der an die Methode übergeben werden soll. - VALUE rb_funcall_with_block_kw(VALUE recv, ID mid, int argc, const VALUE *argv, VALUE passed_procval, int kw_splat)
-
Wie rb_funcall_with_block, wobei
kw_splatangibt, ob Schlüsselwortargumente übergeben werden. - VALUE rb_eval_string(const char *str)
-
Kompiliert und führt den String als Ruby-Programm aus.
- ID rb_intern(const char *name)
-
Gibt die ID zurück, die dem Namen entspricht.
- char *rb_id2name(ID id)
-
Gibt den Namen zurück, der der ID entspricht.
- char *rb_class2name(VALUE klass)
-
Gibt den Namen der Klasse zurück.
- int rb_respond_to(VALUE obj, ID id)
-
Gibt wahr zurück, wenn das Objekt auf die durch id angegebene Nachricht reagiert.
Instanzvariablen
- VALUE rb_iv_get(VALUE obj, const char *name)
-
Ruft den Wert der Instanzvariable ab. Wenn der Name nicht mit „@“ präfixiert ist, ist diese Variable von Ruby aus nicht zugänglich.
- VALUE rb_iv_set(VALUE obj, const char *name, VALUE val)
-
Setzt den Wert der Instanzvariable.
Kontrollstruktur
- VALUE rb_block_call(VALUE recv, ID mid, int argc, VALUE * argv, VALUE (*func) (ANYARGS), VALUE data2)
-
Ruft eine Methode auf recv auf, wobei der Methodenname durch das Symbol mid und argc Argumente in argv angegeben wird, wobei func als Block bereitgestellt wird. Wenn func als Block aufgerufen wird, empfängt es den Wert von yield als erstes Argument und data2 als zweites Argument. Wenn mit mehreren Werten geyieldet wird (in C, rb_yield_values(), rb_yield_values2() und rb_yield_splat()), wird data2 als
Arrayverpackt, während die geyieldeten Werte über argc/argv der dritten/vierten Argumente abgerufen werden können. - VALUE rb_block_call_kw(VALUE recv, ID mid, int argc, VALUE * argv, VALUE (*func) (ANYARGS), VALUE data2, int kw_splat)
-
Wie rb_funcall_with_block, wobei
kw_splatangibt, ob Schlüsselwortargumente übergeben werden. - [OBSOLET] VALUE rb_iterate(VALUE (*func1)(), VALUE arg1, VALUE (*func2)(), VALUE arg2)
-
Ruft die Funktion func1 auf und stellt func2 als Block bereit. func1 wird mit dem Argument arg1 aufgerufen. func2 empfängt den Wert von yield als erstes Argument und arg2 als zweites Argument.
Wenn rb_iterate in 1.9 verwendet wird, muss func1 eine Ruby-Methode aufrufen. Diese Funktion ist seit 1.9 veraltet; verwenden Sie stattdessen rb_block_call.
- VALUE rb_yield(VALUE val)
-
Yieldet val als einzelnes Argument an den Block.
- VALUE rb_yield_values(int n, …)
-
Yieldet
nArgumente an den Block und verwendet ein C-Argument pro Ruby-Argument. - VALUE rb_yield_values2(int n, VALUE *argv)
-
Yieldet
nArgumente an den Block, wobei alle Ruby-Argumente im C-argv-Array stehen. - VALUE rb_yield_values_kw(int n, VALUE *argv, int kw_splat)
-
Wie rb_yield_values2, wobei
kw_splatangibt, ob Schlüsselwortargumente übergeben werden. - VALUE rb_yield_splat(VALUE args)
-
Wie rb_yield_values2, außer dass Argumente durch das Ruby-Array
argsangegeben werden. - VALUE rb_yield_splat_kw(VALUE args, int kw_splat)
-
Wie rb_yield_splat, wobei
kw_splatangibt, ob Schlüsselwortargumente übergeben werden. - VALUE rb_rescue(VALUE (*func1)(ANYARGS), VALUE arg1, VALUE (*func2)(ANYARGS), VALUE arg2)
-
Ruft die Funktion func1 mit arg1 als Argument auf. Wenn während func1 eine Ausnahme auftritt, ruft es func2 mit arg2 als erstes Argument und dem Ausnahmeobjekt als zweites Argument auf. Der Rückgabewert von rb_rescue() ist der Rückgabewert von func1, wenn keine Ausnahme auftritt, andernfalls der von func2.
- VALUE rb_ensure(VALUE (*func1)(ANYARGS), VALUE arg1, VALUE (*func2)(ANYARGS), VALUE arg2)
-
Ruft die Funktion func1 mit arg1 als Argument auf und dann func2 mit arg2, wenn die Ausführung beendet ist. Der Rückgabewert von rb_ensure() ist der von func1, wenn keine Ausnahme aufgetreten ist.
- VALUE rb_protect(VALUE (*func) (VALUE), VALUE arg, int *state)
-
Ruft die Funktion func mit arg als Argument auf. Wenn während func keine Ausnahme aufgetreten ist, gibt sie das Ergebnis von func zurück und *state ist null. Andernfalls gibt sie Qnil zurück und setzt *state auf ungleich null. Wenn state NULL ist, wird es in beiden Fällen nicht gesetzt. Sie müssen die Fehlerinformationen mit rb_set_errinfo(Qnil) löschen, wenn Sie die abgefangene Ausnahme ignorieren.
- void rb_jump_tag(int state)
-
Setzt die von rb_protect() und rb_eval_string_protect() abgefangene Ausnahme fort. state muss der zurückgegebene Wert von diesen Funktionen sein. Diese Funktion kehrt niemals zum Aufrufer zurück.
- void rb_iter_break()
-
Verlässt den aktuellen innersten Block. Diese Funktion kehrt niemals zum Aufrufer zurück.
- void rb_iter_break_value(VALUE value)
-
Verlässt den aktuellen innersten Block mit dem Wert. Der Block gibt den angegebenen Argumentwert zurück. Diese Funktion kehrt niemals zum Aufrufer zurück.
Ausnahmen und Fehler
- void rb_warn(const char *fmt, …)
-
Gibt eine Warnmeldung gemäß einem printf-ähnlichen Format aus.
- void rb_warning(const char *fmt, …)
-
Gibt eine Warnmeldung gemäß einem printf-ähnlichen Format aus, wenn $VERBOSE wahr ist.
- void rb_raise(rb_eRuntimeError, const char *fmt, …)
-
Löst eine
RuntimeErroraus. fmt ist eine Formatzeichenkette wie printf(). - void rb_raise(VALUE exception, const char *fmt, …)
-
Löst eine Klassenausnahme aus. fmt ist eine Formatzeichenkette wie printf().
- void rb_fatal(const char *fmt, …)
-
Löst einen fatalen Fehler aus, beendet den Interpreter. Für fatale Fehler wird keine Ausnahmebehandlung durchgeführt, aber es wird sichergestellt, dass ensure-Blöcke ausgeführt werden.
- void rb_bug(const char *fmt, …)
-
Beendet den Interpreter sofort. Diese Funktion sollte in Situationen aufgerufen werden, die durch einen Bug im Interpreter verursacht werden. Es werden keine Ausnahmebehandlung oder ensure-Ausführung durchgeführt.
Hinweis: In der Formatzeichenkette kann „%“PRIsVALUE für die Ausgabe von Object#to_s (oder Object#inspect, wenn das '+'-Flag gesetzt ist) verwendet werden (und das zugehörige Argument muss ein VALUE sein). Da dies mit „%i“ für Ganzzahlen in Formatzeichenketten kollidiert, verwenden Sie „%d“.
Threading
Ab Ruby 1.9 unterstützt Ruby native 1:1-Threading mit einem Kernel-Thread pro Ruby Thread-Objekt. Derzeit gibt es eine GVL (Global VM Lock), die die gleichzeitige Ausführung von Ruby-Code verhindert und von den Funktionen rb_thread_call_without_gvl und rb_thread_call_without_gvl2 freigegeben werden kann. Diese Funktionen sind schwer zu verwenden und in thread.c dokumentiert; verwenden Sie sie nicht, bevor Sie die Kommentare in thread.c gelesen haben.
- void rb_thread_schedule(void)
-
Gibt dem Scheduler einen Hinweis, die Ausführung an einen anderen Thread zu übergeben.
Input/Output (IO) auf einem einzelnen Dateideskriptor
- int rb_io_wait_readable(int fd)
-
Wartet unbegrenzt darauf, dass der angegebene FD lesbar wird, und erlaubt die Planung anderer Threads. Gibt einen wahren Wert zurück, wenn gelesen werden kann, und falsch bei einem nicht behebbaren Fehler.
- int rb_io_wait_writable(int fd)
-
Wie rb_io_wait_readable, aber für Schreibbarkeit.
- int rb_wait_for_single_fd(int fd, int events, struct timeval *timeout)
-
Ermöglicht das Warten auf einen einzelnen FD für ein oder mehrere Ereignisse mit einem angegebenen Timeout.
eventsist eine Maske aus beliebigen Kombinationen der folgenden Werte-
RB_WAITFD_IN - Warten auf Lesbarkeit von normalen Daten
-
RB_WAITFD_OUT - Warten auf Schreibbarkeit
-
RB_WAITFD_PRI - Warten auf Lesbarkeit dringender Daten
Verwenden Sie ein NULL-
timeout, um unbegrenzt zu warten. -
I/O-Multiplexing
Ruby unterstützt I/O-Multiplexing basierend auf dem select(2)-Systemaufruf. Die Linux-Manpage select_tut(2) <man7.org/linux/man-pages/man2/select_tut.2.html> bietet einen guten Überblick über die Verwendung von select(2), und die Ruby-API verfügt über analoge Funktionen und Datenstrukturen zur bekannten select-API. Ein Verständnis von select(2) ist erforderlich, um diesen Abschnitt zu verstehen.
- typedef struct rb_fdset_t
-
Die Datenstruktur, die die von select(2) verwendete fd_set-Bitmap umschließt. Dies ermöglicht Ruby, FD-Sätze zu verwenden, die größer sind als die, die durch historische Einschränkungen auf modernen Plattformen zulässig sind.
- void rb_fd_init(rb_fdset_t *)
-
Initialisiert die rb_fdset_t, sie muss vor anderen rb_fd_* Operationen initialisiert werden. Analog zum Aufruf von malloc(3) zum Zuweisen eines fd_set.
- void rb_fd_term(rb_fdset_t *)
-
Zerstört die rb_fdset_t, gibt alle von ihr verwendeten Speicher und Ressourcen frei. Sie muss vor der zukünftigen Verwendung mit rb_fd_init reinitialisiert werden. Analog zum Aufruf von free(3) zum Freigeben von Speicher für ein fd_set.
- void rb_fd_zero(rb_fdset_t *)
-
Löscht alle FDs aus der rb_fdset_t, analog zu FD_ZERO(3).
- void rb_fd_set(int fd, rb_fdset_t *)
-
Fügt einen gegebenen FD in die rb_fdset_t ein, analog zu FD_SET(3).
- void rb_fd_clr(int fd, rb_fdset_t *)
-
Entfernt einen gegebenen FD aus der rb_fdset_t, analog zu FD_CLR(3).
- int rb_fd_isset(int fd, const rb_fdset_t *)
-
Gibt wahr zurück, wenn ein gegebener FD in der rb_fdset_t gesetzt ist, andernfalls falsch. Analog zu FD_ISSET(3).
- int rb_thread_fd_select(int nfds, rb_fdset_t *readfds, rb_fdset_t *writefds, rb_fdset_t *exceptfds, struct timeval *timeout)
-
Analog zum select(2)-Systemaufruf, aber erlaubt die Planung anderer Ruby-Threads während des Wartens.
Wenn Sie nur auf einen einzelnen FD warten, bevorzugen Sie die Funktionen rb_io_wait_readable, rb_io_wait_writable oder rb_wait_for_single_fd, da diese für spezifische Plattformen (derzeit nur Linux) optimiert werden können.
Interpreter initialisieren und starten
Die Embedding-API-Funktionen sind unten aufgeführt (nicht für Erweiterungsbibliotheken erforderlich)
- void ruby_init()
-
Initialisiert den Interpreter.
- void *ruby_options(int argc, char **argv)
-
Verarbeitet Kommandozeilenargumente für den Interpreter. Und kompiliert die Ruby-Quelle zur Ausführung. Es gibt einen opaken Zeiger auf die kompilierte Quelle oder einen internen Sonderwert zurück.
- int ruby_run_node(void *n)
-
Führt die gegebene kompilierte Quelle aus und beendet den Prozess. Gibt EXIT_SUCCESS zurück, wenn die Quelle erfolgreich ausgeführt wurde. Andernfalls gibt es einen anderen Wert zurück.
- void ruby_script(char *name)
-
Gibt den Namen des Skripts ($0) an.
Hooks für Interpreter-Ereignisse
- void rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
-
Fügt eine Hook-Funktion für die angegebenen Interpreter-Ereignisse hinzu. events sollte ein OR-wert von sein
RUBY_EVENT_LINE RUBY_EVENT_CLASS RUBY_EVENT_END RUBY_EVENT_CALL RUBY_EVENT_RETURN RUBY_EVENT_C_CALL RUBY_EVENT_C_RETURN RUBY_EVENT_RAISE RUBY_EVENT_ALL
Die Definition von rb_event_hook_func_t ist unten
typedef void (*rb_event_hook_func_t)(rb_event_t event, VALUE data, VALUE self, ID id, VALUE klass)Das dritte Argument ‚data‘ für rb_add_event_hook() wird als zweites Argument an die Hook-Funktion übergeben, was in 1.8 ein Zeiger auf den aktuellen NODE war. Siehe RB_EVENT_HOOKS_HAVE_CALLBACK_DATA unten.
- int rb_remove_event_hook(rb_event_hook_func_t func)
-
Entfernt die angegebene Hook-Funktion.
Speichernutzung
- void rb_gc_adjust_memory_usage(ssize_t diff)
-
Passt die Menge des registrierten externen Speichers an. Sie können dem
GCmitteilen, wie viel Speicher von einer externen Bibliothek mit dieser Funktion verwendet wird. Das Aufrufen dieser Funktion mit positivem diff bedeutet, dass die Speichernutzung erhöht wird; ein neuer Speicherblock wird allokiert oder ein Block wird größer reallokiert. Das Aufrufen dieser Funktion mit negativem diff bedeutet, dass die Speichernutzung verringert wird; ein Speicherblock wird freigegeben oder ein Block wird kleiner reallokiert. Diese Funktion kann denGCauslösen.
Makros für Kompatibilität
Einige Makros zur Prüfung der API-Kompatibilitäten sind standardmäßig verfügbar.
- NORETURN_STYLE_NEW
-
Bedeutet, dass das NORETURN-Makro im funktionalen Stil anstelle eines Präfixes ist.
- HAVE_RB_DEFINE_ALLOC_FUNC
-
Bedeutet, dass die Funktion rb_define_alloc_func() bereitgestellt wird, was bedeutet, dass das Allokations-Framework verwendet wird. Dies ist dasselbe wie das Ergebnis von have_func(“rb_define_alloc_func”, “ruby.h”).
- HAVE_RB_REG_NEW_STR
-
Bedeutet, dass die Funktion rb_reg_new_str() bereitgestellt wird, die ein
Regexp-Objekt aus einemString-Objekt erstellt. Dies ist dasselbe wie das Ergebnis von have_func(“rb_reg_new_str”, “ruby.h”). - HAVE_RB_IO_T
-
Bedeutet, dass der Typ rb_io_t bereitgestellt wird.
- USE_SYMBOL_AS_METHOD_NAME
-
Bedeutet, dass Symbole als Methodennamen zurückgegeben werden, z.B.
Module#methods, #singleton_methods und so weiter. - HAVE_RUBY_*_H
-
Definiert in ruby.h und bedeutet, dass die entsprechende Header-Datei verfügbar ist. Wenn beispielsweise HAVE_RUBY_ST_H definiert ist, sollten Sie ruby/st.h und nicht nur st.h verwenden.
Header-Dateien, die diesen Makros entsprechen, können direkt aus Erweiterungsbibliotheken eingebunden werden.
- RB_EVENT_HOOKS_HAVE_CALLBACK_DATA
-
Bedeutet, dass rb_add_event_hook() das dritte Argument 'data' akzeptiert, das an die gegebene Event-Hook-Funktion übergeben wird.
Definition von rückwärtskompatiblen Makros für Funktionen mit Schlüsselwortargumenten
Die meisten Ruby C-Erweiterungen sind darauf ausgelegt, mehrere Ruby-Versionen zu unterstützen. Um Ruby 2.7+ in Bezug auf die Trennung von Schlüsselwortargumenten korrekt zu unterstützen, müssen C-Erweiterungen `*_kw`-Funktionen verwenden. Diese Funktionen existieren jedoch nicht in Ruby 2.6 und älter. In diesen Fällen sollten Makros definiert werden, um die Verwendung desselben Codes in mehreren Ruby-Versionen zu ermöglichen. Hier sind Beispielmakros, die Sie in Erweiterungen verwenden können, die Ruby 2.6 (oder älter) unterstützen, wenn Sie die in Ruby 2.7 eingeführten `*_kw`-Funktionen verwenden.
#ifndef RB_PASS_KEYWORDS
/* Only define macros on Ruby <2.7 */
#define rb_funcallv_kw(o, m, c, v, kw) rb_funcallv(o, m, c, v)
#define rb_funcallv_public_kw(o, m, c, v, kw) rb_funcallv_public(o, m, c, v)
#define rb_funcall_passing_block_kw(o, m, c, v, kw) rb_funcall_passing_block(o, m, c, v)
#define rb_funcall_with_block_kw(o, m, c, v, b, kw) rb_funcall_with_block(o, m, c, v, b)
#define rb_scan_args_kw(kw, c, v, s, ...) rb_scan_args(c, v, s, __VA_ARGS__)
#define rb_call_super_kw(c, v, kw) rb_call_super(c, v)
#define rb_yield_values_kw(c, v, kw) rb_yield_values2(c, v)
#define rb_yield_splat_kw(a, kw) rb_yield_splat(a)
#define rb_block_call_kw(o, m, c, v, f, p, kw) rb_block_call(o, m, c, v, f, p)
#define rb_fiber_resume_kw(o, c, v, kw) rb_fiber_resume(o, c, v)
#define rb_fiber_yield_kw(c, v, kw) rb_fiber_yield(c, v)
#define rb_enumeratorize_with_size_kw(o, m, c, v, f, kw) rb_enumeratorize_with_size(o, m, c, v, f)
#define SIZED_ENUMERATOR_KW(obj, argc, argv, size_fn, kw_splat) \
rb_enumeratorize_with_size((obj), ID2SYM(rb_frame_this_func()), \
(argc), (argv), (size_fn))
#define RETURN_SIZED_ENUMERATOR_KW(obj, argc, argv, size_fn, kw_splat) do { \
if (!rb_block_given_p()) \
return SIZED_ENUMERATOR(obj, argc, argv, size_fn); \
} while (0)
#define RETURN_ENUMERATOR_KW(obj, argc, argv, kw_splat) RETURN_SIZED_ENUMERATOR(obj, argc, argv, 0)
#define rb_check_funcall_kw(o, m, c, v, kw) rb_check_funcall(o, m, c, v)
#define rb_obj_call_init_kw(o, c, v, kw) rb_obj_call_init(o, c, v)
#define rb_class_new_instance_kw(c, v, k, kw) rb_class_new_instance(c, v, k)
#define rb_proc_call_kw(p, a, kw) rb_proc_call(p, a)
#define rb_proc_call_with_block_kw(p, c, v, b, kw) rb_proc_call_with_block(p, c, v, b)
#define rb_method_call_kw(c, v, m, kw) rb_method_call(c, v, m)
#define rb_method_call_with_block_kw(c, v, m, b, kw) rb_method_call_with_block(c, v, m, b)
#define rb_eval_cmd_kw(c, a, kw) rb_eval_cmd(c, a, 0)
#endif
Anhang C. Funktionen, die für die Verwendung in extconf.rb verfügbar sind
Siehe Dokumentation für mkmf.
Anhang D. Generational GC
Ruby 2.1 führte einen generationalen Garbage Collector (genannt RGenGC) ein. RGenGC (größtenteils) behält die Kompatibilität bei.
Im Allgemeinen ist die Verwendung der als Write Barrier bezeichneten Technik in Erweiterungsbibliotheken für einen generationalen GC erforderlich (en.wikipedia.org/wiki/Garbage_collection_%28computer_science%29). RGenGC funktioniert auch ohne Write Barriers in Erweiterungsbibliotheken.
Wenn Ihre Bibliothek die folgenden Tipps befolgt, kann die Leistung weiter verbessert werden. Insbesondere der Abschnitt „Zeiger nicht direkt berühren“ ist wichtig.
Inkompatibilität
Sie können das Feld RBASIC(obj)->klass nicht mehr direkt schreiben, da es jetzt ein konstanter Wert ist.
Grundsätzlich sollten Sie dieses Feld nicht beschreiben, da MRI erwartet, dass es ein unveränderliches Feld ist. Wenn Sie dies jedoch in Ihrer Erweiterung tun möchten, können Sie die folgenden Funktionen verwenden:
- VALUE rb_obj_hide(VALUE obj)
-
Löscht das Feld RBasic::klass. Das Objekt wird zu einem internen Objekt.
ObjectSpace::each_objectkann dieses Objekt nicht finden. - VALUE rb_obj_reveal(VALUE obj, VALUE klass)
-
Setzt RBasic::klass auf klass zurück. Wir erwarten, dass 'klass' eine von rb_obj_hide() versteckte Klasse ist.
Write Barriers
RGenGC erfordert keine Write Barriers, um einen generationalen GC zu unterstützen. Die Berücksichtigung von Write Barriers kann jedoch die Leistung von RGenGC verbessern. Bitte beachten Sie die folgenden Tipps.
Zeiger nicht direkt berühren
In MRI (include/ruby/ruby.h) werden einige Makros zur Erfassung von Zeigern auf interne Datenstrukturen unterstützt, wie z.B. RARRAY_PTR(), RSTRUCT_PTR() und so weiter.
VERWENDEN SIE DIESE MAKROS NICHT, sondern verwenden Sie die entsprechenden C-APIs wie rb_ary_aref(), rb_ary_store() und so weiter.
Prüfen Sie, ob Write Barriers eingefügt werden sollen
Sie müssen sich keine Gedanken über Write Barriers machen, wenn Sie nur eingebaute Typen verwenden.
Wenn Sie T_DATA-Objekte unterstützen, sollten Sie die Verwendung von Write Barriers in Betracht ziehen.
Das Einfügen von Write Barriers in T_DATA-Objekte funktioniert nur mit den folgenden Objekttypen: (a) langlebige Objekte, (b) wenn eine riesige Anzahl von Objekten generiert wird und (c) Container-Objekte, die Verweise auf andere Objekte enthalten. Wenn Ihre Erweiterung solche T_DATA-Objekte bereitstellt, sollten Sie das Einfügen von Write Barriers in Betracht ziehen.
(a): kurzlebige Objekte werden nicht zu alten Generationsobjekten. (b): nur wenige alte Objekte haben keine Leistungsauswirkungen. (c): nur wenige Referenzen haben keine Leistungsauswirkungen.
Das Einfügen von Write Barriers ist ein sehr schwieriger Hack, es ist leicht, kritische Fehler einzuführen. Außerdem sind mit dem Einfügen von Write Barriers einige Overhead verbunden. Grundsätzlich empfehlen wir nicht, Write Barriers einzufügen. Bitte erwägen Sie sorgfältig die Risiken.
Kombinieren mit eingebauten Typen
Bitte erwägen Sie die Nutzung von eingebauten Typen. Die meisten eingebauten Typen unterstützen Write Barrier, sodass Sie diese verwenden können, um das manuelle Einfügen von Write Barriers zu vermeiden.
Wenn Ihr T_DATA beispielsweise Verweise auf andere Objekte hat, können Sie diese Verweise in ein Array verschieben. Ein T_DATA-Objekt hat dann nur einen Verweis auf ein Array-Objekt. Oder Sie können ein Struct-Objekt verwenden, um ein T_DATA-Objekt (ohne Referenzen) und ein Array, das Referenzen enthält, zusammenzufassen.
Mit der Verwendung solcher Techniken müssen Sie keine Write Barriers mehr einfügen.
Write Barriers einfügen
[NOCHMALS] Das Einfügen von Write Barriers ist ein sehr schwieriger Hack und es ist leicht, kritische Fehler einzuführen. Außerdem sind mit dem Einfügen von Write Barriers einige Overhead verbunden. Grundsätzlich empfehlen wir nicht, Write Barriers einzufügen. Bitte erwägen Sie sorgfältig die Risiken.
Bevor Sie Write Barriers einfügen, müssen Sie den RGenGC-Algorithmus kennen (gc.c hilft Ihnen dabei). Makros und Funktionen zum Einfügen von Write Barriers sind in include/ruby/ruby.h verfügbar. Ein Beispiel ist in iseq.c enthalten.
Für eine vollständige Anleitung zu RGenGC und Write Barriers siehe <bugs.ruby-lang.org/projects/ruby-master/wiki/RGenGC>.
Anhang E. RB_GC_GUARD zum Schutz vor vorzeitigem GC
C Ruby verwendet derzeit einen konservativen Garbage Collector, daher müssen VALUE-Variablen auf dem Stack oder in Registern sichtbar bleiben, um sicherzustellen, dass zugehörige Daten verwendbar bleiben. Optimierende C-Compiler sind nicht auf konservative Garbage Collection ausgelegt, daher können sie den ursprünglichen VALUE optimieren, selbst wenn der Code von Daten abhängt, die mit diesem VALUE verbunden sind.
Das folgende Beispiel veranschaulicht die Verwendung von RB_GC_GUARD, um sicherzustellen, dass der Inhalt von sptr gültig bleibt, während der zweite Aufruf von rb_str_new_cstr ausgeführt wird.
VALUE s, w;
const char *sptr;
s = rb_str_new_cstr("hello world!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
sptr = RSTRING_PTR(s);
w = rb_str_new_cstr(sptr + 6); /* Possible GC invocation */
RB_GC_GUARD(s); /* ensure s (and thus sptr) do not get GC-ed */
Im obigen Beispiel muss RB_GC_GUARD *nach* der letzten Verwendung von sptr platziert werden. Das Platzieren von RB_GC_GUARD vor der Dereferenzierung von sptr wäre nutzlos. RB_GC_GUARD ist nur für den Datentyp VALUE wirksam, nicht für konvertierte C-Datentypen.
RB_GC_GUARD wäre im obigen Beispiel überhaupt nicht notwendig, wenn nach der Dereferenzierung von sptr nicht-inline-Funktionsaufrufe auf 's' getätigt werden. In diesem Fall würde der Aufruf einer beliebigen nicht-inline-Funktion auf 's', wie z.B.:
rb_str_modify(s);
Stellt sicher, dass 's' auf dem Stack oder im Register bleibt, um zu verhindern, dass eine GC-Ausführung es vorzeitig freigibt.
Die Verwendung des RB_GC_GUARD-Makros ist dem Schlüsselwort „volatile“ in C vorzuziehen. RB_GC_GUARD hat die folgenden Vorteile:
-
Die Absicht der Makronutzung ist klar.
-
RB_GC_GUARD beeinflusst nur seinen Aufruf-Site, während „volatile“ jedes Mal, wenn die Variable verwendet wird, zusätzlichen Code generiert, was die Optimierung beeinträchtigt.
-
„volatile“-Implementierungen können bei einigen Compilern und Architekturen fehlerhaft/inkonsistent sein. RB_GC_GUARD ist für fehlerhafte Systeme/Compiler anpassbar, ohne andere Systeme negativ zu beeinflussen.
Anhang F. Ractor-Unterstützung
Ractor(s) sind der Parallelisierungsmechanismus, der in Ruby 3.0 eingeführt wurde. Alle Ractor können parallel auf einem anderen OS-Thread laufen (unter Verwendung eines zugrunde liegenden systemseitig bereitgestellten Threads), daher sollte die C-Erweiterung Thread-sicher sein. Eine C-Erweiterung, die in mehreren Ractoren laufen kann, wird als „Ractor-sicher“ bezeichnet.
Ractor-Sicherheit rund um C-Erweiterungen hat die folgenden Eigenschaften:
-
Standardmäßig werden alle C-Erweiterungen als Ractor-unsicher eingestuft.
-
Ractor-unsichere C-Methoden können nur vom Haupt-
Ractoraufgerufen werden. Wenn sie von einem Nicht-Haupt-Ractoraufgerufen werden, wird einRactor::UnsafeErrorausgelöst. -
Wenn eine Erweiterung als Ractor-sicher markiert werden soll, sollte die Erweiterung rb_ext_extractor_safe(true) in der Init_-Funktion der Erweiterung aufrufen, und alle definierten Methoden werden als Ractor-sicher markiert.
Um eine „Ractor-sichere“ C-Erweiterung zu erstellen, müssen wir die folgenden Punkte prüfen:
-
Teilen Sie keine nicht teilbaren Objekte zwischen Raktoren
Beispielsweise kann eine globale Variable in C dazu führen, dass nicht teilbare Objekte zwischen Raktoren geteilt werden.
VALUE g_var; VALUE set(VALUE self, VALUE v){ return g_var = v; } VALUE get(VALUE self){ return g_var; }Das set()- und get()-Paar kann nicht teilbare Objekte mithilfe von g_var teilen, und das ist Ractor-unsicher.
Nicht nur die direkte Verwendung von globalen Variablen, auch indirekte Datenstrukturen wie globale st_tables können Objekte teilen, also seien Sie vorsichtig.
Beachten Sie, dass Klassen- und Objektmodule teilbare Objekte sind, sodass Sie den Code „cFoo = rb_define_class(…)“ mit globalen C-Variablen beibehalten können.
-
Prüfen Sie die Thread-Sicherheit der Erweiterung
Eine Erweiterung sollte Thread-sicher sein. Der folgende Code ist beispielsweise nicht Thread-sicher:
bool g_called = false; VALUE call(VALUE self) { if (g_called) rb_raise("recursive call is not allowed."); g_called = true; VALUE ret = do_something(); g_called = false; return ret; }da die globale Variable g_called von Threads anderer Raktoren synchronisiert werden sollte. Um solche Datenrennen zu vermeiden, sollten einige Synchronisierungen verwendet werden. Prüfen Sie include/ruby/thread_native.h und include/ruby/atomic.h.
Mit Ractors sind alle als Methodenparameter übergebenen Objekte und der Empfänger (self) garantiert aus dem aktuellen
Ractoroder teilbar. Folglich ist es einfacher, Code Ractor-sicher zu machen, als Code allgemein Thread-sicher zu machen. Zum Beispiel müssen wir kein Array-Objekt sperren, um darauf zuzugreifen. -
Prüfen Sie die Thread-Sicherheit jeder verwendeten Bibliothek
Wenn die Erweiterung auf eine externe Bibliothek angewiesen ist, wie z.B. eine Funktion foo() aus einer Bibliothek libfoo, sollte die Funktion libfoo foo() Thread-sicher sein.
-
Machen Sie ein Objekt teilbar
Dies ist nicht erforderlich, um eine Erweiterung Ractor-sicher zu machen.
Wenn eine Erweiterung spezielle Objekte bereitstellt, die durch rb_data_type_t definiert sind, überlegen Sie, ob diese Objekte teilbar sein können oder nicht.
Das Flag RUBY_TYPED_FROZEN_SHAREABLE zeigt an, dass diese Objekte teilbar sein können, wenn das Objekt eingefroren ist. Das bedeutet, dass die Mutation der verpackten Daten nicht erlaubt ist, wenn das Objekt eingefroren ist.
-
Sonstiges
Es gibt möglicherweise andere Punkte oder Anforderungen, die bei der Erstellung einer Ractor-sicheren Erweiterung berücksichtigt werden müssen. Dieses Dokument wird erweitert, sobald sie entdeckt werden.