0% found this document useful (0 votes)
13 views19 pages

SL 2

The document provides a comprehensive guide on extending Ruby with C, detailing the steps to create a Ruby C extension, including writing C code, compiling it, and testing it within Ruby. It explains Ruby's object representation in C, memory management, and the benefits of wrapping C structures for performance. Additionally, it covers Ruby's dynamic and duck typing, embedding Ruby in other languages, and similarities with languages like C, Java, and Python.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
13 views19 pages

SL 2

The document provides a comprehensive guide on extending Ruby with C, detailing the steps to create a Ruby C extension, including writing C code, compiling it, and testing it within Ruby. It explains Ruby's object representation in C, memory management, and the benefits of wrapping C structures for performance. Additionally, it covers Ruby's dynamic and duck typing, embedding Ruby in other languages, and similarities with languages like C, Java, and Python.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 19

💡 Extending Ruby with C

Extending Ruby means adding new features using C to make Ruby programs:

 Run faster
 Use less memory
 Connect with system tools

Ruby is slow because it’s an interpreted language. Writing performance-critical parts in C can
significantly improve speed and efficiency.

🛠️Creating a Ruby C Extension (Step-by-Step with Example)

We’ll build a C extension that adds two numbers.

✅ Step 1: Create a New Directory

mkdir myext && cd myext

✅ Step 2: Write the C Extension Code

Create a file named myext.c:

#include <ruby.h>

// Function to add two numbers


VALUE add(VALUE self, VALUE num1, VALUE num2) {
int a = NUM2INT(num1); // Convert Ruby value to C integer
int b = NUM2INT(num2);
return INT2NUM(a + b); // Convert C integer back to Ruby value
}

// Initialize the extension


void Init_myext() {
VALUE MyExtension = rb_define_module("MyExtension");
rb_define_method(MyExtension, "add", RUBY_METHOD_FUNC(add), 2);
}

The function void Init_myext():


 Defines a new Ruby module MyExtension.
 Registers a method "add" in that module.
 The add method maps to the C function add() and takes 2 Ruby arguments.

✅ Step 3: Create extconf.rb

This generates a Makefile to compile the extension:

require "mkmf"
create_makefile("myext")

✅ Step 4: Compile the Extension

ruby extconf.rb
make

➡️This generates myext.so (on Linux/macOS) or myext.dll (on Windows)

✅ Step 5: Test the Extension in Ruby

Create test.rb:

require "./myext"
include MyExtension

puts add(10, 20) # Output: 30


puts add(7, 5) # Output: 12

Run:

ruby test.rb

🖨️Output

30
12

🔷 Ruby Objects in C (Simplified Notes)

🔹 How Ruby Represents Data in C

 In Ruby, everything is an object, and variables hold references to objects.


 In C (inside the Ruby interpreter), all Ruby objects are represented using the type VALUE.

🔹 What Is VALUE?

 VALUE is a C type used to represent any Ruby object.


 Usually, it stores the address of the object in memory, not the actual object.

🔹 Exception: Immediate Objects

Some common values are stored directly inside the VALUE instead of using pointers, for speed
and memory efficiency.

These are called immediate values:

 Fixnum (integers)
 Symbol
 true
 false
 nil

This avoids memory allocation and makes Ruby faster.

🔹 Checking Object Types with Macros

To identify the type stored in a VALUE, Ruby provides macros:

Macro Purpose

FIXNUM_P(val) Is the value a Fixnum?

SYMBOL_P(val) Is the value a Symbol?

NIL_P(val) Is the value nil?

RTEST(val) Is the value truthy? (not nil/false)


1. Converting Integers to Ruby Numbers

int num = 42; INT2NUM(num); // Converts int to Fixnum or Bignum

int num = 42; INT2FIX(num); // Converts int to Fixnum (faster)

(do the same for remaining data types)

int c_int = NUM2INT(INT2NUM(42));

printf("%d\n", c_int); // Output: 42

int c_fixnum = FIX2INT(INT2FIX(10));

printf("%d\n", c_fixnum); // Output: 10


Working with Ruby Strings in C

In C, strings are null-terminated, meaning they end with a special character '\0' to mark the
end of the string.
However, Ruby strings can contain null characters inside them, so C-style string handling
doesn’t work safely.

