SL Unit Ii
SL Unit Ii
Extending Ruby:
Extending Ruby means adding extra powers to the Ruby programming language by
bringing in tools or capabilities from other languages like C or C++. This lets
programmers use existing software written in those languages directly within their
Ruby programs. For example, if there's a really fast and powerful tool written in C that
you want to use in your Ruby program, you can "extend" Ruby to include that tool's
abilities.This enables developers to leverage existing libraries, access low-level system
functionality, or improve performance for certain tasks.
Ruby Objects in C
Everything you interact with, from numbers and strings to actions (methods), is treated
as an object. When you work with these objects in C extensions for Ruby, you need to
understand how they are represented and accessed.
Most Ruby objects reside in memory, managed by pointers of type VALUE in C. These
pointers act like addresses, pointing to the object's location in memory where its actual
data is stored (like the value of a number or the characters in a string).
There's a special case for simple values like integers (Fixnums), symbols, true/false,
and nil. These are stored directly within the VALUE variable itself, forgoing the need for
separate memory allocation. This makes accessing and manipulating them faster.
VALUE acts as a container type in the Ruby C API, able to hold either pointers (for most
objects) or immediate values (simple values). The Ruby interpreter utilizes the object's
memory address to distinguish between the two:
❖ If the lower bits of the VALUE variable are zero (due to memory
alignment), it indicates a pointer.
❖ If the lower bits are not zero, it signifies an immediate value stored within
the VALUE variable itself.
➔ Regular objects:
◆ They have a table of instance variables to store their specific data, like
the value of an integer or the characters in a string.
◆ They also have information about their class (type) attached to them.
This tells the interpreter what kind of object it is (e.g., String, Array, Hash)
and allows it to access the appropriate methods defined for that class.
➔ Immediate values:
◆ Their value is directly stored within the VALUE variable itself.
◆ Class information might be attached in a different way, depending on the
specific type of immediate value.
When you're working with Ruby's internal data structures, some values are stored
directly in a special way. These include small numbers (Fixnums), symbols, true, false,
and nil.
For Fixnums, instead of storing them as they are, Ruby shifts them left by one bit and
sets the rightmost bit (bit 0) to 1. This special bit pattern helps distinguish them from
other types of data. So, if a value is used as a pointer to a Ruby structure, it always has
its rightmost bit set to 0.
To check if a value is a Fixnum, you just need to check if its rightmost bit is set to 1.
Ruby provides a macro called FIXNUM_P to do this check easily. Similarly, there are
similar checks for other immediate values like symbols, nil, and checking if a value is
neither nil nor false.
In C, these other immediate values (true, false, and nil) are represented by the
constants Qtrue, Qfalse, and Qnil respectively. You can directly test Ruby values
against these constants, or use conversion macros which handle the necessary
typecasting for you.
To access these properties of a Ruby string in C code, we use the RSTRING macro. For
example, RSTRING(str)->len gives us the length of the string, and RSTRING(str)->ptr
gives us a pointer to where the string is stored.
For example, if we want to iterate over all the characters in a string, we would call
StringValue on the original object to ensure it's a string, then access its length and
pointer through the RSTRING macro.
If we just need the pointer to the string's contents and don't care about the length, we
can use the convenience method StringValuePtr, which resolves the string reference
and returns the C pointer to the contents directly.
In Ruby, values are the fundamental building blocks that hold different kinds of
information. Think of them as multi-purpose containers. Some VALUEs directly store
data, like simple numbers or short text strings. Others are more like signposts – they
point to a specific location where a larger, more complex Ruby object is stored.
Each type of Ruby object, like strings, arrays, or numbers, has a predefined structure
behind the scenes. These structures are written in the C programming language and
are detailed within a file called ruby.h. It's like having architectural plans for different
kinds of objects. Ruby provides special helper functions (called macros) that start with
"RClassname" which allow you to access and work with these internal structures.
You can check the underlying structure of a VALUE by using the TYPE(obj) macro. This
is like checking the type of container a value is stored in. The Check_Type macro helps
ensure you're working with the right kind of object. It's a safety measure that prevents
you from accidentally trying to manipulate the wrong kind of data.
Let's use an array as an example. If you have a VALUE that represents an array, you can
use the RARRAY(arr) macro to access the array's internal structure. This lets you see
things like the length of the array (how many items it holds), its capacity (how much
space is allocated), and even a pointer to where the array's data is actually stored.
Global Variables
● In most cases, your C extensions use classes and objects to share data with
Ruby code. This is the recommended approach for organized and safe data
exchange.
● However, there might be situations where you need a single, global variable
accessible to both sides.
● Create a VALUE (Ruby object): This is a container that can hold various data
types in Ruby.
○ In this example, hardware_list is a VALUE that initially stores an empty
array.
● Bind the VALUE's address to a Ruby variable name:
○ The rb_define_variable function associates the address of hardware_list
with the Ruby variable $hardware.
○ The $ prefix is optional but commonly used to indicate a global variable.
● Fill the array with data:
○ rb_ary_push adds elements like "DVD", "CDPlayer1", and "CDPlayer2" to
the array.
Memory Allocation:
In Ruby, sometimes you need to allocate memory for special purposes, like big images
or lots of small data. Ruby's garbage collector helps manage memory, but when you're
doing it yourself, use special tools provided by Ruby. These tools make sure that if you
can't allocate memory, the garbage collector tries to free up space. They're more
advanced than basic memory allocation functions like malloc.
For instance, if ALLOC_N determines that it cannot allocate the desired amount of
memory, it will invoke the garbage collector to try to reclaim some space. It will raise a
NoMemError if it can’t or if the requested amount of memory is invalid.
API: Memory Allocation
1. type * ALLOC_N( c-type, n ): This function is used in Ruby extensions for
allocating memory.
c-type: This is the first argument, and it represents the data type of the objects
you want to allocate memory for. It should be the actual name of the C data type,
like int, float, or a custom structure you defined.
n: This is the second argument, and it represents the number of objects you
want to allocate memory for. It should be an integer expression that evaluates
to a positive number.
Example: For example, if you call ALLOC_N(int, 10), it will allocate memory for
10 integer objects. Since each int takes 4 bytes, the total memory allocated will
be 40
2.type * ALLOC(c-type): type * ALLOC(c-type) is another specific function used in
Ruby extensions for memory allocation, but unlike ALLOC_N, it only allocates
memory for one object.
❖ Similar to ALLOC_N, it interacts with the Ruby garbage collector to ensure
efficient memory management.
❖ If there's not enough memory available even after the garbage collector cleans
up, ALLOC will raise an error called NoMemError.
❖ Unlike standard malloc in C, the ALLOC function automatically casts the
allocated memory to a pointer of the same type (c-type *). This means it returns
the starting address of the allocated memory location, allowing you to directly
access and modify the object's data.
3. REALLOC_N( var, c-type, n ): This function is used in Ruby extensions for resizing
previously allocated memory.
❖ var: This is the first argument, and it's a pointer variable of type c-type*. It points
to the memory location that you want to resize.
❖ c-type: This is the second argument, and it represents the data type of the
objects stored in the memory pointed to by var.
❖ n: This is the third argument, and it represents the new number of objects you
want to fit in the resized memory.
❖ This function attempts to resize the memory block pointed to by var to
accommodate n objects of type c-type.
❖ It considers two possibilities:
➔ Increasing size: If n is larger than the current number of objects,
REALLOC_N tries to expand the memory to hold n objects.
➔ Decreasing size: If n is smaller than the current number of objects,
REALLOC_N tries to shrink the memory to fit n objects and potentially
free up unused space.
specified type and size. This memory is automatically freed when the function
called ALLOCA_N returns. It's useful for quickly allocating memory for small to
However, it's best suited for small data structures due to stack space limitations.
For larger memory needs or data persistence beyond a function's scope, heap
Duck typing means that when writing code, you're more concerned with what an
object can do (its behavior) rather than what specific type or class it belongs to.
Example:
Duck typing in Ruby can be illustrated with a classic example involving animals.
Imagine we have a function called make_sound that expects an object to respond to
a method called sound. Instead of checking if the object is specifically a "Duck", we
simply check if it responds to the sound method. If it does, we treat it as if it were a
duck, regardless of its actual type. For instance:
class Duck
def sound
puts "Quack!"
end
end
class Dog
def sound
puts "Woof!"
end
end
def make_sound(animal)
animal.sound
end
duck = Duck.new
dog = Dog.new
make_sound(duck) # Output: Quack!
make_sound(dog) # Output: Woof!
Creating an Extension
1. Create the C source code file(s) in a given directory.
2. Optionally create any supporting Ruby files in a lib subdirectory.
3. Create extconf.rb.
4. Run extconf.rb to create a Makefile for the C files in this directory.
5. Run make.
6. Run make install.
Suppose we want to create a C extension for Ruby that provides a function to add
two numbers together. Here's how we would do it:
Create the C source code file(s):
➔ Create a dedicated directory for your extension to organize the related files.
➔ Inside this directory, create a C source file (e.g., my_extension.c) that will
contain the C code implementing your extension's functionality.
Let's create a file named addition.c in our directory with the following content:
#include "ruby.h"
VALUE method_add(VALUE self, VALUE num1, VALUE num2) {
int result = NUM2INT(num1) + NUM2INT(num2);
return INT2NUM(result);
}
void Init_addition() {
VALUE MyClass = rb_define_class("MyClass", rb_cObject);
rb_define_method(MyClass, "add", method_add, 2);
}
Create supporting Ruby files:
If your extension requires additional Ruby code to interact with the C functionalities
or define helper methods, you can create a subdirectory named lib within your main
directory.
Place any relevant Ruby files (e.g., modules, classes) in this lib subdirectory for
better organization.
We won't create any supporting Ruby files for this example.
Create extconf.rb:
require 'mkmf'
create_makefile('addition')
Run extconf.rb:
Open your terminal and navigate to the directory containing your extension files.
Run the command ruby extconf.rb in the terminal. This command processes your
extconf.rb file and creates a Makefile that defines the build process for your
extension.
Open your terminal, navigate to the directory containing your files, and run ruby
extconf.rb. This will generate a Makefile for your C files.
Run make:
Once the Makefile is generated, execute the command make in the terminal. This
command uses the instructions in the Makefile to compile the C code and create the
extension library.
After extconf.rb has generated the Makefile, run make in your terminal. This will
compile your C code into a shared library.
Finally, run the command make install in the terminal. This command installs the
compiled extension library into your Ruby environment, making it available for use in
your Ruby programs.
Finally, run make install. This will install your Ruby C extension, making it available
for use in Ruby scripts.
After following these steps, you can use your C extension in Ruby scripts like this:
require 'addition'
obj = MyClass.new
puts obj.add(3, 5) #=> 8
This will create an instance of MyClass and call the add method, which is defined in
the C code, to add the two numbers.
API: Defining Classes:
1.rb_define_class():
Defines a new class at the top level with the given name and super-
class (for class Object, use rb_cObject).
Example:
rb_define_class("MyClass", rb_cObject);
MyClass is defined with rb_define_class. It will have Object as its superclass since
rb_cObject represents the Object class.
2.rb_define_module():
Defines a new module at the top level with the given name.
3.rb_define_class_under():
4.rb_define_module_under():
Syntax:
5.rb_include_module():
1.rb_define_method():
Defines a new instance method for a class or module in Ruby. It allows you to
extend the functionality of existing classes or create custom methods for new
classes.
Syntax:
void rb_define_method( VALUE classmod, char *name, VALUE(*func)(), int argc )
❖ VALUE classmod: Represents the class or module where the method will be
defined.
❖ char *name: A null-terminated string containing the name of the method to be
defined.
❖ VALUE(*func)(): A pointer to a C function that will implement the logic of the
method. This function takes the following arguments:
❖ int argc: An integer specifying the expected number of arguments for the Ruby
method. This helps ensure type safety and prevents unexpected behavior if the
number of arguments passed doesn't match the method's definition.
2.rb_define_alloc_func():
Defines an allocation function for a Ruby class or module.
This function is responsible for creating new instances of that class/module
when a call like MyClass.new is made in Ruby code.
Syntax: void rb_define_alloc_func( VALUE classmod, VALUE(*func)() )
❖ VALUE classmod: Represents the Ruby class or module for which the allocation
function is being defined.
❖ VALUE(*func)(): A pointer to a C function that will be responsible for allocating
memory and initializing new instances of the class/module. This function:
➢ Takes no arguments.
➢ Returns a VALUE which represents the newly allocated Ruby object.
3.rb_define_module_function():
Defines a function that can be called directly on the module itself using the
module name and dot notation (e.g., ModuleName.function_name). These
functions are private and cannot be called on instances of the module.
Syntax:
void rb_define_module_function( VALUE module, char *name, VALUE(*func)(),
int argc) )
4.rb_define_global_function():
Defines a global function in Ruby, accessible from anywhere in your Ruby code
without the need for an object or module prefix.These functions are similar to
built-in Ruby functions like puts or print, but they are defined in your C extension.
Syntax: void rb_define_global_function( char *name, VALUE(*func)(),int argc )
char *name: A null-terminated string containing the desired name for the global
function.
VALUE(*func)(): A pointer to a C function that implements the logic of the global
function. This function:
int argc: An integer specifying the expected number of arguments for the global
function.
5.rb_define_singleton_method():
Singleton methods defined with rb_define_singleton_method are specific to the
individual class or module instance on which they are defined. They are not
inherited by subclasses or instances created later.
Syntax:
void rb_define_singleton_method( VALUE classmod, char *name,
VALUE(*func)(), int argc )
Syntax:
VALUE rb_rescue( VALUE (*body)(), VALUE args, VALUE(*rescue)(), VALUE rargs)
API: Iterators:
Iterator:Ruby iterators provide a way to process elements in collections like
arrays or hashes. You typically use iterator blocks with methods like each, map,
reduce, etc. These methods call the block you provide once for each element in
the collection.
1.rb_iter_break( )
Purpose of rb_iter_break
Within an iterator block, if a certain condition is met for an element, you might
want to stop iterating altogether, even though there are more elements
remaining. rb_iter_break helps achieve this by signaling an immediate exit from
the loop.
Syntax:
void rb_iter_break( )
2.rb_each()
In Ruby's C API, rb_each is a function designed to iterate over elements in an
enumerable object (like an array, hash, string, etc.). However, it's important to
note that rb_each isn't directly available in Ruby code. It's an internal function
primarily used for implementing core Ruby methods like each and other iterators.
Syntax:
VALUE rb_each( VALUE obj )
3.rb_yield():
rb_yield(VALUE arg) is an internal function within Ruby's C API. It's not
something you'd use directly in everyday Ruby programming.
Executes a block of code associated with a method call in Ruby, but from C
code.
Primarily for extending Ruby or creating C extensions that interact with blocks in
a very specific way.
VALUE arg is a Ruby value that can potentially be passed to the block, but not
always.
Focus on using yield within Ruby methods to work with blocks. rb_yield is for
specialized C-level interactions.
4.rb_iterate():
The rb_iterate function is a C function provided by the Ruby C API. It's used for
iterating over a block of Ruby code, calling a given method with specified
arguments and potentially a block.
Syntax:
VALUE rb_iterate( VALUE (*method)(),VALUE args,VALUE (*block)(),VALUE arg2 )
Example:
#include "ruby.h"
VALUE my_method(VALUE arg) {
// Do something with the argument
return Qnil; // In this example, we return nil
}
VALUE my_block(VALUE arg) {
// Do something with the block argument
return Qnil; // In this example, we return nil
}
int main() {
// Initialize Ruby VM
ruby_init();
// Define arguments
VALUE args = ...; // Define your arguments
VALUE arg2 = ...; // Define your additional argument
// Call rb_iterate
rb_iterate(my_method, args, my_block, arg2);
// Clean up Ruby VM
ruby_cleanup(0);
return 0;
}
5.rb_catch():
➔ Implements exception handling using a non-standard mechanism.
➔ Temporarily intercepts exceptions (denoted by throw) within a block of code.
➔ Executes the designated code and returns a value based on whether an
exception is thrown.
Syntax:
VALUE rb_catch( const char *tag, VALUE (*proc)(), VALUE value )
Example:
def my_proc(value)
if value < 0
throw "neg_val", "Encountered negative value"
else
value * 2
end
end
result = rb_catch("neg_val") do
my_proc(-5)
end
puts result
API: Accessing Variables
1.rb_iv_get():
In Ruby, rb_iv_get( VALUE obj, char *name ) is a function used to retrieve the
value of an instance variable associated with a specific object.
❖ obj (VALUE): The Ruby object from which you want to retrieve the instance
variable.
❖ name (char *name): A C-style string representing the name of the instance
variable (without the leading @ symbol).
Example:
class Person
def initialize(name, age)
@name = name # Instance variable assignment (using `@`)
@age = age
end
def get_name
rb_iv_get(self, "@name") # Accessing instance variable using rb_iv_get
end
end
person = Person.new("Alice", 30)
name = person.get_name
puts name # Output: "Alice"
2.rb_ivar_get():
The difference lies in how the instance variable name is specified:
❖ rb_iv_get() requires you to specify the name of the instance variable as a
separate argument.
❖ rb_ivar_get() doesn't require you to specify the instance variable name explicitly;
it assumes that you want to access the instance variable with the same name
as the variable you're assigning the result to in your C code.
Syntax:
VALUE value = rb_ivar_get(obj);
3.rb_iv_set():
In Ruby, rb_iv_set( VALUE obj, char *name, VALUE value ) is a C function used to
assign a value to an instance variable associated with a specific object.
❖ Set or modify the value of an instance variable within a C extension.
Example:
#include <ruby.h>
2. Implement Methods in C:
- Write C functions that perform specific tasks, such as playing a song or stopping
playback.
- Use Ruby's C API to interact with Ruby objects and call C functions.
Example
Here's a simple example of what the C code for the Jukebox extension might look like:
#include "ruby.h"
// Example method to play a song
static VALUE play_song(VALUE self, VALUE song_name)
{
printf("Playing song: %s\n", StringValueCStr(song_name));
return Qnil;
}
// Initialization function
void Init_jukebox()
{
VALUE cJukebox = rb_define_class("Jukebox", rb_cObject);
rb_define_method(cJukebox, "play_song", play_song, 1);
}
Usage in Ruby
After compiling the extension, you can use it in Ruby as follows:
require './jukebox'
jukebox = Jukebox.new
jukebox.play_song("My Favorite Song")
This is a simplified example, but the actual Jukebox extension would include more
complex functionality, such as interacting with hardware or managing playlists.