什么是ruby c扩展?
我们知道,我们调用的ruby方法,很多都是由c实现的:
image.png
上图所示,String类的定义,他的方法都是由c实现的;使用类似的方法我们也可以用c语言实现一些扩展功能,成为ruby的c extension。
什么时候使用 c 扩展
- 想以 C 的速度优化某个特别重要的方法
- 想在 C 库和 Ruby 之间创建接口(一个好用的c库,我想在ruby里调用)
三步实现一个c扩展
- ext_conf.rb
require 'mkmf'
create_makefile 'echo'
- echo.c
#include <stdio.h>
#include <ruby.h>
VALUE do_it(VALUE self, VALUE str) {
const char* c_string = StringValueCStr(str);
printf("from ruby:%s\n",c_string);
return Qnil;
}
void Init_echo() {
VALUE echo = rb_define_class("Echo", rb_cObject);
rb_define_singleton_method(echo, "do_it", do_it, 1);
}
- 生成,调用
ruby extconf.rb #create makefile
make # 编译生成c扩展
ruby -e "require './echo';Echo.do_it('abc')" # ruby调用
# from ruby:abc
详解
require 'mkmf'
create_makefile 'echo'
以上执行后就会生成一个Makefile文件,指定了ruby的库文件,头文件位置,最终生成共享库文件echo.bundle
// 引入ruby头文件,可以使用ruby定义的数据类型,方法
#include <ruby.h>
// void Init_echo 中echo必须与上面的库名一致
// ruby在require 扩展时会调用这个方法
void Init_echo() {
// ruby的数据都是由 c语言的VALUE类型表示的
// rb_cObject 就是ruby里的Object
// rb_define_class 定义一个Echo类,以rb_cObject为基类
VALUE echo = rb_define_class("Echo", rb_cObject);
// 给echo类定义一个单例方法,c中的函数名为do_it,1代表有一个参数
rb_define_singleton_method(echo, "do_it", do_it, 1);
}
// 给ruby方法对用的函数都要有返回值VALUE(ruby方法都有返回值)
// VALUE self 方法调用发
// VALUE str 传过来的一个参数
VALUE do_it(VALUE self, VALUE str) {
// StringValueCStr 把 ruby的string转化为c的char*
const char* c_string = StringValueCStr(str);
// 打印
printf("from ruby:%s\n",c_string);
// Qnil就是ruby的nil
return Qnil;
}
至此,我们完成了从ruby到c的穿梭;其实重要的无外乎两点:
- ruby调用传入的参数转为c类型处理
- 传出的参数包装为VALUE(ruby数据类型)
不固定参数 及 块调用
// args 参数数组
VALUE sum(VALUE self, VALUE args) {
// 获取数组长度
long len = RARRAY_LEN(args);
long i;
long sum = 0; // 合计值
for (i = 0; i < len; i++) {
// 挨个获取数组index的内容
VALUE element = rb_ary_entry(args, i);
// 转为 long相加
sum = sum + FIX2LONG(element);
}
// 如果提供了block(block_given?)
if (rb_block_given_p()) {
// 调用block 传参,1:1个参数
// 把sum * 2 转回ruby int
rb_yield_values(1, LONG2FIX(sum*2));
}
// 返回 转回ruby int
return LONG2FIX(sum);
}
void Init_sum() {
// 给Objectding定义单例方法,参数-2 表示参数以数组方式传入
rb_define_singleton_method(rb_cObject, "sum", sum, -2);
}
常用的一些方法
https://docs.ruby-lang.org/en/3.2/extension_rdoc.html
typed_data_struct
TypedData 对象允许 C 扩展开发人员在对象中存储他们自己的 C 结构。与实例变量的值必须是有效的 Ruby 对象不同,任何东西都可以放置在这个结构中。
需要注意的是,当我们将 TypedData 对象传递回 Ruby 时,它看起来就像任何其他 Ruby 对象一样。换句话说,我们仍然可以执行诸如访问实例变量和调用 TypedData 对象上的方法之类的操作。
image.png image.png
https://github.com/secondrocker/typeddata-benchmark
基准测试结果
image.png
typeddata 最快,提升近一倍速度,ivar快点有限,ruby最慢
网友评论