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

require 'ffi/tools/const_generator'
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:

Parameters:

  • prefix (#to_s) (defaults to: nil)
  • options (Hash) (defaults to: {})

Options Hash (options):

  • :required (Boolean)

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

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


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

def constants
  @constants
end

Class Method Details

.optionsHash

Get class options.

Returns:

  • (Hash)

    class options


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

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.

Parameters:

  • options (Hash)

Returns:

  • (Hash)

    class options


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

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

Instance Method Details

#[](name) ⇒ Object

Access a constant by name.

Parameters:

  • name (String)

Returns:

  • constant value (converted if a converter was defined).


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

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

#calculate(options = {}) ⇒ nil

Calculate constants values.

Parameters:

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :cppflags (String)

    flags for C compiler

Returns:

  • (nil)

Raises:

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


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
150
# File 'lib/ffi/tools/const_generator.rb', line 107

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.

    Parameters:

    • converter (#call) (defaults to: nil)

      convert the value from a string to the appropriate type for #to_ruby.

  • #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

Parameters:

  • name (#to_s)

    C constant name

  • format (String) (defaults to: nil)

    a printf format string to print the value out

  • cast (String) (defaults to: '')

    a C cast for the value

  • ruby_name (defaults to: nil)

    alternate ruby name for #to_ruby


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

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.

Parameters:

  • io (#puts)

Returns:

  • (nil)

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

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.

Parameters:

  • i (List<String>, Array<String>)

    include file(s)

Returns:

  • (Array<String>)

    array of include files


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

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.

Returns:

  • (String)

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

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