Generates an FFI Struct layout.
Given the @@@ portion in:
module Zlib::ZStream < FFI::Struct
@@@
name "struct z_stream_s"
include "zlib.h"
field :next_in, :pointer
field :avail_in, :uint
field :total_in, :ulong
# ...
@@@
end
StructGenerator will create the layout:
layout :next_in, :pointer, 0,
:avail_in, :uint, 4,
:total_in, :ulong, 8,
# ...
StructGenerator does its best to pad the layout it produces to preserve line numbers. Place the struct definition as close to the top of the file for best results.
# File lib/ffi/tools/struct_generator.rb, line 39
39: def initialize(name, options = {})
40: @name = name
41: @struct_name = nil
42: @includes = []
43: @fields = []
44: @found = false
45: @size = nil
46:
47: if block_given? then
48: yield self
49: calculate self.class.options.merge(options)
50: end
51: end
# File lib/ffi/tools/struct_generator.rb, line 58
58: def calculate(options = {})
59: binary = File.join Dir.tmpdir, "rb_struct_gen_bin_#{Process.pid}"
60:
61: raise "struct name not set" if @struct_name.nil?
62:
63: Tempfile.open("#{@name}.struct_generator") do |f|
64: f.puts "#include <stdio.h>"
65:
66: @includes.each do |inc|
67: f.puts "#include <#{inc}>"
68: end
69:
70: f.puts "#include <stddef.h>\n\n"
71: f.puts "int main(int argc, char **argv)\n{"
72: f.puts " #{@struct_name} s;"
73: f.puts %[ printf("sizeof(#{@struct_name}) %u\\n", (unsigned int) sizeof(#{@struct_name}));]
74:
75: @fields.each do |field|
76: f.puts printf("#{field.name} %u %u\\n", (unsigned int) offsetof(#{@struct_name}, #{field.name}), (unsigned int) sizeof(s.#{field.name}));
77: end
78:
79: f.puts "\n return 0;\n}"
80: f.flush
81:
82: output = `gcc #{options[:cppflags]} #{options[:cflags]} -D_DARWIN_USE_64_BIT_INODE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -x c -Wall -Werror #{f.path} -o #{binary} 2>&1`
83:
84: unless $?.success? then
85: @found = false
86: output = output.split("\n").map { |l| "\t#{l}" }.join "\n"
87: raise "Compilation error generating struct #{@name} (#{@struct_name}):\n#{output}"
88: end
89: end
90:
91: output = `#{binary}`.split "\n"
92: File.unlink(binary + (FFI::Platform.windows? ? ".exe" : ""))
93: sizeof = output.shift
94: unless @size
95: m = /\s*sizeof\([^)]+\) (\d+)/.match sizeof
96: @size = m[1]
97: end
98:
99: line_no = 0
100: output.each do |line|
101: md = line.match(/.+ (\d+) (\d+)/)
102: @fields[line_no].offset = md[1].to_i
103: @fields[line_no].size = md[2].to_i
104:
105: line_no += 1
106: end
107:
108: @found = true
109: end
# File lib/ffi/tools/struct_generator.rb, line 124
124: def dump_config(io)
125: io.puts "rbx.platform.#{@name}.sizeof = #{@size}"
126:
127: @fields.each { |field| io.puts field.to_config(@name) }
128: end
# File lib/ffi/tools/struct_generator.rb, line 114
114: def field(name, type=nil)
115: field = Field.new(name, type)
116: @fields << field
117: return field
118: end
# File lib/ffi/tools/struct_generator.rb, line 120
120: def found?
121: @found
122: end
# File lib/ffi/tools/struct_generator.rb, line 130
130: def generate_layout
131: buf = ""
132:
133: @fields.each_with_index do |field, i|
134: if buf.empty?
135: buf << "layout :#{field.name}, :#{field.type}, #{field.offset}"
136: else
137: buf << " :#{field.name}, :#{field.type}, #{field.offset}"
138: end
139:
140: if i < @fields.length - 1
141: buf << ",\n"
142: end
143: end
144:
145: buf
146: end
# File lib/ffi/tools/struct_generator.rb, line 148
148: def get_field(name)
149: @fields.find { |f| name == f.name }
150: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.