Ruby-FFIで可変長配列(variable length array)を持つ構造体(struct)を使う。またはビットフィールド(bitfields)を使う方法。
公式のGithubのissueで相談したところ、開発者の方から方法を教えてもらったので記録として残します。
こんにちはRuby-FFI開発者!
素晴らしい仕事をありがとう。
質問があります。 可変長配列の構造をRubyコードに変換するにはどうすればよいですか?
typedef struct {
uint32_t capacity;
int32_t dp_score, dp_max, dp_max2;
uint32_t n_ambi:30, trans_strand:2;
uint32_t n_cigar;
uint32_t cigar[]; # Here
} mm_extra_t;
もう1つ質問があります。
ruby-ffi wikiを調べたところ、ビットフィールドはサポートされていないと書かれています。 これは今日でも当てはまりますか?
Lars Kanisさんによる回答
ビットフィールド(Bit fields)はサポートされていませんが、単純な整数演算でエミュレートできます。 可変長配列は
struct.pointers
で使用できます。 次のように使用します。
class MmExtra < FFI::Struct
layout capacity: :uint32,
dp_score: :int32,
dp_max: :int32,
dp_max2: :int32,
n_ambi_trans_strand: :uint32,
n_cigar: :uint32
end
n_ambi = 123456
trans_strand = 0x2
cigar = [4,5,6]
s = MmExtra.new(FFI::MemoryPointer.new(MmExtra.size + FFI.type_size(:uint32) * cigar.size))
s[:n_ambi_trans_strand] = n_ambi | (trans_strand << 30)
s[:n_cigar] = cigar.size
s.pointer.put_array_of_uint32(s.size, cigar)
p n_ambi: s[:n_ambi_trans_strand] & ((1 << 30) - 1), trans_strand: (s[:n_ambi_trans_strand] >> 30) & ((1 << 2) - 1) # => {:n_ambi=>123456, :trans_strand=>2}
p s[:n_cigar] # => 3
p s.pointer.get_array_of_uint32(s.size, 3) # => [4, 5, 6]
p s.pointer.read_bytes(s.pointer.size) # => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\xE2\x01\x80\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00"
MmExtraにカスタムメソッドを追加して、フィールドにアクセスし、ビットフィールド演算を実行することもできますよ。
ありがとうございます。