Module: FFI::DRY::StructHelper
- Defined in:
- lib/ffi/dry.rb
Overview
A module to add syntactic sugar and some nice automatic getter/setter logic to FFI::Struct, FFI::ManagedStruct, etc.
For example:
require 'rubygems'
require 'ffi'
require 'ffi/dry'
require 'pp'
class SomeStruct < FFI::Struct
include FFI::DRY::StructHelper
# we get a new way of specifying layouts with a 'dsl'-like syntax
dsl_layout do
field :field1, :uint16, :desc => 'this is field 1'
field :field2, :uint16, :desc => 'this is field 2'
end
end
ss0=SomeStruct.new
pp ss0. # we can look at definition metadata
# produces...
# [{:type=>:uint16, :name=>:field1, :desc=>"this is field 1"},
# {:type=>:uint16, :name=>:field2, :desc=>"this is field 2"}]
# And we have additional ways of instantiating and declaring values
# 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| pp ["ss#{i}",[x.field1, x.field2]]}
# produces...
# ["ss0", [0, 0]]
# ["ss1", [0, 65535]]
# ["ss2", [1, 2]]
# ["ss3", [1, 0]]
# ["ss4", [1, 65535]]
Defined Under Namespace
Modules: ClassMethods
Instance Attribute Summary collapse
-
#dsl_metadata ⇒ Object
readonly
< ::FFI::Struct.
Class Method Summary collapse
Instance Method Summary collapse
-
#copy(grown = 0) ⇒ Object
Returns a new instance of self.class containing a seperately allocated copy of all our data.
-
#initialize(*args) {|_self| ... } ⇒ Object
Adds field setting on initialization to ::FFI::Struct.new as well as a “yield(self) if block_given?” at the end.
-
#ptr_to(field) ⇒ Object
Returns a pointer to the specified field, which is the name assigned to a member in the layout.
-
#set_fields(params = nil) ⇒ Object
Sets field values in the struct specified by their symbolic name from a hash of ‘:field => value’ pairs.
Instance Attribute Details
#dsl_metadata ⇒ Object (readonly)
< ::FFI::Struct
59 60 61 |
# File 'lib/ffi/dry.rb', line 59 def @dsl_metadata end |
Class Method Details
.included(base) ⇒ Object
181 182 183 |
# File 'lib/ffi/dry.rb', line 181 def self.included(base) base.extend(ClassMethods) end |
Instance Method Details
#copy(grown = 0) ⇒ Object
Returns a new instance of self.class containing a seperately allocated copy of all our data. This abstract method should usually be called with super() from overridden ‘copy’ implementations for structures containing pointers to other memory or variable length data at the end.
Note also that, by default, this implementation determine’s size automatically based on the structure size. This is comparable to sizeof(some_struct) in C. However, you can supply a ‘grown’ parameter which can be used to add to the size of the copied instance as it is allocated and copied.
129 130 131 |
# File 'lib/ffi/dry.rb', line 129 def copy(grown=0) self.class.new( :raw => self.to_ptr.read_string(self.size+grown) ) end |
#initialize(*args) {|_self| ... } ⇒ Object
Adds field setting on initialization to ::FFI::Struct.new as well as a “yield(self) if block_given?” at the end.
The field initialization kicks in if there is only one argument, and it is a Hash.
Note: The :raw parameter is a special tag in the hash. The value is taken as a string and initialized into a new FFI::MemoryPointer which this Struct then overlays.
If your struct layout has a field named :raw field, it won’t be assignable through the hash argument.
See also: set_fields() which is called automatically on the hash, minus the :raw tag.
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/ffi/dry.rb', line 78 def initialize(*args) @dsl_metadata = self.class. params=nil if args.size == 1 and (oparams=args[0]).is_a? Hash params = oparams.dup if raw=params.delete(:raw) super( ::FFI::MemoryPointer.from_string(raw) ) else super() end else super(*args) end set_fields(params) yield self if block_given? end |
#ptr_to(field) ⇒ Object
Returns a pointer to the specified field, which is the name assigned to a member in the layout.
135 136 137 138 |
# File 'lib/ffi/dry.rb', line 135 def ptr_to(field) x = self[field] # this is actually a test, to raise if missing return (self.to_ptr + self.offset_of(field)) end |
#set_fields(params = nil) ⇒ Object
Sets field values in the struct specified by their symbolic name from a hash of ‘:field => value’ pairs. Uses accessor field wrapper methods instead of a direct reference to the field (as in “obj.field1 = x”, not “obj = x”). The difference is subtle, but this allows you to take advantage of any wrapper methods you override when initializing a new object. The only caveat is that the wrapper method must be named the same as the field, and the field must be included in members() from the layout.
This method is called automatically if you are using the initialize() method provided in the DryStruct class and passing it a Hash as its only argument.
109 110 111 112 113 114 115 116 117 |
# File 'lib/ffi/dry.rb', line 109 def set_fields(params=nil) (params || {}).keys.each do |p| if members().include?(p) self.__send__(:"#{p}=", params[p]) else raise(::ArgumentError, "#{self.class} does not have a '#{p}' field") end end end |