class FFI::ConstGenerator

ConstGenerator turns C constants into ruby values.

@example 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"

Attributes

constants[R]

Public Class Methods

new(prefix = nil, options = {}) { |self| ... } click to toggle source

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.

@param [#to_s] prefix @param [Hash] options @return @option options [Boolean] :required @overload initialize(prefix, options) @overload initialize(prefix, options) { |gen| … }

@yieldparam [ConstGenerator] gen new generator is passed to the block
When passed a block, {#calculate} is automatically called at the end of
the block, otherwise you must call it yourself.
# 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
options() click to toggle source

Get class options. @return [Hash] class options

# File lib/ffi/tools/const_generator.rb, line 60
def self.options
  @options
end
options=(options) click to toggle source

Set class options These options are merged with {#initialize} options when it is called with a block. @param [Hash] options @return [Hash] class options

# File lib/ffi/tools/const_generator.rb, line 55
def self.options=(options)
  @options = options
end

Public Instance Methods

[](name) click to toggle source

@param [String] name @return constant value (converted if a converter was defined). Access a constant by name.

# File lib/ffi/tools/const_generator.rb, line 66
def [](name)
  @constants[name].converted_value
end
calculate(options = {}) click to toggle source

Calculate constants values. @param [Hash] options @option options [String] :cppflags flags for C compiler @return [nil] @raise if a constant is missing and :required was set to true (see {#initialize})

# 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 = %xgcc #{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 = %x#{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, &converter_proc) click to toggle source

Request the value for C constant name.

@param [#to_s] name C constant name @param [String] format a printf format string to print the value out @param [String] cast a C cast for the value @param ruby_name alternate ruby name for {#to_ruby}

@overload const(name, format=nil, cast='', ruby_name=nil, converter=nil)

+converter+ is a Method or a Proc.
@param [#call] converter convert the value from a string to the appropriate
 type for {#to_ruby}.

@overload 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}.
@yieldparam value constant value
# 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) click to toggle source

Dump constants to io. @param [#puts] io @return [nil]

# 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) click to toggle source

Add additional C include file(s) to calculate constants from. @note stdio.h and stddef.h automatically included @param [List<String>, Array<String>] i include file(s) @return [Array<String>] array of include files

# File lib/ffi/tools/const_generator.rb, line 178
def include(*i)
  @includes |= i.flatten
end
to_ruby() click to toggle source

Outputs values for discovered constants. If the constant's value was not discovered it is not omitted. @return [String]

# 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