Creating extension libraries for Ruby¶ ↑
This document explains how to make extension libraries for Ruby.
Basic Knowledge¶ ↑
In C, variables have types and data do not have types. In contrast, Ruby variables do not have a static type, and data themselves have types, so data will need to be converted between the languages.
Objects in Ruby are represented by the C type ‘VALUE’. Each VALUE data has its data type.
To retrieve C data from a VALUE, you need to:
-
Identify the VALUE’s data type
-
Convert the VALUE into C data
Converting to the wrong data type may cause serious problems.
Ruby data types¶ ↑
The Ruby interpreter has the following data types:
- T_NIL
-
nil
- T_OBJECT
-
ordinary object
- T_CLASS
-
class
- T_MODULE
-
module
- T_FLOAT
-
floating point number
- T_STRING
-
string
- T_REGEXP
-
regular expression
- T_ARRAY
-
array
- T_HASH
-
associative array
- T_STRUCT
-
(Ruby) structure
- T_BIGNUM
-
multi precision integer
- T_FIXNUM
-
Fixnum(31bit or 63bit integer)
- T_COMPLEX
-
complex number
- T_RATIONAL
-
rational number
- T_FILE
- T_TRUE
-
true
- T_FALSE
-
false
- T_DATA
-
data
- T_SYMBOL
-
symbol
In addition, there are several other types used internally:
- T_ICLASS
-
included module
- T_MATCH
-
MatchDataobject - T_UNDEF
-
undefined
- T_NODE
-
syntax tree node
- T_ZOMBIE
-
object awaiting finalization
Most of the types are represented by C structures.
Check type of the VALUE data¶ ↑
The macro TYPE() defined in ruby.h shows the data type of the VALUE. TYPE() returns the constant number T_XXXX described above. To handle data types, your code will look something like this:
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;
}
There is the data type check function
void Check_Type(VALUE value, int type)
which raises an exception if the VALUE does not have the type specified.
There are also faster check macros for fixnums and nil.
FIXNUM_P(obj) NIL_P(obj)
Convert VALUE into C data¶ ↑
The data for type T_NIL, T_FALSE, T_TRUE are nil, false, true respectively. They are singletons for the data type. The equivalent C constants are: Qnil, Qfalse, Qtrue. RTEST() will return true if a VALUE is neither Qfalse nor Qnil. If you need to differentiate Qfalse from Qnil, specifically test against Qfalse.
The T_FIXNUM data is a 31bit or 63bit length fixed integer. This size depends on the size of long: if long is 32bit then T_FIXNUM is 31bit, if long is 64bit then T_FIXNUM is 63bit. T_FIXNUM can be converted to a C integer by using the FIX2INT() macro or FIX2LONG(). Though you have to check that the data is really FIXNUM before using them, they are faster. FIX2LONG() never raises exceptions, but FIX2INT() raises RangeError if the result is bigger or smaller than the size of int. There are also NUM2INT() and NUM2LONG() which converts any Ruby numbers into C integers. These macros include a type check, so an exception will be raised if the conversion failed. NUM2DBL() can be used to retrieve the double float value in the same way.
You can use the macros StringValue() and StringValuePtr() to get a char* from a VALUE. StringValue(var) replaces var’s value with the result of “var.to_str()”. StringValuePtr(var) does the same replacement and returns the char* representation of var. These macros will skip the replacement if var is a String. Notice that the macros take only the lvalue as their argument, to change the value of var in place.
You can also use the macro named StringValueCStr(). This is just like StringValuePtr(), but always adds a NUL character at the end of the result. If the result contains a NUL character, this macro causes the ArgumentError exception. StringValuePtr() doesn’t guarantee the existence of a NUL at the end of the result, and the result may contain NUL.
Other data types have corresponding C structures, e.g. struct RArray for T_ARRAY etc. The VALUE of the type which has the corresponding structure can be cast to retrieve the pointer to the struct. The casting macro will be of the form RXXXX for each data type; for instance, RARRAY(obj). See “ruby.h”. However, we do not recommend to access RXXXX data directly because these data structures are complex. Use corresponding rb_xxx() functions to access the internal struct. For example, to access an entry of array, use rb_ary_entry(ary, offset) and rb_ary_store(ary, offset, obj).
There are some accessing macros for structure members, for example ‘RSTRING_LEN(str)’ to get the size of the Ruby String object. The allocated region can be accessed by ‘RSTRING_PTR(str)’.
Notice: Do not change the value of the structure directly, unless you are responsible for the result. This ends up being the cause of interesting bugs.
Convert C data into VALUE¶ ↑
To convert C data to Ruby values:
- FIXNUM
-
left shift 1 bit, and turn on its least significant bit (LSB).
- Other pointer values
-
cast to VALUE.
You can determine whether a VALUE is a pointer or not by checking its LSB.
Notice: Ruby does not allow arbitrary pointer values to be a VALUE. They should be pointers to the structures which Ruby knows about. The known structures are defined in <ruby.h>.
To convert C numbers to Ruby values, use these macros:
- INT2FIX()
-
for integers within 31bits.
- INT2NUM()
-
for arbitrary sized integers.
INT2NUM() converts an integer into a Bignum if it is out of the FIXNUM range, but is a bit slower.
Manipulating Ruby object¶ ↑
As I already mentioned, it is not recommended to modify an object’s internal structure. To manipulate objects, use the functions supplied by the Ruby interpreter. Some (not all) of the useful functions are listed below:
String functions¶ ↑
- rb_str_new(const char *ptr, long len)
-
Creates a new Ruby string.
- rb_str_new2(const char *ptr)
- rb_str_new_cstr(const char *ptr)
-
Creates a new Ruby string from a C string. This is equivalent to rb_str_new(ptr, strlen(ptr)).
- rb_str_new_literal(const char *ptr)
-
Creates a new Ruby string from a C string literal.
- rb_sprintf(const char *format, …)
- rb_vsprintf(const char *format, va_list ap)
-
Creates a new Ruby string with printf(3) format.
Note: In the format string, “%”PRIsVALUE can be used for
Object#to_s(orObject#inspectif ‘+’ flag is set) output (and related argument must be a VALUE). Since it conflicts with “%i”, for integers in format strings, use “%d”. - rb_str_append(VALUE str1, VALUE str2)
-
Appends Ruby string str2 to Ruby string str1.
- rb_str_cat(VALUE str, const char *ptr, long len)
-
Appends len bytes of data from ptr to the Ruby string.
- rb_str_cat2(VALUE str, const char* ptr)
- rb_str_cat_cstr(VALUE str, const char* ptr)
-
Appends C string ptr to Ruby string str. This function is equivalent to 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)
-
Appends C string format and successive arguments to Ruby string str according to a printf-like format. These functions are equivalent to rb_str_append(str, rb_sprintf(format, …)) and rb_str_append(str, rb_vsprintf(format, ap)), respectively.
- rb_enc_str_new(const char *ptr, long len, rb_encoding *enc)
- rb_enc_str_new_cstr(const char *ptr, rb_encoding *enc)
-
Creates a new Ruby string with the specified encoding.
- rb_enc_str_new_literal(const char *ptr, rb_encoding *enc)
-
Creates a new Ruby string from a C string literal with the specified encoding.
- rb_usascii_str_new(const char *ptr, long len)
- rb_usascii_str_new_cstr(const char *ptr)
-
Creates a new Ruby string with encoding US-ASCII.
- rb_usascii_str_new_literal(const char *ptr)
-
Creates a new Ruby string from a C string literal with encoding US-ASCII.
- rb_utf8_str_new(const char *ptr, long len)
- rb_utf8_str_new_cstr(const char *ptr)
-
Creates a new Ruby string with encoding UTF-8.
- rb_utf8_str_new_literal(const char *ptr)
-
Creates a new Ruby string from a C string literal with encoding UTF-8.
- rb_str_resize(VALUE str, long len)
-
Resizes a Ruby string to len bytes. If str is not modifiable, this function raises an exception. The length of str must be set in advance. If len is less than the old length the content beyond len bytes is discarded, else if len is greater than the old length the content beyond the old length bytes will not be preserved but will be garbage. Note that RSTRING_PTR(str) may change by calling this function.
- rb_str_set_len(VALUE str, long len)
-
Sets the length of a Ruby string. If str is not modifiable, this function raises an exception. This function preserves the content up to len bytes, regardless RSTRING_LEN(str). len must not exceed the capacity of str.
- rb_str_modify(VALUE str)
-
Prepares a Ruby string to modify. If str is not modifiable, this function raises an exception, or if the buffer of str is shared, this function allocates new buffer to make it unshared. Always you MUST call this function before modifying the contents using RSTRING_PTR and/or rb_str_set_len.
Array functions¶ ↑
- rb_ary_new()
-
Creates an array with no elements.
- rb_ary_new2(long len)
- rb_ary_new_capa(long len)
-
Creates an array with no elements, allocating internal buffer for len elements.
- rb_ary_new3(long n, …)
- rb_ary_new_from_args(long n, …)
-
Creates an n-element array from the arguments.
- rb_ary_new4(long n, VALUE *elts)
- rb_ary_new_from_values(long n, VALUE *elts)
-
Creates an n-element array from a C array.
- rb_ary_to_ary(VALUE obj)
-
Converts the object into an array. Equivalent to Object#to_ary.
There are many functions to operate an array. They may dump core if other types are given.
- rb_ary_aref(int argc, const VALUE *argv, VALUE ary)
-
Equivalent to
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)
- rb_ary_pop(VALUE ary)
-
ary.push, ary.pop, ary.shift, ary.unshift
- rb_ary_cat(VALUE ary, const VALUE *ptr, long len)
-
Appends len elements of objects from ptr to the array.
Extending Ruby with C¶ ↑
Adding new features to Ruby¶ ↑
You can add new features (classes, methods, etc.) to the Ruby interpreter. Ruby provides APIs for defining the following things:
-
Classes, Modules
-
Methods, singleton methods
-
Constants