本篇聚焦「讀者如何把 C 核心以 Ruby C 擴充橋接進專案」。筆者以 Mongory 的 ext 實作為範本:extconf.rb 編譯旗標、子模組整合、Ruby 值轉 C 值的 shallow/deep/recover、記憶體池與 GC 協作、例外傳遞語意,以及「不可以讓他 segfault!」的防線。文內所有做法均可抽離 Mongory,遷移到讀者的任意 C×Ruby 專案。
——
——
目標:用 Ruby 的 mkmf
直接把 C 核心的 .c
檔納入擴充模組,產生 mongory_ext
(讀者可替換名稱)。
關鍵做法(抽象化自 Mongory):
Bundler
/mkmf
產生 Makefile。core/include
加入 $INCFLAGS
,並蒐集 core/src/**.c
作為 $srcs
。-std=c99 -Wall -Wextra
,針對舊工具鏈額外關閉噪音警告。-Wl,-undefined,dynamic_lookup
,避免強鏈接 libruby
(由載入器解符號)。這些要點在 ext/mongory_ext/extconf.rb
皆可見,讀者可 1:1 改名複製。
——
何時用 submodule:
何時直接內嵌原始碼:
Mongory 的做法:Ruby 端直接編譯 mongory-core
的 C 檔案,不需要獨立建立靜態庫,也不引入 core 的 CMake 測試依賴,簡化安裝步驟。
——
設計目標:
origin
回指實體,避免不必要的重新分配。抽象化自 mongory_ext.c
的關鍵要點:
rb_to_mongory_value_primitive
:直譯數值/字串/布林/正則/符號。rb_to_mongory_value_shallow
:Hash/Array 以 wrapper 結構包住,get
時再呼叫 Ruby API。rb_to_mongory_value_deep
:條件遞迴轉換為 C 結構,並在 key 路徑上建立 string_map/symbol_map
快取以減少 Ruby 物件 churn。mongory_value_to_ruby
:以 value->origin
回傳原生 Ruby 值。——
Mongory 的 C 側以 mongory_memory_pool
管理生命週期,Ruby 側透過 mark_list
配合 GC:
origin
:所有 C 包裝值指回對應的 Ruby VALUE。mark_list
:在 Ruby 的 mark 階段逐一 rb_gc_mark
,確保外部物件壽命足夠。scratch_pool->reset
:每次 match?
後重置,避免重複配置。pool->free
:在 wrapper 釋放時統一回收(含 trace_pool)。這使 C 程式碼能像高階語言一樣「一次配置、多次使用、集中釋放」。
——
原則:所有錯誤都收斂到 Ruby 例外,避免讓 C 發生未定義行為。
做法:
pool->error
作為錯誤匯流,統一在邊界檢查並 rb_raise
(見 rb_mongory_error_handling
)。Regexp#match?
、Matchers.lookup/new/match?
),錯誤自然以 Ruby 例外呈現。rb_protect
或等效保護策略;Mongory 當前實作以簡潔性為先,由呼叫端確保輸入正確。——
最小流程:
bundle install
bundle exec rake compile
或使用專案腳本(若有):
scripts/build_with_core.sh
驗證是否載入 C 擴充:
require 'mongory'
puts defined?(Mongory::CMatcher) ? 'C extension available' : 'pure Ruby path'
——
骨架:
ext/your_ext/your_ext.c
:Ruby 綁定與轉換實作。ext/your_ext/extconf.rb
:收集 core 原始碼、設定旗標、處理 macOS dynamic_lookup。ext/your_ext/core/**
:C 核心(submodule 或直接複製)。檢查清單:
include
與 src
加入編譯。-Wl,-undefined,dynamic_lookup
。$srcs/$objs
,並為外部來源附加明確規則。mongory_value
設定 origin
(必要時)。mark_list
確保 GC 可達,reset/free
節點明確。——
若核心將跨多語言重用,建議:
——
結語:C → Ruby bridge 的本質是「把語意邊界穩穩立起來」。以 Mongory 的實作為範本,讀者能在自家專案中搭出可維護、可觀測、可擴充、且不會炸掉整個進程的 C 擴充橋接。