Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Introduction to C Extensions
Search
sylph01
March 09, 2025
Programming
300
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Introduction to C Extensions
@ kyoto.rb 2025/3/9
sylph01
March 09, 2025
More Decks by sylph01
See All by sylph01
人命を救う技術としてのEnd-to-End暗号化とMessaging Layer Security
sylph01
3
230
Updates on MLS on Ruby (and maybe more)
sylph01
1
290
End-to-End Encryption Saves Lives. You Can Start Saving Lives With Ruby, Too (RubyConf Taiwan 2025 ver.)
sylph01
1
170
PicoRuby's Networking is Incomplete
sylph01
1
280
The Definitive? Guide To Locally Organizing RubyKaigi
sylph01
9
3.9k
End-to-End Encryption Saves Lives. You Can Start Saving Lives With Ruby, Too
sylph01
1
220
End-to-End Encryption Saves Lives. You Can Start Saving Lives With Ruby, Too (JP subtitles)
sylph01
2
970
"Actual" Security in Microcontroller Ruby!?
sylph01
0
240
Everyone Now Understands AuthZ/AuthN and Encryption Perfectly and I'm Gonna Lose My Job
sylph01
1
130
Other Decks in Programming
See All in Programming
act1-costs.pdf
sumedhbala
0
230
「正の参照」と 「負の導出」で組む ハーネスエンジニアリング
cottpan
1
140
The Past, Present, and Future of Enterprise Java
ivargrimstad
0
240
【やさしく解説 設計編 #1】「ドメイン駆動」と「実装駆動」ってなに? 〜設計の考え方を、たとえ話で学ぼう〜
panda728
PRO
1
120
なぜ関数型プログラミングで「型」と「証明」が語られるのか #fp_matsuri
kajitack
3
930
分散システム、なんですぐ死んでしまうん?耐障害性を高めたいあなたのためのレジリエンスパターン入門
mshibuya
7
6.2k
AI時代、エンジニアはどう育つのか -未経験エンジニアの成長を間近で見て考えたこと-
thasu0123
0
110
Claude Opus 4.6以後の受託開発エンジニアの変化(Claude Code開発ノウハウ大公開スペシャルbyクラスメソッド)
iidatakuma
1
760
ランチタイムLT会3周年!ランチタイムLT会を3年間続けられたお話
y0hgi
1
140
関数型プログラミングのメリットって何だろう?
wanko_it
0
180
エンジニアにデザインハーネスを 〜デザインプロセスを規定するためのハーネス〜 / Design harness from an engineer's perspective
rkaga
2
1.5k
地域 SRE コミュニティ最前線 - ホンマでっかSRE勉強会
tk3fftk
0
230
Featured
See All Featured
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.8k
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
4
2.9k
Building the Perfect Custom Keyboard
takai
2
810
GraphQLとの向き合い方2022年版
quramy
50
15k
Deep Space Network (abreviated)
tonyrice
0
230
WCS-LA-2024
lcolladotor
0
710
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
490
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
2
240
Introduction to Domain-Driven Design and Collaborative software design
baasie
1
900
SEOcharity - Dark patterns in SEO and UX: How to avoid them and build a more ethical web
sarafernandez
0
220
The B2B funnel & how to create a winning content strategy
katarinadahlin
PRO
1
420
[SF Ruby Conf 2025] Rails X
palkan
2
1.2k
Transcript
Introduction to C Extensions Ryo Kajiwara (sylph01), 2025/3/9 @ kyoto.rb
1
RubyKaigi, more like CKaigi, huh? よく冗談めかして言われますね 2
C 拡張って何 C 言語でRuby の機能を書くこと gem の形で使うことが多い C やアセンブリで書いたほうが速い部分をC で書く
実はJIT のおかげで必ずしもC だから速いというわけでもない C 以外の言語の入り口になることもある そういえば今年Go gem の話がありますね? 3
現代RubyKaigi ではもはやC 拡張の話は 当たり前のこととして通過される RubyKaigi のトークには新規性が必要(なことが多い) Ruby Core としてはもはや当たり前のもの C
で書かれたライブラリのラッパーだけでは新規性がない とはいえスルーするにはあまりにも不親切。なので今回できる限りの解 説を試みる。全部は 私もわからないので 解説しきれません。 4
見るべき2 大ドキュメント ruby/ruby のdoc/extension.rdoc 日本語がある(リンクは日本語のほう) The Definitive Guide to Ruby's
C API 5
実際に自分が手を入れた/ ているコー ドがこちら https:/ /github.com/sylph01/openssl/blob/hpke/ext/openssl/ossl_hpke _ctx.c https:/ /github.com/sylph01/openssl/blob/hpke/ext/openssl/ossl_ hpke_ctx.h https:/
/github.com/sylph01/openssl/blob/hpke/ext/openssl/ossl.c から Init_ossl_hpke_ctx() が呼ばれるのがエントリーポイ ント 6
クラス/ モジュールの定義 void Init_ossl_hpke_ctx(void) { mHPKE = rb_define_module_under(mOSSL, "HPKE"); cContext
= rb_define_class_under(mHPKE, "Context", rb_cObject); cSenderContext = rb_define_class_under(cContext, "Sender", cContext); cReceiverContext = rb_define_class_under(cContext, "Receiver", cContext); eHPKEError = rb_define_class_under(mHPKE, "HPKEError", eOSSLError); ... class OpenSSL::HPKE::Context class OpenSSL::HPKE::Context::Sender class OpenSSL::HPKE::Context::Receiver class OpenSSL::HPKE::Error 7
メソッドの定義 rb_define_method(cSenderContext, "initialize", ossl_hpke_ctx_new_sender, 2); (1) 定義したいクラス、(2) メソッド名、(3) 実装を示す関数ポインタ、(4) 引数の個数
以下に相当 class OpenSSL::HPKE::Context::Sender def initialize(arg1, arg2) end 8
メソッドの定義 VALUE ossl_hpke_ctx_new_sender(VALUE self, VALUE mode, VALUE suite) { ...
C の世界ではRuby のオブジェクトは全部 VALUE 自身がどんな型であるかを知っているデータ(へのポインタ) (1) self 、(2) 以降は rb_define_method で指定した引数の数だけ VALUE が続く return で返す VALUE がRuby の世界で返る値 9
メソッドの定義 おまけ: モジュール関数の場合は rb_define_module_function rb_define_module_function(mHPKE, "keygen", ossl_hpke_keygen, 3); おまけ2: mruby/c
ではどうするの?→るびま0064 号のRubyKaigi 2024 の トーク解説記事 10
attributes // attr_readers for suite values rb_define_attr(cContext, "kem_id", 1, 0);
rb_define_attr(cContext, "kdf_id", 1, 0); rb_define_attr(cContext, "aead_id", 1, 0); https:/ /docs.ruby-lang.org/ja/latest/function/rb_define_attr.html 第3 引数はread 、第4 引数はwrite 11
インスタンス変数 rb_iv_set(self, "@kem_id", kem_id); kem_id = rb_iv_get(suite, "@kem_id"); get するときは
VALUE が返ってくるので、Ruby の世界のinteger をC で使 う場合は NUM2INT(kem_id) みたいな形で変換する。 12
定数の呼び出し https:/ /docs.ruby-lang.org/ja/latest/function/rb_const_get_at.html による と VALUE rb_const_get_at(VALUE klass, ID name)
。 ID → rb_intern で名前との対応関係が得られる。 mode_table = rb_const_get_at(cContext, rb_intern("MODES")); 13
Ruby の世界の関数を呼び出す mode_id = rb_funcall(mode_table, rb_intern("[]"), 1, mode); ↓ mode_id
= MODES[mode] rb_p(rb_funcall(rbstr, rb_intern("unpack1"), 1, rb_str_new_cstr("H*"))); ↓ p(str.unpack1("H*")) 14
C の構造体をRuby で包む static void ossl_hpke_ctx_free(void *ptr) { OSSL_HPKE_CTX_free(ptr); }
const rb_data_type_t ossl_hpke_ctx_type = { "OpenSSL/HPKE_CTX", { 0, ossl_hpke_ctx_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY }; 15
C の構造体をRuby で包む static VALUE hpke_ctx_new0(VALUE arg){ OSSL_HPKE_CTX *ctx =
(OSSL_HPKE_CTX *)arg; VALUE obj; obj = rb_obj_alloc(cContext); RTYPEDDATA_DATA(obj) = ctx; return obj; } VALUE ossl_hpke_ctx_new(OSSL_HPKE_CTX *ctx){ VALUE obj; int status; obj = rb_protect(hpke_ctx_new0, (VALUE)ctx, &status); if (status) { OSSL_HPKE_CTX_free(ctx); rb_jump_tag(status); } return obj; 16
C の構造体をRuby で包む static VALUE ossl_hpke_ctx_alloc(VALUE klass) { return TypedData_Wrap_Struct(klass,
&ossl_hpke_ctx_type, NULL); } void Init_ossl_hpke_ctx(void) { mHPKE = rb_define_module_under(mOSSL, "HPKE"); cContext = rb_define_class_under(mHPKE, "Context", rb_cObject); ... rb_define_alloc_func(cContext, ossl_hpke_ctx_alloc); } 17
C 拡張ってどうやってビルドするの? https:/ /docs.ruby-lang.org/ja/latest/library/mkmf.html Makefile を生成するためのライブラリ。 extconf.rb からmkmf をrequire してMakefile
が作られてshared object が作ら れる。 皆さんももしかしたら apt-get し足りない何かがあったときに extconf.rb がfailed になってるのを見たことあるかもしれない。 18
おまけ: Copilot に作らせてみた だいたい https:/ /github.com/tilo/gem_with_c_extension と近い内容のも のが出てきたが、Mac でビルドできなかった。shared object
( .so ) ファ イルが作られなかった 19
ext/my_c_extension/extconf.rb require 'mkmf' create_makefile('my_c_extension/my_c_extension') 20
ext/my_c_extension/my_c_extension.c #include "ruby.h" VALUE rb_mMyCExtension; VALUE hello_world(VALUE self) { return
rb_str_new_cstr("Hello, world!"); } void Init_my_c_extension() { rb_mMyCExtension = rb_define_module("MyCExtension"); rb_define_method(rb_mMyCExtension, "hello_world", hello_world, 0); } 21
gemspec Gem::Specification.new do |spec| # ... other configurations ... spec.extensions
= ['ext/my_c_extension/extconf.rb'] spec.files = Dir.chdir(File.expand_path(__dir__)) do `git ls-files -z`.split("\x0").select do |f| f.match(%r{^(ext|lib)/}) || f == 'my_c_extension.gemspec' end end # ... other configurations ... end 22
Ruby ラッパー require 'my_c_extension/my_c_extension' module MyCExtension def self.hello MyCExtension.hello_world end
end 23