module Gogyou

gogyou は構造体や共用体、多次元配列 (もどき) を扱うためのライブラリです。

原始的な型情報は Gogyou::Primitives で定義してあり、struct や union メソッド内で利用できる型を次の表に示します:

型名

利用者定義の型情報

型情報を利用者が定義して利用することが出来ます。

型情報オブジェクトは、次のメソッドを必要とします:

利用者定義の型情報は、struct / union / typedef メソッドの引数として与えることが出来ます。

example (ruby.h から struct RBasic と struct RString を模倣した場合)

ポインタ型は実現できていないため、intptr_t で代用しています。

module MyRuby
  extend Gogyou

  typedef :uintptr_t, :VALUE

  RBasic = struct {
    VALUE :flags
    VALUE :klass
  }

  RString = struct {
    RBasic :basic
    union -> {
      struct -> {
        long :len
        intptr_t :ptr
        union -> {
          long :capa
          VALUE :shared
        }, :aux
      }, :heap
      char :ary, RSTRING_EMBED_LEN_MAX + 1
    }, :as
  }
end

“gogyou” の処理の分類とクラスの役割

Constants

Gogyou
VERSION

Public Class Methods

define_typeinfo(type, bytesize, bytealign, extensible, aref, aset) → type click to toggle source

“type“ に対して、型情報子とするための特異メソッドである “#bytesize“ “#bytealign“ “#extensible?“ “#aref“ “#aset“ を定義します。

“bytesize“ と “bytealign“ には整数値、文字列、nil を与えます。

“extensible“ には真偽値、文字列、nil を与えます。

“aref“ には、引数として “(buffer, offset)“ を受け取る Proc オブジェクト、文字列、nil を与えます。

“aset“ には、引数として “(buffer, offset, value)“ を受け取る Proc オブジェクト、文字列、nil を与えます。

これらの引数に文字列を与えた場合、メソッド定義コードとして直接埋め込まれます。

“bytesize“ と “bytealign“、“extensible“ の引数はありません。

“aref“ の文字列内部で利用できる引数は “buffer“ “offset“ です。

“aset“ の文字列内部で利用できる引数は “buffer“ “offset“ “value“ です。

また nil を与えた場合は、対応するメソッドの定義を省略します。

常に “type“ を返します。

# File lib/gogyou.rb, line 305
  def self.define_typeinfo(type, bytesize, bytealign, extensible, aref, aset)
    type.instance_eval do
      unless bytesize.nil?
        bytesize = bytesize.to_i unless bytesize.kind_of?(String)
        eval <<-EOM
          def bytesize
            #{bytesize}
          end
        EOM
      end

      unless bytealign.nil?
        bytealign = bytealign.to_i unless bytealign.kind_of?(String)
        eval <<-EOM
          def bytealign
            #{bytealign}
          end
        EOM
      end

      unless extensible.nil?
        extensible = (!!extensible).inspect unless extensible.kind_of?(String)
        eval <<-EOM
          def extensible?
            #{extensible}
          end
        EOM
      end

      unless aref.nil?
        if aref.kind_of?(String)
          eval <<-EOM
            def aref(buffer, offset)
              #{aref}
            end
          EOM
        else
          define_singleton_method(:aref, aref)
        end
      end

      unless aset.nil?
        if aset.kind_of?(String)
          eval <<-EOM
            def aset(buffer, offset, value)
              #{aset}
            end
          EOM
        else
          define_singleton_method(:aset, aset)
        end
      end
    end

    type
  end
struct { ... } → accessor class click to toggle source

構造体を定義します。モジュールやクラス内で extend Gogyou しない(したくない)場合に利用することが出来ます。

example

class MyClass
  Type1 = Gogyou.struct {
    ...
  }
end
# File lib/gogyou.rb, line 246
def self.struct(&block)
  Model.struct(Model::TYPEMAP.dup, &block).create_accessor
end
typeinfo(typename) → typeinfo click to toggle source
typeinfo(typeobj) → typeinfo

型名に対する型情報子を取得します。

型情報子を渡した場合は、それをそのまま返り値とします。

