1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<span class='line'><span class="cp">#include <ruby.h></span>
</span><span class='line'>
</span><span class='line'><span class="k">static</span> <span class="n">VALUE</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">VALUE</span> <span class="n">self</span><span class="p">,</span> <span class="n">VALUE</span> <span class="n">amount</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="c1">// VALUE rb_iv_set(VALUE obj, char *name, VALUE value)</span>
</span><span class='line'> <span class="n">rb_iv_set</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="s">"@balance"</span><span class="p">,</span> <span class="n">amount</span><span class="p">);</span>
</span><span class='line'> <span class="k">return</span> <span class="n">self</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="k">static</span> <span class="n">VALUE</span> <span class="nf">show_balance</span><span class="p">(</span><span class="n">VALUE</span> <span class="n">self</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="c1">// VALUE rb_iv_get(VALUE obj, char *name)</span>
</span><span class='line'> <span class="k">return</span> <span class="n">rb_iv_get</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="s">"@balance"</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="kt">void</span> <span class="nf">Init_person</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="c1">// VALUE rb_define_class(char *name, VALUE superclass)</span>
</span><span class='line'> <span class="n">VALUE</span> <span class="n">cPerson</span> <span class="o">=</span> <span class="n">rb_define_class</span><span class="p">(</span><span class="s">"Person"</span><span class="p">,</span> <span class="n">rb_cObject</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'> <span class="c1">// void rb_define_method(VALUE classmod, char *name, VALUE(*func)(), int argc)</span>
</span><span class='line'> <span class="n">rb_define_method</span><span class="p">(</span><span class="n">cPerson</span><span class="p">,</span> <span class="s">"initialize"</span><span class="p">,</span> <span class="n">initialize</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span><span class='line'> <span class="n">rb_define_method</span><span class="p">(</span><span class="n">cPerson</span><span class="p">,</span> <span class="s">"show_balance"</span><span class="p">,</span> <span class="n">show_balance</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span>
I think the code is very self-explanatory, almost like writing Ruby in C. As you can see we define a class called “Person” in the “Init_person” function (which is called by Ruby when it loads our module), where we add the functions as instance methods with “rb_define_method”. To compile this, we need an extconf.rb, which contains the following:
1
2
3
4
1
2
3
4
<span class='line'><span class="n">require</span> <span class="err">'</span><span class="n">mkmf</span><span class="err">'</span>
</span><span class='line'>
</span><span class='line'><span class="n">extension_name</span> <span class="o">=</span> <span class="err">'</span><span class="n">person</span><span class="err">'</span>
</span><span class='line'><span class="n">create_makefile</span><span class="p">(</span><span class="n">extension_name</span><span class="p">)</span>
</span>
Now we can build and run it:
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
<span class='line'>→ ruby extconf.rb
</span><span class='line'>creating Makefile
</span><span class='line'>→ make
</span><span class='line'>gcc -I. <span class="o">[</span>output snipped<span class="o">]</span>
</span><span class='line'>→ irb
</span><span class='line'>>> <span class="nv">$:</span><<<span class="s1">'.'</span> <span class="c">#=> [..., '.']</span>
</span><span class='line'>>> require <span class="s1">'person'</span> <span class="c">#=> true</span>
</span><span class='line'>>> <span class="nv">p</span> <span class="o">=</span> Person.new<span class="o">(</span>3000<span class="o">)</span> <span class="c">#=> #<Person:0x00000100a01940 @balance=3000></span>
</span><span class='line'>>> p.show_balance <span class="c">#=> 3000</span>
</span>
As you can see everything works as if we had defined the class in Ruby. Let’s add another method to deposit some money, which gives us a chance to demonstrate “rb_funcall”:
1
2
3
4
5
6
1
2
3
4
5
6
<span class='line'><span class="k">static</span> <span class="n">VALUE</span> <span class="nf">deposit</span><span class="p">(</span><span class="n">VALUE</span> <span class="n">self</span><span class="p">,</span> <span class="n">VALUE</span> <span class="n">amount</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="c1">// VALUE rb_funcall(VALUE recv, ID id, int argc, ... )</span>
</span><span class='line'> <span class="n">VALUE</span> <span class="n">result</span> <span class="o">=</span> <span class="n">rb_funcall</span><span class="p">(</span><span class="n">rb_iv_get</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="s">"@balance"</span><span class="p">),</span> <span class="n">rb_intern</span><span class="p">(</span><span class="s">"+"</span><span class="p">),</span> <span class="mi">1</span><span class="p">,</span> <span class="n">amount</span><span class="p">);</span>
</span><span class='line'> <span class="n">rb_iv_set</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="s">"@balance"</span><span class="p">,</span> <span class="n">result</span><span class="p">);</span>
</span><span class='line'> <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span>
We also need to add this function as a method in “Init_person”:
1
1
<span class='line'> <span class="n">rb_define_method</span><span class="p">(</span><span class="n">cPerson</span><span class="p">,</span> <span class="s">"deposit"</span><span class="p">,</span> <span class="n">deposit</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span>
Does it work?
1
1
<span class='line'><span class="o">>></span> <span class="nb">p</span><span class="o">.</span><span class="n">deposit</span><span class="p">(</span><span class="mi">400</span><span class="p">)</span> <span class="c1">#=> 3400</span>
</span>
It does and we’ve successfully created a Ruby class from C. Probably not the type of class you’d really implement this way, but good enough as an example.
Recommended reading: