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

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

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:

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

    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)


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

- (Object) constants (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

+ (Hash) options

Get class options.

Returns:

  • (Hash)

    class options



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

def self.options
  @options
end

+ (Hash) options=(options)

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

Parameters:

  • options (Hash)

Returns:

  • (Hash)

    class options



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

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

Instance Method Details

- (Object) [](name)

Access a constant by name.

Parameters:

  • name (String)

Returns:

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



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

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

- (nil) calculate(options = {})

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)



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

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

Request the value for C constant name.

Overloads:

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

    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.

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

    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



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

- (nil) dump_constants(io)

Dump constants to io.

Parameters:

  • io (#puts)

Returns:

  • (nil)


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

- (Array<String>) include(*i)

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



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

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

- (String) to_ruby

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

Returns:

  • (String)


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