ruby c擴展 - travel between ruby and c

什么是ruby c擴展?

我們知道,我們調用的ruby方法,很多都是由c實現(xiàn)的:


image.png

上圖所示,String類的定義,他的方法都是由c實現(xiàn)的;使用類似的方法我們也可以用c語言實現(xiàn)一些擴展功能,成為ruby的c extension。

什么時候使用 c 擴展

  • 想以 C 的速度優(yōu)化某個特別重要的方法
  • 想在 C 庫和 Ruby 之間創(chuàng)建接口(一個好用的c庫,我想在ruby里調用)

三步實現(xiàn)一個c擴展

  1. ext_conf.rb
require 'mkmf'
create_makefile 'echo'
  1. 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);
}
  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'

以上執(zhí)行后就會生成一個Makefile文件,指定了ruby的庫文件,頭文件位置,最終生成共享庫文件echo.bundle

// 引入ruby頭文件,可以使用ruby定義的數(shù)據(jù)類型,方法
#include <ruby.h>

// void Init_echo 中echo必須與上面的庫名一致
// ruby在require 擴展時會調用這個方法
void Init_echo() {
  // ruby的數(shù)據(jù)都是由 c語言的VALUE類型表示的
  // rb_cObject 就是ruby里的Object
  // rb_define_class 定義一個Echo類,以rb_cObject為基類
  VALUE echo = rb_define_class("Echo", rb_cObject);
  // 給echo類定義一個單例方法,c中的函數(shù)名為do_it,1代表有一個參數(shù)
  rb_define_singleton_method(echo, "do_it", do_it, 1);
}
// 給ruby方法對用的函數(shù)都要有返回值VALUE(ruby方法都有返回值)
// VALUE self 方法調用發(fā)
// VALUE str 傳過來的一個參數(shù)
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的穿梭;其實重要的無外乎兩點:

  1. ruby調用傳入的參數(shù)轉為c類型處理
  2. 傳出的參數(shù)包裝為VALUE(ruby數(shù)據(jù)類型)

不固定參數(shù) 及 塊調用

// args 參數(shù)數(shù)組
VALUE sum(VALUE self, VALUE args) {
  // 獲取數(shù)組長度
  long len = RARRAY_LEN(args);
  long i;
  long sum = 0; // 合計值
  for (i = 0; i < len; i++) {
    // 挨個獲取數(shù)組index的內容
    VALUE element = rb_ary_entry(args, i);
    // 轉為 long相加
    sum = sum + FIX2LONG(element);
  }
  // 如果提供了block(block_given?)
  if (rb_block_given_p()) {
    // 調用block 傳參,1:1個參數(shù)
    // 把sum * 2 轉回ruby int
    rb_yield_values(1, LONG2FIX(sum*2));
  }
  // 返回 轉回ruby int
  return LONG2FIX(sum);
}

void Init_sum() {
  // 給Objectding定義單例方法,參數(shù)-2 表示參數(shù)以數(shù)組方式傳入
  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 擴展開發(fā)人員在對象中存儲他們自己的 C 結構。與實例變量的值必須是有效的 Ruby 對象不同,任何東西都可以放置在這個結構中。

需要注意的是,當我們將 TypedData 對象傳遞回 Ruby 時,它看起來就像任何其他 Ruby 對象一樣。換句話說,我們仍然可以執(zhí)行諸如訪問實例變量和調用 TypedData 對象上的方法之類的操作。


image.png
image.png

https://github.com/secondrocker/typeddata-benchmark

基準測試結果


image.png

typeddata 最快,提升近一倍速度,ivar快點有限,ruby最慢

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 本文將會介紹,使用Go語言編寫 Ruby的擴展程序 Ruby-FFI Ruby-FFI 是一個以編程的方式加載動態(tài)...
    falm閱讀 1,077評論 0 3
  • 近期,在做一個商場項目,需要用到圖形驗證碼的功能。在網(wǎng)上找到了huacnlee[https://github.co...
    威爾士的海閱讀 567評論 0 0
  • 包(lib)、模塊(module) 在Python中,存在包和模塊兩個常見概念。 模塊:編寫Python代碼的py...
    清清子衿木子水心閱讀 3,908評論 0 27
  • 認識python(了解) 1. Python發(fā)展歷史 起源 Python的作者,Guido von Rossum ...
    幼姿沫閱讀 920評論 0 0
  • 一、異同對比選擇1、Python和ruby的相同點: * 都強調語法簡單,都具有更一般的表達方式。python是縮...
    沃倫蓋茨閱讀 4,300評論 2 24

友情鏈接更多精彩內容