Ruby strings are stored in an RString structure, which includes:

 RSTRING_LEN(str) → Gets the length of the Ruby string.

 RSTRING_PTR(str) → Gets a pointer to the string’s data.

 RSTRING_END(str) → Gets a pointer to the end of the string.

Working with Other Objects

Ruby objects in C have specific structures (e.g., RString, RArray). The following macros help
access these structures safely.

🎵 What Is the Jukebox Extension?

Imagine there's a C library from a company that controls a CD Jukebox.


You want to use that jukebox in Ruby, but Ruby can't understand C structures directly.

So we wrap the C structure (CDJukebox) inside a Ruby object using special tools Ruby gives us.
This is called creating a Ruby C extension.
🧱 What Are We Trying to Do?

We want to:

 Wrap a C struct (CDJukebox *) in a Ruby object


 Make it behave like a Ruby class
 Let Ruby manage its memory safely
 Support .new, .clone, etc.

🔧 Step-by-Step Breakdown

🔹 Step 1: Wrap C Structure as Ruby Object

Ruby gives us a macro:

Data_Wrap_Struct(klass, mark, free, pointer);

Example:

Data_Wrap_Struct(klass, 0, cd_free, jukebox);

What this does:

 klass – the Ruby class (e.g., CDPlayer)


 cd_free – function to clean up C memory when Ruby is done
 jukebox – the C pointer we are wrapping

✅ It turns a C pointer into a Ruby object!

🔹 Step 2: Allocation Function

This function is called when you write CDPlayer.new in Ruby — before initialize is run.

static VALUE cd_alloc(VALUE klass) {


CDJukebox *jukebox = new_jukebox(); // C library call
return Data_Wrap_Struct(klass, 0, cd_free, jukebox);
}

✅ This creates the jukebox and wraps it as a Ruby object.


🔹 Step 3: Initialization Function

The initialize method is called after allocation to assign a unit ID to the jukebox.

Now Ruby calls initialize, and you can give values.

static VALUE cd_initialize(VALUE self, VALUE unit) {


CDJukebox *jb;
Data_Get_Struct(self, CDJukebox, jb); // Unwrap C pointer
assign_jukebox(jb, NUM2INT(unit)); // Set the ID
return self;
}

 Data_Get_Struct gives you the original CDJukebox * from the Ruby object
 assign_jukebox() sets its unit ID using the C library

🔹 Step 4: Garbage Collection (Free Memory)

When Ruby deletes the object, it must also clean the C memory.

static void cd_free(void *p) {


free_jukebox((CDJukebox *)p);
}

 This is called automatically by Ruby when the object is garbage collected


 It avoids memory leaks

🔹 Step 5: Support for .clone or .dup

If you write jukebox.clone, Ruby:

1. Allocates a new object


2. Copies variables
3. Calls initialize_copy

✅ This ensures cloned objects also get a proper copy of the jukebox struct.
Memory Allocation

When writing Ruby extensions in C, you might need to allocate memory manually, especially
for:

 Large data structures (e.g., images, Bloom filters)


 External C libraries

Don’t use malloc() directly!

Ruby has its own Garbage Collector (GC), and using regular C functions like malloc can
confuse it.
🧬 Ruby Type System

Ruby is both dynamically typed and duck-typed. This means Ruby doesn't care about an
object's type — only about whether it behaves the way we expect.

🔹 1. Dynamic Typing in Ruby

Ruby does not require type declarations. The type of a variable is decided at runtime, based
on the value you assign.

✅ Example: Dynamic Typing

x = 10 # Integer
x = "Hello" # Now it's a String
x = 3.14 # Now it's a Float

puts x # Output: 3.14

💬 Explanation:

 xstarts as an integer, becomes a string, and then a float.


 Ruby lets you reassign different types to the same variable.

🔸 2. Duck Typing

Duck typing is based on what an object can do, not what it is.

“If it looks like a duck, swims like a duck, and quacks like a duck — it’s probably a duck.”

This means: If an object has the methods you need, you can use it, regardless of its class.

✅ Example: Duck Typing

def display(duck)
duck.swim
duck.speak
end

class Duck
def swim
puts "can swim"
end
def speak
puts "Quack!"
end
end

