123456789_123456789_123456789_123456789_123456789_

Class: FFI::ConstGenerator

Relationships & Source Files
Namespace Children
Classes:
Inherits: Object
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"

Class Attribute Summary

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(prefix, options) ⇒ Object .new(prefix, options) { |gen| ... }) {|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.

Parameters:

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

Options Hash (options):

  • :required (Boolean)
[ GitHub ]

  
# 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

Class Attribute Details

.optionsHash (rw)

Get class options.

Returns:

  • (Hash)

    class options

[ GitHub ]

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

def self.options
  @options
end

.options=(options) ⇒ Hash (rw)

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

Parameters:

  • options (Hash)

Returns:

  • (Hash)

    class options

[ GitHub ]

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

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

Instance Attribute Details

#constants (readonly)

[ GitHub ]

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

attr_reader :constants

Instance Method Details

#[](name) ⇒ Object

Access a constant by name.

Parameters:

  • name (String)

Returns:

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

[ GitHub ]

  
# 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

Raises:

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

[ GitHub ]

  
# File 'lib/ffi/tools/const_generator.rb', line 107

def calculate(options = {})
  binary_path = nil

  Tempfile.open("#{@prefix}.const_generator") do |f|
    binary_path = f.path + ".bin"
    @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

    cc = ENV['CC'] || 'gcc'
    output = `#{cc} #{options[:cppflags]} -D_DARWIN_USE_64_BIT_INODE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -x c -Wall -Werror #{f.path} -o #{binary_path} 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_path}`
  File.unlink(binary_path + (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) #const(name, format=nil, cast='', ruby_name=nil) { |value| ... }) {|value| ... }

Request the value for C constant name.

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

[ GitHub ]

  
# 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)
[ GitHub ]

  
# File 'lib/ffi/tools/const_generator.rb', line 157

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

[ GitHub ]

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

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.

[ GitHub ]

  
# File 'lib/ffi/tools/const_generator.rb', line 167

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