00:00:00.480
Juan Carlos Ruiz is a software engineer, community leader, and mentor who loves knowledge sharing and teamwork. He is part of the Easy Broker product team, a multiple listing service that helps real estate agents provide the best experience in finding a home. He has participated in various technological communities, trying to help others accelerate their careers in engineering and software development.
00:00:26.800
Juan Carlos has served as a mentor in the international Ruby Me 2019 program, assisting others in building a professional development experience through working on open-source projects. Today, he will demonstrate how Ruby can operate with C language, aiming for better performance.
00:00:42.320
In this talk, I will show you how to use Ruby to create an interface for compiled languages like C. If you have any questions, please open the stream on the left side panel and leave them in the stream chat. We'll get back to you later. Juan Carlos, please take it away!
00:01:10.240
Thank you! First of all, hi everyone, welcome to my talk titled 'Going Native with FFI'. My name is Juan Carlos Ruiz, and I'm super excited to be here. This is my first talk in English, so I'm a little nervous.
00:01:24.000
Currently, I work at Easy Broker and have been working remotely since 2013, which is before remote work became popular. I love open-source projects and really enjoy contributing to communities and creating new features. I am presenting this talk from Guanajuato, Mexico.
00:01:48.080
Today, I'll discuss how Ruby can work with the C language. I feel a bit like Milhouse trying to show his emotions about the old fox because C was the first language I learned about 12 years ago in college. Although I don't use it often in my work, it provided me with the fundamentals needed to advance my career.
00:02:14.080
Now, let's explore the options available to manage native code in Ruby. The first option is MKMF. MKMF is used by Ruby C extensions to generate a makefile which will correctly compile and link the C extensions to Ruby.
00:02:30.560
The process involves having a C library and wrapping it with Ruby code to generate the makefile. The result is a bundle file, which we can then use inside a Ruby module. Let's jump to the console and try this.
00:02:50.720
I've created a small project that includes a C extension. Let's take a look inside the C extension. I will open 'vim text', and inside the 'x' directory, we will explore the 'very edge' file.
00:03:04.640
As you can see, this library is very small, containing just two simple functions. The first function, 'hello', takes a name as a parameter and prints 'Hello, [name].' The second function takes a number and returns the square of that number.
00:03:30.720
Now, let’s see how to wrap this library in Ruby. The code for this is located in my Ruby extension file. We need to import the Ruby Edge library and create new functions to wrap the existing functions in the C library.
00:03:51.440
The Ruby code required to create the extension is quite simple. After compiling it, we generate a small bundle file that can be used inside a Ruby module.
00:04:02.720
We can call my helpers, which is the namespace I defined in the C library, and that includes functions like 'c_hello' and 'c_square'. I wrap these functions within the module using 'self.hello' and 'self.square'.
00:04:15.840
Let’s open the admin console to test this out. Inside this console, I can use 'rubyx.hello' and pass a string parameter. For instance, I will call it with 'Juan'. This should display 'Hello, Juan!'.
00:04:33.280
Similarly, if I call 'rubyx.square' and pass a number, I will get the result from the functions defined in the C library. Let’s go back to the presentation.
00:04:51.840
The pros of this approach are that we can use C functions directly in Ruby code while leveraging the existing Ruby Edge library to create the extension. However, we need additional C code to create the bindings, and this solution only works with MRI.
00:05:15.680
The other option for interfacing with C is to use FFI, which stands for Foreign Function Interface. This mechanism allows a program written in one programming language to call routines or use services created in another.
00:05:42.720
Here’s a simple example taken from the FFI documentation. You only need to import the FFI gem, extend your module with the FFI library, and import the library you want to use—like the standard C library.
00:05:55.680
We will attach the function 'puts', which is the equivalent of the C puts function. Finally, we can see how the method is used, calling 'myleap.puts' and passing the parameters we want.
00:06:09.120
The advantages of using FFI are that we do not need to add additional C code. An FFI extension is multi-platform and supports multiple Ruby implementations. On the downside, complex macros can be difficult to maintain.
00:06:37.760
Now, I want to demonstrate an example of how to use the FFI gem to encapsulate a C library. I will create a simple implementation that deals with PGM images. A PGM image is a grayscale image stored in plain text.
00:07:03.840
The header structure of a PGM file includes a magic number, dimensions, and the maximum gray value, which represents white and black.
00:07:08.480
Now, this is the content of the PGM header implemented in C. The file structure includes four methods: load_pgm, free_pgm, and invert_colors, the latter being the focus of this presentation.
00:07:15.840
Once we have the library implemented, we need to compile it to create a shared library. This shared library has the .so extension, which allows us to include it in our Ruby module that extends from FFI.
00:07:48.960
Next, we need to add a PGM class that inherits from FFI::Library. This class is responsible for mapping the image structure defined in the C library, allowing easier interaction with the Ruby code.
00:08:17.440
For each function we want to use from the C library, we need to use the FFI attach function. The first parameter is the name of the function in Ruby, the second is the name in the C library, followed by an array of parameters and the return type.
00:08:54.760
Let's see how this works in the console. Inside my example folder, I have a PGM image file. I will open this file to show you that it is plain text.
00:09:09.200
Now, let’s execute the code in Ruby to process this image file. I will create an instance for both the input image and the output image using the PGM bindings.
00:09:54.080
Next, we will load the image into the PGM instance using the load_pgm method, passing necessary parameters like the path to the image. Now that it is loaded, we can call the invert_colors function.
00:10:26.400
After inverting the colors, we will save the output using the save_pgm function, specifying the output path. Let's check our disk to see the output image.
00:10:55.200
As you can see, we successfully utilized the PGM library within our Ruby code. This library implementation simplifies interactions with C functions, allowing for efficient processing of image files.
00:11:34.320
In conclusion, use MKMF when you require total control over your C code, or if you need to wrap a small library. Use FFI if you prefer not to write C code for creating bindings, especially for projects favoring multi-platform compatibility.
00:11:56.560
The code for this example is available on my GitHub account. I hope you enjoyed this talk. If you'd like to chat, you can find me as Juan CRG90 on social networks.
00:12:20.480
Thank you for watching! If you have any questions, feel free to reach out.
00:12:38.960
(Audiences ask questions and discuss topics relevant to the talk.)