class Person
def swim
puts "can swim"
end
def speak
puts "speaks"
end
end

d = Duck.new
p = Person.new

display(d)
display(p)

✅ Output:

can swim
Quack!
can swim
speaks

✅ Advantages of Duck Typing

 Flexible: Any object with the required methods can be used.


 Simple Code: No need to declare or check types.
 Reusability: Same function works with different types of objects.

⚠️Disadvantages of Duck Typing

 Runtime Errors: If a method is missing, error shows only while running.


 Hard to Read: You can’t always tell what type of object is expected.
 Maintenance Issues: In large codebases, it’s easy to lose track of required methods.

🔄 Embedding Ruby into Other Languages

Embedding Ruby means using the Ruby interpreter inside programs written in other languages
like C, C++, Java, Python, etc.
This allows these host programs to run Ruby code or scripts and use Ruby’s features within their
workflows.
🧬 Why Embed Ruby?

 To add scripting support to your app


 To allow customization using Ruby
 To access Ruby's expressive syntax and dynamic features
 To combine the power of compiled languages with Ruby's flexibility

🧩 Ruby’s Similarities with Other Languages

Ruby’s syntax and behavior are familiar to developers coming from other languages. Here's a
comparison:

🛠️C

 Ruby has a close relationship with C (it's built in C!).


 Similarities:
o Strings use double quotes: "Hello"
o Strings are mutable (can be modified)
o Embedding works easily via C functions like ruby_init(), rb_eval_string()

➕ C++

 Ruby borrows object-oriented ideas from C++.


 Similarities:
o Access control keywords: public, private, protected
o Exception handling is similar (but Ruby uses begin, rescue, ensure instead of
try, catch)

☕ Java

 Java is fast, compiled, and has a rich ecosystem.


 Switching from Java to Ruby:
o Code is shorter and quicker to write
o Prototypes can be built faster
 Similarities:
o Automatic memory management via Garbage Collector
o Strongly typed objects
o Access modifiers (public, private, protected) behave similarly

🐪 Perl

 Perl developers will feel at home with Ruby.


 Similarities:
o Method calls don’t always need parentheses
o Strings are flexible and easy to manipulate
o Ruby has closures (blocks, procs, lambdas) — just like Perl

🐘 PHP

 PHP is popular for web development; Ruby is too (thanks to Rails).


 Ruby is considered cleaner and more structured.
 Similarities:
o Both are dynamically typed — no need to declare variable types
o Used heavily in web apps
 Ruby encourages a more elegant, framework-driven approach (e.g., Ruby on Rails)

🐍 Python

 Python and Ruby are often compared for their simplicity and elegance.
 Similarities:
o Multi-line strings are easy (e.g., triple quotes in Python, <<-HEREDOC in Ruby)
o Both are dynamically and strongly typed
 Variables don’t need type declarations
 But types are not automatically changed once assigned
o Both support functional programming:
 Lambdas, closures, first-class functions
o Syntax is clean and readable

✅ Summary
Language Similarity with Ruby

C Mutable strings, double quotes, easy to embed

C++ Access control, exceptions


Language Similarity with Ruby

Java Garbage collection, OOP, faster prototyping in Ruby

Perl Optional parentheses, closures

PHP Dynamic typing, web development focus

Python Clean syntax, dynamic/strong typing, functional support

🔗 Embedding a Ruby Interpreter

Embedding Ruby means using the Ruby interpreter inside a C program.


This lets the C program run Ruby scripts, making the program more dynamic and scriptable.

📄 Ruby Script Example (add.rb)


# File: add.rb
a = 15
b = 25
sum = a + b
puts "Sum = #{sum}"

🛠️Steps to Embed Ruby in C

🔹 1. Include the Ruby Header

At the top of your C file:

#include <ruby.h>

🔹 2. Initialize the Ruby Interpreter

Before running any Ruby code:

ruby_init(); // Start the Ruby interpreter


ruby_init_loadpath(); // Set up Ruby's load path

🔹 3. Run Ruby Code


✅ Option 1: Run Ruby Code Inline

Use rb_eval_string() to run Ruby directly as a string:

rb_eval_string("puts 'Hello from Ruby!'");


rb_eval_string("puts 10 + 20");

✅ Option 2: Load a Ruby File

Use rb_load_file() and ruby_exec() to run a Ruby script file:

rb_load_file("add.rb"); // Load script


ruby_exec(); // Run script

🔹 4. Finalize the Interpreter

After running your Ruby code, shut the interpreter down cleanly:

ruby_finalize();

🧪 Example: C Program That Runs Ruby Script


#include <ruby.h>

int main() {
ruby_init();
ruby_init_loadpath();
rb_load_file("add.rb");
ruby_exec(); // Only needed in older Ruby versions
ruby_finalize();
return 0;
}

⚙️5. Compile and Run


Compile and run the program

✅ Output:

Sum = 40
🌍 Global Variables in Ruby (not a subtopic)

🔹 Definition:

A global variable is a variable that can be accessed and changed from anywhere in a Ruby
program — inside methods, classes, modules, or even outside them.

 Global variables start with a $ (dollar sign).


👉 Examples: $count, $name, $total

🔑 Key Features

Feature Description

Scope Global — available throughout the entire program

Prefix Always starts with a $

Persistence Value remains until explicitly changed

Caution Can lead to bugs if overused or misused

⚠️Disadvantages of Global Variables


 ❌ Breaks encapsulation (everything can access/change it)
 ❌ Makes debugging harder (anyone can change the value)
 ❌ Reduces code readability and maintainability
 ❌ Best to avoid unless absolutely needed

✅ Simple Example
$greeting = "Hello"

def greet_user
puts $greeting + ", User!"
end

class Messenger
def change_greeting
$greeting = "Welcome"
end
end

greet_user # Output: Hello, User!

msg = Messenger.new
msg.change_greeting

greet_user # Output: Welcome, User!

🔗 Wrapping C Structures in Ruby (not a subtopic)

Wrapping C structures means turning a C struct into a Ruby object. This allows Ruby code
to access, store, and modify C-level data using Ruby-style objects.

🎯 Why Wrap C Structures?

 ✅ Improve performance (C is faster than Ruby)


 ✅ Reuse existing C libraries inside Ruby
 ✅ Expose custom data types to Ruby users

🧱 Main Functions Used for Wrapping

🔹 1. Data_Wrap_Struct
VALUE Data_Wrap_Struct(VALUE klass, void (*mark)(), void (*free)(), void
*ptr);

✅ What It Does:

 Wraps a C pointer into a Ruby object.


 Connects it with a Ruby class (klass).
 Optionally registers:
o mark() – for Ruby GC (optional, use NULL if not needed).
o free() – called to clean up memory.

🔹 2. Data_Make_Struct
VALUE Data_Make_Struct(VALUE klass, c_type, mark, free, c_type *ptr);

✅ What It Does:

 Shortcut for:
o ALLOC()
o memset()
o Data_Wrap_Struct()
 Automatically allocates, zeroes, and wraps memory.
 Also gives you direct access to the C pointer.

🔹 3. Data_Get_Struct
Data_Get_Struct(VALUE obj, c_type, c_type *ptr);

✅ What It Does:

 Extracts the original C struct pointer from a Ruby object.


 Lets you access and modify the struct inside the Ruby object.

🧱 Object Creation in Ruby (not a subtopic)

An object is an instance of a class. In Ruby, we create objects using the .new method.

✅ Syntax
object_name = ClassName.new
🐶 Example: Defining a Class and Creating an Object
class Dog
def initialize(name, breed)
@name = name
@breed = breed
end

def speak
puts "#{@name} says woof!"
end

def info
puts "Name: #{@name}, Breed: #{@breed}"
end
end

my_dog = Dog.new("Buddy", "Golden Retriever")

my_dog.speak # Output: Buddy says woof!


my_dog.info # Output: Name: Buddy, Breed: Golden Retriever

🔁 Cloning Objects in Ruby (not a subtopic)

Cloning creates a copy of an existing object. Ruby provides two methods:

1️⃣ dup

 Creates a shallow copy of the object.


 Does not copy:
o Singleton methods
o Frozen/tainted state (in older Ruby versions)
 Keeps the object's class and instance variables.

2️⃣ clone

 Also creates a shallow copy


 Does copy:
o Singleton methods
o Frozen and tainted state
 More complete than dup

You might also like