Ruby FFI - Ruby调用C库最棒的工具

Ruby FFI - Ruby调用C库最棒的工具

2008-11-02 00:40 by 见习编辑  robbin  评论(6)  有4162人浏览
Ruby  C++  C  C#
Ruby解析器的性能一向被人垢病,很多性能敏感的程序,都必须用C语言来编写,然后使用Ruby去调用C库。但糟糕的是,Ruby调用C库并不是一件轻松的活,需要你对Ruby的内部数据结构有比较深入的了解,甚至需要你仔细阅读Ruby相关的源代码,然后用C语言编程作为黏合剂,用Ruby特有的C API去给外部的C库增加一层封装。 

以上的过程,相当于你要打开Ruby解析器的内部进行API调用了,对程序员的要求很高。而且一旦Ruby内部的数据结构随着版本升级发生变动,你的这些黏合剂程序就必须重写。比方说现在Ruby 1.9出来以后,绝大部分Ruby的C扩展库统统无法正常编译。 

基于以上这些原因,给Ruby写C的扩展库一件非常痛苦的事情,而且也是Ruby社区程序员一直抱怨的问题之一。然而随着FFI的出现,我们即将告别这些痛苦的历史! 

FFI即Foreign Function Interface,外部函数调用接口,并非Ruby独有的概念,只不过因为Ruby扩展库带来的痛苦,使得Ruby的FFI显得格外迫切。FFI最早已经在Rubinius虚拟机平台上实现了,随后在JRuby上面也得以实现,而今天:2008年11月1日,在Ruby官方版本的解析器Ruby 1.8.6/1.8.7和1.9版本上也可以使用FFI了。 

安装FFI很简单: 

Ruby代码 
  1. gem install ffi  


使用FFI也很简单,比方说调用C语言标准库的printf函数,我们可以这样来写: 

Ruby代码 
  1. require 'ffi'  
  2. module MyExtModule  
  3.   extend FFI::Library  
  4.   attach_function :printf, [:string], :void  
  5. end  
  6.   
  7. MyExtModule.printf("Hello FFI\n")  


用attach_fuction就可以把C语言标准库的printf函数绑定到ruby里面来了,后面的参数分别声明C函数的传入参数和返回值。如果需要引入其他C库,可以预先使用ffi_lib指定库的名称。(我使用ffi_lib未能绑定非标准库,有可能是FFI的bug?也有可能是我没有设置对?) 

有了FFI,在Ruby语言里面调用C库,就变成了一件异常轻松的事情,完全不需要ruby程序员再去吭哧吭哧啃C语言了,只要你手里捧着C库的API手册能看清楚函数的参数定义就够用了,然后你就可以直接在ruby里面随心所欲的调用它了,在调用之前,只需要用attach_function进行一次函数绑定声明即可。这对于整个ruby社区来说是一件非常棒的好消息。 

在FFI的源代码树的samples目录下面还有几个小例子,大家可以自己看看: http://kenai.com/projects/ruby-ffi/sources/723/show 

作为一个对比,我们来看看传统的ruby扩展库是如何调用C库的吧。以上面的为例,如果我们希望在ruby里面使用C库的printf函数,那么要先用ruby API的格式来写一段C代码去封装它: 

C代码 
  1. #include   
  2.    
  3. static VALUE  
  4. dummy_printf(VALUE self, VALUE format, VALUE num)  
  5. {  
  6.     int rlt = printf(RSTRING_PTR(format), NUM2INT(num));  
  7.     return INT2FIX(rlt);  
  8. }  
  9.    
  10. void Init_printf()  
  11. {  
  12.     /* register the method to the Kernel module */  
  13.     rb_define_method(rb_mKernel, "dummy_printf",  
  14.                      RUBY_METHOD_FUNC(dummy_printf), 2);  
  15. }  


当然你必须对ruby.h里面定义的ruby内部数据结构和ruby内部的函数调用有充分的了解。然后你要创建一个extconf.rb文件: 

Ruby代码 
  1. require 'mkmf'   
  2. create_makefile('printf')  


运行这个文件:ruby extconf.rb,会创建出来编译这段C代码的Makefile,然后你再编译和安装,最终这个共享链接库会被拷贝到你的ruby的本地扩展库目录下面。最后你才可以在ruby里面调用这个封装好的dummy_printf方法。 

另外,对于通过FFI去调用比较复杂的C库的时候,会涉及到不同语言之间的数据类型转换问题,关于数据类型的对照表,以及FFI使用更详细的介绍请看张驰原(他也是rmmseg的作者)的文章: http://pluskid.lifegoo.com/?p=370 

你可能感兴趣的:(Ruby FFI - Ruby调用C库最棒的工具)