⛳
[Ruby] Fiddle: 構造体(Fiddle::CStruct)を解放する
RubyからCのライブラリを呼び出すのに使うfiddleについての記事です。引数に構造体ポインタを使う際の手順を残しておきます。
あまり文献が見当たらず、正しいかどうかよくわかりませんとお断りしておきます。
例題: time関数
time
は、Unix時間(エポック秒)を得る、Cの標準ライブラリの関数です。
#include <time.h>
time_t time( time_t *t );
ここでtime_t
は、近年では一般にlong
(8バイト符号付き整数)の別名であり、構造体というには大げさですが、ほかに引数のポインタに結果を入れてくれる類のお手軽そうな関数が思いつかなかったので time_t
を構造体と思って例題にします。[1]
fiddle/import
fiddle/importで、timeをRubyから呼び出すコードを書いてみます。time_t
は、longの値1個を持つ構造体として定義しました。
試した環境はRubyのDockerコンテナです (
ruby:3.1-slim
)
require 'fiddle/import'
module LibC
extend Fiddle::Importer
dlload 'libc.so.6'
typealias "time_t", "long"
extern 'time_t time(time_t *t)'
Time_t = struct(['long value'])
end
time_ptr = LibC::Time_t.malloc
LibC.time(time_ptr)
p time_ptr.value # time()によるエポック秒
p Time.now.to_i # Rubyで答え合わせ
結果の例です。
1642258051
1642258051
freeする
以上で動きましたが、LibC::Time_t.malloc
したままfreeしていないのが何とも落ち着きません。Fiddle::CStruct#malloc
した領域をfree
する文献にいまいち巡り合えなかったですが、以下のようにするのがよさそうな気配です。
require 'fiddle/import'
module LibC
extend Fiddle::Importer
dlload 'libc.so.6'
typealias "time_t", "long"
extern 'time_t time(time_t *t)'
Time_t = struct(['long value'])
end
time_ptr = nil
begin
time_ptr = LibC::Time_t.malloc
LibC.time(time_ptr)
p time_ptr.value
ensure
Fiddle.free(time_ptr.to_i) if time_ptr
end
mallocを明示的に呼んだならばfree、そうでないならば要らなそうです。Rubyオブジェクトが先にあってそのポインタを得るようなケース (例: Fiddle::Pointer[str]
, Fiddle::Closure::BlockCaller
等) でfreeするとセグフォします。
-
time()
を使う際は一般に戻り値を使うことが多そうですが、今回は引数のポインタ渡しについて焦点を当てます。 ↩︎
Discussion