Class: FFI::ConstGenerator

Inherits:
Object
  • Object
show all
Defined in:
lib/ffi/tools/const_generator.rb

Overview

ConstGenerator turns C constants into ruby values.

Examples:

a simple example for stdio

cg = FFI::ConstGenerator.new('stdio') do |gen|
  gen.const(:SEEK_SET)
  gen.const('SEEK_CUR')
  gen.const('seek_end')   # this constant does not exist
end            # #calculate called automatically at the end of the block

cg['SEEK_SET'] # => 0
cg['SEEK_CUR'] # => 1
cg['seek_end'] # => nil
cg.to_ruby     # => "SEEK_SET = 0\nSEEK_CUR = 1\n# seek_end not available"

Defined Under Namespace

Classes: Constant

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(prefix, options) ⇒ Object #initialize(prefix, options) {|gen| ... } ⇒ Object

Creates a new constant generator that uses prefix as a name, and an options hash.

The only option is :required, which if set to true raises an error if a constant you have requested was not found.

Overloads:

  • #initialize(prefix, options) {|gen| ... } ⇒ Object

    When passed a block, #calculate is automatically called at the end of the block, otherwise you must call it yourself.

    Yield Parameters:

Options Hash (options):

  • :required (Boolean)


38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/ffi/tools/const_generator.rb', line 38

def initialize(prefix = nil, options = {})
  @includes = ['stdio.h', 'stddef.h']
  @constants = {}
  @prefix = prefix

  @required = options[:required]
  @options = options

  if block_given? then
    yield self
    calculate self.class.options.merge(options)
  end
end

Instance Attribute Details

#constantsObject (readonly)

Returns the value of attribute constants



21
22
23
# File 'lib/ffi/tools/const_generator.rb', line 21

def constants
  @constants
end

Class Method Details

.optionsHash

Get class options.



60
61
62
# File 'lib/ffi/tools/const_generator.rb', line 60

def self.options
  @options
end

.options=(options) ⇒ Hash

Set class options These options are merged with #initialize options when it is called with a block.



55
56
57
# File 'lib/ffi/tools/const_generator.rb', line 55

def self.options=(options)
  @options = options
end

Instance Method Details

#[](name) ⇒ Object

Access a constant by name.



66
67
68
# File 'lib/ffi/tools/const_generator.rb', line 66

def [](name)
  @constants[name].converted_value
end

#calculate(options = {}) ⇒ nil

Calculate constants values.

Options Hash (options):

  • :cppflags (String)

    flags for C compiler

Raises:

  • if a constant is missing and :required was set to true (see #initialize)



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/ffi/tools/const_generator.rb', line 106

def calculate(options = {})
  binary = File.join Dir.tmpdir, "rb_const_gen_bin_#{Process.pid}"

  Tempfile.open("#{@prefix}.const_generator") do |f|
    @includes.each do |inc|
      f.puts "#include <#{inc}>"
    end
    f.puts "\nint main(int argc, char **argv)\n{"

    @constants.each_value do |const|
      f.puts <<-EOF
  #ifdef #{const.name}
  printf("#{const.name} #{const.format}\\n", #{const.cast}#{const.name});
  #endif
      EOF
    end

    f.puts "\n\treturn 0;\n}"
    f.flush

    output = `gcc #{options[:cppflags]} -D_DARWIN_USE_64_BIT_INODE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -x c -Wall -Werror #{f.path} -o #{binary} 2>&1`

    unless $?.success? then
      output = output.split("\n").map { |l| "\t#{l}" }.join "\n"
      raise "Compilation error generating constants #{@prefix}:\n#{output}"
    end
  end

  output = `#{binary}`
  File.unlink(binary + (FFI::Platform.windows? ? ".exe" : ""))
  output.each_line do |line|
    line =~ /^(\S+)\s(.*)$/
    const = @constants[$1]
    const.value = $2
  end

  missing_constants = @constants.select do |name, constant|
    constant.value.nil?
  end.map { |name,| name }

  if @required and not missing_constants.empty? then
    raise "Missing required constants for #{@prefix}: #{missing_constants.join ', '}"
  end
end

#const(name, format = nil, cast = '', ruby_name = nil, converter = nil) ⇒ Object #const(name, format = nil, cast = '', ruby_name = nil) {|value| ... } ⇒ Object

Request the value for C constant name.

Overloads:

  • #const(name, format = nil, cast = '', ruby_name = nil, converter = nil) ⇒ Object

    converter is a Method or a Proc.

  • #const(name, format = nil, cast = '', ruby_name = nil) {|value| ... } ⇒ Object

    Use a converter block. This block convert the value from a string to the appropriate type for #to_ruby.

    Yield Parameters:

    • value

      constant value



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/ffi/tools/const_generator.rb', line 85

def const(name, format = nil, cast = '', ruby_name = nil, converter = nil,
          &converter_proc)
  format ||= '%d'
  cast ||= ''

  if converter_proc and converter then
    raise ArgumentError, "Supply only converter or converter block"
  end

  converter = converter_proc if converter.nil?

  const = Constant.new name, format, cast, ruby_name, converter
  @constants[name.to_s] = const
  return const
end

#dump_constants(io) ⇒ nil

Dump constants to io.



154
155
156
157
158
159
# File 'lib/ffi/tools/const_generator.rb', line 154

def dump_constants(io)
  @constants.each do |name, constant|
    name = [@prefix, name].join '.' if @prefix
    io.puts "#{name} = #{constant.converted_value}"
  end
end

#include(*i) ⇒ Array<String>

Note:

stdio.h and stddef.h automatically included

Add additional C include file(s) to calculate constants from.



178
179
180
# File 'lib/ffi/tools/const_generator.rb', line 178

def include(*i)
  @includes |= i.flatten
end

#to_rubyString

Outputs values for discovered constants. If the constant's value was not discovered it is not omitted.



164
165
166
167
168
169
170
171
172
# File 'lib/ffi/tools/const_generator.rb', line 164

def to_ruby
  @constants.sort_by { |name,| name }.map do |name, constant|
    if constant.value.nil? then
      "# #{name} not available"
    else
      constant.to_ruby
    end
  end.join "\n"
end