型名が存在しないか、型情報子でない場合は nil を返します。

# File lib/gogyou.rb, line 265
def self.typeinfo(type)
  case type
  when Symbol, String
    return nil unless type =~ /\A[_A-Za-z][_0-9A-Za-z]*\Z/
    Model::TYPEMAP[type.intern]
  else
    if Model.check_typeinfo(type)
      type
    else
      nil
    end
  end
end
union(&block) click to toggle source
# File lib/gogyou.rb, line 250
def self.union(&block)
  Model.union(Model::TYPEMAP.dup, &block).create_accessor
end

Public Instance Methods

struct(&block) click to toggle source

構造体 (もどき) を定義します。

入れ子の構造体や共用体を定義するのはもちろん、無名構造体に無名共用体、多次元配列を定義することが出来ます。

extend Gogyou したモジュール・クラス内で定義された構造体(もどき)のクラスは自動的に型情報を取り込みます。 サンプルコードの MyType3 の定義する際に使われる MyType1 と MyType2 に注目して下さい。

example

class MyClass
  extend Gogyou

  MyType1 = struct {        # struct MyType1 {
    uint32_t :a             #   uint32_t a;
    uint32_t :b             #   uint32_t b;
    uint32_t :c, 8, 4       #   uint32_t c[8][4];
  }                         # };

  MyType2 = struct {        # struct MyType2 {
    float :a, :b, :c, 8, 4  #   float a, b, c[8][4];
  }                         # };

  MyType3 = union {         # union MyType3 {
    MyType1 :a              #   MyType1 a;
    MyType2 :b              #   MyType2 b;
  }                         # };
end

t1 = MyClass::MyType1.new
t2 = MyClass::MyType2.bind(String.alloc(MyClass::MyType2::BYTESIZE))
t3 = MyClass::MyType3.bind(File.read("sample.bin", MyClass::MyType3::BYTESIZE, mode: "rb"))
# File lib/gogyou.rb, line 395
def struct(&block)
  Model.struct(update_typemap__GOGYOU__, &block).create_accessor
end
typedef type, aliasname → self click to toggle source
typedef type, aliasname, *elements → self

***limitation***: not usable the pointer.

type

This parameter can given a symbol or an object.

シンボル (または文字列) を与える場合、すでに定義されている型名である必要があります。

クラスオブジェクト (またはモジュールオブジェクト) を与える場合、‘.aset` と `.aref` `.bytesize` `.bytealign` メソッドを持つ必要があります。

aliasname

定義する型名としてのシンボル (または文字列) を与えます。

elements

配列型の要素数を与えます。要素数は複数をとることが出来、最後の要素数として ‘0` を与えると任意個の要素数として定義されます。

# File lib/gogyou.rb, line 448
def typedef(type, aliasname, *elements)
  Model.typedef(update_typemap__GOGYOU__, type, aliasname, *elements)
end
typeinfo(typename) → typeinfo click to toggle source
typeinfo(typeobj) → typeinfo

型名に対する型情報子を取得します。

型情報子を渡した場合は、それをそのまま返り値とします。

型名が存在しないか、型情報子でない場合は nil を返します。

# File lib/gogyou.rb, line 414
def typeinfo(type)
  case type
  when Symbol, String
    return nil unless type =~ /\A[_A-Za-z][_0-9A-Za-z]*\Z/
    update_typemap__GOGYOU__[type.intern]
  else
    if Model.check_typeinfo(type)
      type
    else
      nil
    end
  end
end
union(&block) click to toggle source
# File lib/gogyou.rb, line 399
def union(&block)
  Model.union(update_typemap__GOGYOU__, &block).create_accessor
end

Private Instance Methods

update_typemap__GOGYOU__(force = false) click to toggle source
# File lib/gogyou.rb, line 453
def update_typemap__GOGYOU__(force = false)
  typemap = @typemap__GOGYOU__ ||= Model::TYPEMAP.dup
  constants.each do |n|
    obj = const_get(n)
    next unless Model.check_typeinfo(obj)
    if force
      typemap[n] = obj
    else
      typemap[n] ||= obj
    end
  end
  typemap
end