ffi_dry
文件大小: unknow
源码售价: 5 个金币 积分规则     积分充值
资源说明:Sugar and helpers for Ruby-FFI
= ffi_dry

Helpers, sugar methods, and new features over Ruby FFI to do some common 
things and add support for some uncommon ones.

== Requirements
 
* ffi (>= 0.5.0) - github.com/ffi/ffi


== Synopsis

(samples/ in the package for code)

One major feature is a DSL"-like" syntax for declaring structure members
in FFI::Struct or FFI::ManagedStruct definitions.
    
    require 'rubygems'
    require 'ffi'
    require 'ffi/dry'

    class SomeStruct < FFI::Struct
      include FFI::DRY::StructHelper

      # we get a new way of specifying layouts with a 'dsl'-like syntax
      # The hash containing {:desc => ... } can contain arbitrary keys which 
      # can be used however we like. dsl_metadata will contain all these
      # in the class and instance.
      dsl_layout do
        field   :field1,  :uint16, :desc => 'this is field 1'
        field   :field2,  :uint16, :desc => 'this is field 2'
      end
    end

    ss0=SomeStruct.new

With the declarations above, we specified :desc hash value in metadata. Extra
metadata can have arbitrary keys and is accessible in every instance and class.

    pp ss0.dsl_metadata 
    [{:type=>:uint16, :name=>:field1, :desc=>"this is field 1"},
     {:type=>:uint16, :name=>:field2, :desc=>"this is field 2"}]
    # => nil

    pp SomeStruct.dsl_metadata
    #...

We get some additional ways of instantiating and declaring values for free 
during initialization. (The FFI standard ways still work too)

    raw_data = "\x00\x00\xff\xff"

    ss1=SomeStruct.new :raw => raw_data
    ss2=SomeStruct.new :raw => raw_data, :field1 => 1, :field2 => 2
    ss3=SomeStruct.new {|x| x.field1=1 }
    ss4=SomeStruct.new(:raw => raw_data) {|x| x.field1=1 }

    [ ss0, ss1, ss2, ss3, ss4 
    ].each_with_index {|x,i| p ["ss#{i}",[x.field1, x.field2]]}

    # which produces...
    # ["ss0", [0, 0]]
    # ["ss1", [0, 65535]]
    # ["ss2", [1, 2]]
    # ["ss3", [1, 0]]
    # ["ss4", [1, 65535]]


Here's a broader example which utilizes that arbitrary ':desc' parameter in a 
"neighborly" way. This also demonstrates using superclasses to add common 
struct features, declaring array fields, as well as nesting other structs.

    require 'rubygems'
    require 'ffi'
    require 'ffi/dry'

    class NeighborlyStruct < ::FFI::Struct
      include ::FFI::DRY::StructHelper

      def self.describe
        print "Struct: #{self.name}"
        dsl_metadata().each_with_index do |spec, i|
          print "  Field #{i}\n"
          print "    name:  #{spec[:name].inspect}\n"
          print "    type:  #{spec[:type].inspect}\n"
          print "    desc:  #{spec[:desc]}\n\n"
        end
        print "\n"
      end
      def describe;  self.class.describe;  end
    end

    class TestStruct < NeighborlyStruct
      dsl_layout do
        field   :field1,  :uint8,  :desc => "test field 1"
        field   :field2,  :uint8,  :desc => "test field 2"
      end
    end

    class SomeStruct < NeighborlyStruct
      dsl_layout do
        field  :kind, :uint8,      :desc => "a type identifier"
        struct :tst,  TestStruct,  :desc => "a nested TestStruct"
        field  :len,  :uint8,      :desc => "8-bit size value (>= self.size+2)"
        array  :str,  [:char,255], 
            :desc => "a string up to 255 bytes bound by :len"
      end

      # override kind getter method with our own
      # resolves kind to some kind of type array for example...
      def kind
        [:default, :bar, :baz][ self[:kind] ]
      end
    end

    s1=TestStruct.new
    s2=SomeStruct.new

    # check out that 'kind' override:
    s2.kind
    # => :default

    # oh and the regular FFI way is always intact
    s2[:kind]
    # => 0

    s2[:kind]=1
    s2.kind
    # => :bar

    s2.kind=3
    s2.kind
    # => :baz

    puts "*"*70
    s1.describe
    ## we get a dump of metadata
    # **********************************************************************
    # Struct: TestStruct  
    # Field 0
    #   name:  :field1
    #   type:  :uint8
    #   desc:  test field 1
    #
    # Field 1
    #   name:  :field2
    #   type:  :uint8
    #   desc:  test field 2

    puts "*"*70
    s2.describe
    ## we get a dump of metadata
    # Struct: SomeStruct  Field 0
    #     name:  :kind
    #     type:  :uint8
    #     desc:  a type identifier
    #
    #   Field 1
    #     name:  :tst
    #     type:  TestStruct
    #     desc:  a nested TestStruct
    #
    #   Field 2
    #     name:  :len
    #     type:  :uint8
    #     desc:  8-bit size value (>= self.size+2)
    #
    #   Field 3
    #     name:  :str
    #     type:  [:char, 255]
    #     desc:  a string up to 255 bytes bound by :len

    puts "*"*70
    s2.tst.describe
    ## same as s1.describe
    # **********************************************************************
    # Struct: TestStruct  
    # Field 0
    #   name:  :field1
    #   type:  :uint8
    #   desc:  test field 1
    #
    # Field 1
    #   name:  :field2
    #   type:  :uint8
    #   desc:  test field 2

There's also some helper modules for collecting lookup maps for constants, a 
common and handy thing when porting various libraries. We use the Ruby Socket 
socket namespace here for demonstration  purposes. You can 'slurp' constants 
from any namespace this way.

    require 'ffi/dry'
    require 'socket'

    module AddressFamily
      include FFI::DRY::ConstMap
      slurp_constants ::Socket, "AF_"
      def list ; @@list ||= super() ; end  # only generate the hash once
    end

AddressFamily now has all the constants it found for Socket::AF_* minus the
prefix.

   AddressFamily::INET
   AddressFamily::LINK
   AddressFamily::INET6

etc...

We can do type or value lookups using []

    AddressFamily[2]      # => "INET"
    AddressFamily["INET"] # => 2
    
We can get a hash of all constant->value pairs with .list

    AddressFamily.list
    # => {"NATM"=>31, "DLI"=>13, "UNIX"=>1, "NETBIOS"=>33,  ...}

... and invert for a reverse mapping

    AddressFamily.list.invert
    # => {16=>"APPLETALK", 5=>"CHAOS", 27=>"NDRV", 0=>"UNSPEC", ...}


== License

Copyright (c) 2009 Eric Monti. See LICENSE for details.

本源码包内暂不包含可直接显示的源代码文件,请下载源码包。