Class: Inline::C

### Home Classes Methods

### In Files

- lib/inline.rb

### Parent

Object

### Methods

- ::new
- #accessor
- #add_compile_flags
- #add_id
- #add_link_flags
- #add_static
- #add_to_init
- #add_type_converter
- #alias_type_converter
- #build
- #c
- #c2ruby
- #c_raw
- #c_raw_singleton
- #c_singleton
- #crap_for_windoze
- #generate
- #generate_ext
- #include
- #include_ruby_last
- #load
- #load_cache
- #map_c_const
- #map_ruby_const
- #module_name
- #parse_signature
- #prefix
- #reader
- #remove_type_converter
- #ruby2c
- #so_name
- #strip_comments
- #writer

### Included Modules

- ZenTestMapping

### Files

- History.txt
- Manifest.txt
- README.txt

### Class/Module Index

Quicksearch

- Inline
- Inline::C
- CompilationError
- Dir
- File
- Module

No matching classes.

# Inline::C

Inline::C is the default builder used and the only one provided by Inline. It can be used as a template to write builders for other languages. It understands type-conversions for the basic types and can be extended as needed using add_type_converter, alias_type_converter and remove_type_converter.

### Constants

MAGIC_ARITY
MAGIC_ARITY_THRESHOLD
TYPE_MAP

  Default C to ruby and ruby to C type map

### Attributes

flags[RW]
init_extra[RW]
libs[RW]
mod[RW]
pre[RW]
rb_file[R]
sig[RW]
src[RW]
struct_name[RW]

Sets the name of the C struct for generating accessors. Used with accessor, reader, writer.

### Public Class Methods

new(mod) click to toggle source

    # File lib/inline.rb, line 399
    def initialize(mod)
      raise ArgumentError, "Class/Module arg is required" unless Module === mod
      # new (but not on some 1.8s) -> inline -> real_caller|eval
      stack = caller
      meth = stack.shift until meth =~ /in .(inline|test_|setup)/ or stack.empty?
      raise "Couldn't discover caller" if stack.empty?
      real_caller = stack.first
      real_caller = stack[3] if real_caller =~ /\(eval\)/
      real_caller =~ /(.*):(\d+)/
      real_caller = $1
      @rb_file = File.expand_path real_caller

      @mod = mod
      @pre = []
      @src = []
      @inc = []
      @sig = {}
      @flags = []
      @libs = []
      @init_extra = []
      @include_ruby_first = true
      @inherited_methods = {}
      @struct_name = nil

      @type_map = TYPE_MAP.dup
    end

### Public Instance Methods

accessor(method, type, member = method) click to toggle source

Adds a reader and writer for a C struct member wrapped via Data_Wrap_Struct. `method` is the ruby name to give the accessor, `type` is the C type. Unless the C member name is overridden with `member`, the method name is used as the struct member.

    builder.struct_name = 'MyStruct'
    builder.accessor :title,        'char *'
    builder.accessor :stream_index, 'int',   :index

The latter accesses MyStruct->index via the stream_index method.

    # File lib/inline.rb, line 438
    def accessor(method, type, member = method)
      reader method, type, member
      writer method, type, member
    end

add_compile_flags(*flags) click to toggle source

Adds compiler options to the compiler command line. No preprocessing is done, so you must have all your dashes and everything.

    # File lib/inline.rb, line 668
    def add_compile_flags(*flags)
      @flags.push(*flags)
    end

add_id(name) click to toggle source

Registers a static id_name for the symbol :name.

    # File lib/inline.rb, line 675
    def add_id name
      self.add_static "id_#{name}", "rb_intern(\"#{name}\")"
    end

add_link_flags(*flags) click to toggle source

Adds linker flags to the link command line. No preprocessing is done, so you must have all your dashes and everything.

    # File lib/inline.rb, line 683
    def add_link_flags(*flags)
      @libs.push(*flags)
    end

add_static(name, init, type = "VALUE") click to toggle source

Create a static variable and initialize it to a value.

    # File lib/inline.rb, line 690
    def add_static name, init, type = "VALUE"
      prefix      "static #{type} #{name};"
      add_to_init "#{name} = #{init};"
    end

add_to_init(*src) click to toggle source

Adds custom content to the end of the init function.

    # File lib/inline.rb, line 698
    def add_to_init(*src)
      @init_extra.push(*src)
    end

add_type_converter(type, r2c, c2r) click to toggle source

Registers C type-casts `r2c` and `c2r` for `type`.

    # File lib/inline.rb, line 705
    def add_type_converter(type, r2c, c2r)
      warn "WAR\NING: overridding #{type} on #{caller[0]}" if @type_map.has_key? type
      @type_map[type] = [r2c, c2r]
    end

alias_type_converter(existing_type, alias_type) click to toggle source

Registers C type `alias_type` as an alias of `existing_type`

    # File lib/inline.rb, line 713
    def alias_type_converter(existing_type, alias_type)
      warn "WAR\NING: overridding #{type} on #{caller[0]}" if
        @type_map.has_key? alias_type

      @type_map[alias_type] = @type_map[existing_type]
    end

build() click to toggle source

Builds the source file, if needed, and attempts to compile it.

    # File lib/inline.rb, line 529
    def build
      so_name = self.so_name
      so_exists = File.file? so_name
      unless so_exists and File.mtime(rb_file) < File.mtime(so_name) then

        unless File.directory? Inline.directory then
          warn "NOTE: creating #{Inline.directory} for RubyInline" if $DEBUG
          FileUtils.mkdir_p Inline.directory, :mode => 0700
        end

        src_name = "#{Inline.directory}/#{module_name}.c"
        old_src_name = "#{src_name}.old"
        should_compare = File.write_with_backup(src_name) do |io|
          io.puts generate_ext
        end

        # recompile only if the files are different
        recompile = true
        if so_exists and should_compare and
            FileUtils.compare_file(old_src_name, src_name) then
          recompile = false

          # Updates the timestamps on all the generated/compiled files.
          # Prevents us from entering this conditional unless the source
          # file changes again.
          t = Time.now
          File.utime(t, t, src_name, old_src_name, so_name)
        end

        if recompile then

          hdrdir = %(srcdir includedir archdir rubyhdrdir).map { |name|
            RbConfig::CONFIG[name]
          }.find { |dir|
            dir and File.exist? File.join(dir, "ruby.h")
          } or abort "ERROR: Can't find header dir for ruby. Exiting..."

          flags = @flags.join(' ')
          libs  = @libs.join(' ')

          config_hdrdir = if RbConfig::CONFIG['rubyarchhdrdir'] then
                            "-I #{RbConfig::CONFIG['rubyarchhdrdir']}"
                          elsif RUBY_VERSION > '1.9' then
                            "-I #{File.join hdrdir, RbConfig::CONFIG['arch']}"
                          else
                            nil
                          end

          windoze = WINDOZE and RUBY_PLATFORM =~ /mswin/
          sane = ! windoze
          cmd = [ RbConfig::CONFIG['LDSHARED'],
                  flags,
                  (RbConfig::CONFIG['DLDFLAGS']         if sane),
                  (RbConfig::CONFIG['CCDLFLAGS']        if sane),
                  RbConfig::CONFIG['CFLAGS'],
                  (RbConfig::CONFIG['LDFLAGS']          if sane),
                  '-I', hdrdir,
                  config_hdrdir,
                  '-I', RbConfig::CONFIG['includedir'],
                  ("-L#{RbConfig::CONFIG['libdir']}"    if sane),
                  (['-o', so_name.inspect]              if sane),
                  File.expand_path(src_name).inspect,
                  libs,
                  crap_for_windoze,
                  (RbConfig::CONFIG['LDFLAGS']          if windoze),
                  (RbConfig::CONFIG['CCDLFLAGS']        if windoze),
                ].compact.join(' ')

          # odd compilation error on clang + freebsd 10. Ruby built w/ rbenv.
          cmd = cmd.gsub(/-Wl,-soname,\$@/, "-Wl,-soname,#{File.basename so_name}")

          # strip off some makefile macros for mingw 1.9
          cmd = cmd.gsub(/\$\(.*\)/, '') if RUBY_PLATFORM =~ /mingw/

          cmd += " 2> #{DEV_NULL}" if $TESTING and not $DEBUG

          warn "Building #{so_name} with '#{cmd}'" if $DEBUG

          result = if WINDOZE
                     Dir.chdir(Inline.directory) { `#{cmd}` }
                   else
                     `#{cmd}`
                   end

          warn "Output:\n#{result}" if $DEBUG

          if $? != 0 then
            bad_src_name = src_name + ".bad"
            File.rename src_name, bad_src_name
            raise CompilationError, "error executing #{cmd.inspect}: #{$?}\nRenamed #{src_name} to #{bad_src_name}"
          end

          # NOTE: manifest embedding is only required when using VC8 ruby
          # build or compiler.
          # Errors from this point should be ignored if RbConfig::CONFIG['arch']
          # (RUBY_PLATFORM) matches 'i386-mswin32_80'
          if WINDOZE and RUBY_PLATFORM =~ /_80$/ then
            Dir.chdir Inline.directory do
              cmd = "mt /manifest lib.so.manifest /outputresource:so.dll;#2"
              warn "Embedding manifest with '#{cmd}'" if $DEBUG
              result = `#{cmd}`
              warn "Output:\n#{result}" if $DEBUG
              if $? != 0 then
                raise CompilationError, "error executing #{cmd}: #{$?}"
              end
            end
          end

          warn "Built successfully" if $DEBUG
        end

      else
        warn "#{so_name} is up to date" if $DEBUG
      end # unless (file is out of date)
    end

c(src, options = {}) click to toggle source

Adds a C function to the source, including performing automatic type conversion to arguments and the return value. The Ruby method name can be overridden by providing method_name. Unknown type conversions can be extended by using `add_type_converter`.

    # File lib/inline.rb, line 783
    def c src, options = {}
      options = {
        :expand_types => true,
      }.merge options
      self.generate src, options
    end

c2ruby(type) click to toggle source

Converts C type `type` to a ruby type

    # File lib/inline.rb, line 498
    def c2ruby(type)
      raise ArgumentError, "Unknown type #{type.inspect}" unless @type_map.has_key? type
      @type_map[type].last
    end

c_raw(src, options = {}) click to toggle source

Adds a raw C function to the source. This version does not perform any type conversion and must conform to the ruby/C coding conventions. The Ruby method name can be overridden by providing method_name.

    # File lib/inline.rb, line 807
    def c_raw src, options = {}
      self.generate src, options
    end

c_raw_singleton(src, options = {}) click to toggle source

Same as `c_raw`, but adds a class function.

    # File lib/inline.rb, line 814
    def c_raw_singleton src, options = {}
      options = {
        :singleton => true,
      }.merge options
      self.generate src, options
    end

c_singleton(src, options = {}) click to toggle source

Same as `c`, but adds a class function.

    # File lib/inline.rb, line 793
    def c_singleton src, options = {}
      options = {
        :expand_types => true,
        :singleton    => true,
      }.merge options
      self.generate src, options
    end

crap_for_windoze() click to toggle source

Returns extra compilation flags for windoze platforms. Ugh.

    # File lib/inline.rb, line 648
    def crap_for_windoze
      # gawd windoze land sucks
      case RUBY_PLATFORM
      when /mswin32/ then
        " -link /OUT:\"#{self.so_name}\" /LIBPATH:\"#{RbConfig::CONFIG['libdir']}\" /DEFAULTLIB:\"#{RbConfig::CONFIG['LIBRUBY']}\" /INCREMENTAL:no /EXPORT:Init_#{module_name}"
      when /mingw32/ then
        c = RbConfig::CONFIG
        " -Wl,--enable-auto-import -L#{c['libdir']} -l#{c['RUBY_SO_NAME']} -o #{so_name.inspect}"
      when /i386-cygwin/ then
        ' -L/usr/local/lib -lruby.dll'
      else
        ''
      end
    end

generate(src, options={}) click to toggle source

    # File lib/inline.rb, line 239
    def generate(src, options={})
      options = {:expand_types=>options} unless Hash === options

      expand_types = options[:expand_types]
      singleton = options[:singleton]
      result = self.strip_comments(src)

      signature = parse_signature(src, !expand_types)
      function_name = signature['name']
      method_name = options[:method_name]
      method_name ||= test_to_normal function_name
      return_type = signature['return']
      arity = options[:arity] || signature['arity']

      raise ArgumentError, "too many arguments" if arity > MAGIC_ARITY_THRESHOLD

      if expand_types then
        prefix = "static VALUE #{function_name}("
        if arity <= MAGIC_ARITY then
          prefix += "int argc, VALUE *argv, VALUE self"
        else
          prefix += "VALUE self"
          prefix += signature['args'].map { |arg, type| ", VALUE _#{arg}"}.join
        end
        prefix += ") {\n"
        prefix += signature['args'].map { |arg, type|
          "  #{type} #{arg} = #{ruby2c(type)}(_#{arg});\n"
        }.join

        # replace the function signature (hopefully) with new sig (prefix)
        result.sub!(/[^;\/\"\>]+#{function_name}\s*\([^\{]+\{/, "\n" + prefix)
        result.sub!(/\A\n/, '') # strip off the \n in front in case we added it
        unless return_type == "void" then
          raise SyntaxError, "Couldn't find return statement for #{function_name}" unless
            result =~ /return/
          result.gsub!(/return\s+([^\;\}]+)/) do
            "return #{c2ruby(return_type)}(#{$1})"
          end
        else
          result.sub!(/\s*\}\s*\Z/, "\nreturn Qnil;\n}")
        end
      else
        prefix = "static #{return_type} #{function_name}("
        result.sub!(/[^;\/\"\>]+#{function_name}\s*\(/, prefix)
        result.sub!(/\A\n/, '') # strip off the \n in front in case we added it
      end

      delta = if result =~ /\A(static.*?\{)/ then
                $1.split(/\n/).size
              else
                msg = "WAR\NING: Can't find signature in #{result.inspect}\n"
                warn msg unless $TESTING
                0
              end

      file, line = $1, $2 if caller[1] =~ /(.*?):(\d+)/

      result = "# line #{line.to_i + delta} \"#{file}\"\n" + result unless
        $DEBUG and not $TESTING

      @src << result
      @sig[function_name] = [arity,singleton,method_name]

      return result if $TESTING
    end

generate_ext() click to toggle source

Builds a complete C extension suitable for writing to a file and compiling.

    # File lib/inline.rb, line 309
    def generate_ext
      ext = []

      if @include_ruby_first
        @inc.unshift "#include \"ruby.h\""
      else
        @inc.push "#include \"ruby.h\""
      end

      ext << @inc
      ext << nil
      unless @pre.empty? then
        ext << @pre.join("\n\n")
        ext << nil
      end
      ext << @src.join("\n\n")
      ext << nil
      ext << nil
      ext << "#ifdef __cplusplus"
      ext << "extern \"C\" {"
      ext << "#endif"
      ext << "  __declspec(dllexport)" if WINDOZE
      ext << "  void Init_#{module_name}() {"
      ext << "    VALUE c = rb_cObject;"

      # TODO: use rb_class2path
      # ext << "    VALUE c = rb_path2class(#{@mod.name.inspect});"
      ext << @mod.name.split("::").map { |n|
        "    c = rb_const_get(c, rb_intern(\"#{n}\"));"
      }.join("\n")

      ext << nil

      @sig.keys.sort.each do |name|
        method = ''
        arity, singleton, method_name = @sig[name]
        if singleton then
          if method_name == 'allocate' then
            raise "#{@mod}::allocate must have an arity of zero" if arity > 0
            ext << "    rb_define_alloc_func(c, (VALUE(*)(VALUE))#{name});"
            next
          end
          method << "    rb_define_singleton_method(c, \"#{method_name}\", "
        else
          method << "    rb_define_method(c, \"#{method_name}\", "
        end
        method << "(VALUE(*)(ANYARGS))#{name}, #{arity});"
        ext << method
      end

      ext << @init_extra.join("\n") unless @init_extra.empty?

      ext << nil
      ext << "  }"
      ext << "#ifdef __cplusplus"
      ext << "}"
      ext << "#endif"
      ext << nil

      ext.join "\n"
    end

include(header) click to toggle source

Adds an include to the top of the file. Don't forget to use quotes or angle brackets.

    # File lib/inline.rb, line 758
    def include(header)
      @inc << "#include #{header}"
    end

include_ruby_last() click to toggle source

Specifies that the the ruby.h header should be included after custom header(s) instead of before them.

    # File lib/inline.rb, line 766
    def include_ruby_last
      @include_ruby_first = false
    end

load() click to toggle source

Loads the generated code back into ruby

    # File lib/inline.rb, line 522
    def load
      require "#{so_name}" or raise LoadError, "require on #{so_name} failed"
    end

load_cache() click to toggle source

Attempts to load pre-generated code returning true if it succeeds.

    # File lib/inline.rb, line 506
    def load_cache
      begin
        file = File.join("inline", File.basename(so_name))
        if require file then
          dir = Inline.directory
          warn "WAR\NING: #{dir} exists but is not being used" if test dd, dir and $VERBOSE
          return true
        end
      rescue LoadError
      end
      return false
    end

map_c_const(names_and_types) click to toggle source

Maps a C constant to ruby. `names_and_types` is a hash that maps the name of the constant to its C type.

    builder.map_c_const :C_NAME => :int

If you wish to give the constant a different ruby name:

    builder.map_c_const :C_NAME => [:int, :RUBY_NAME]

    # File lib/inline.rb, line 747
    def map_c_const(names_and_types)
      names_and_types.each do |name, typ|
        typ, ruby_name = Array === typ ? typ : [typ, name]
        self.add_to_init "    rb_define_const(c, #{ruby_name.to_s.inspect}, #{c2ruby(typ.to_s)}(#{name}));"
      end
    end

map_ruby_const(*names) click to toggle source

Maps RubyConstants to cRubyConstants.

    # File lib/inline.rb, line 730
    def map_ruby_const(*names)
      names.each do |name|
        self.prefix "static VALUE c#{name};"
        self.add_to_init "    c#{name} = rb_const_get(c, rb_intern(#{name.to_s.inspect}));"
      end
    end

module_name() click to toggle source

    # File lib/inline.rb, line 371
    def module_name
      unless defined? @module_name then
        module_name = @mod.name.gsub('::','__')
        md5 = Digest::MD5.new
        @pre.each { |m| md5 << m.to_s }
        @sig.keys.sort_by { |x| x.to_s }.each { |m| md5 << m.to_s }
        @module_name = "Inline_#{module_name}_#{md5}"
      end
      @module_name
    end

parse_signature(src, raw=false) click to toggle source

    # File lib/inline.rb, line 196
    def parse_signature(src, raw=false)

      sig = self.strip_comments(src)
      # strip preprocessor directives
      sig.gsub!(/^\s*\#.*(\\\n.*)*/, '')
      # strip {}s
      sig.gsub!(/\{[^\}]*\}/, '{ }')
      # clean and collapse whitespace
      sig.gsub!(/\s+/, ' ')

      unless defined? @types then
        @types = 'void|' + @type_map.keys.map{|x| Regexp.escape(x)}.join('|')
      end

      if /(#{@types})\s*(\w+)\s*\(([^)]*)\)/ =~ sig then
        return_type, function_name, arg_string = $1, $2, $3
        args = []
        arg_string.split(',').each do |arg|

          # helps normalize into 'char * varname' form
          arg = arg.gsub(/\s*\*\s*/, ' * ').strip

          if /(((#{@types})\s*\*?)+)\s+(\w+)\s*$/ =~ arg then
            args.push([$4, $1])
          elsif arg != "void" then
            warn "WAR\NING: '#{arg}' not understood"
          end
        end

        arity = args.size
        arity = MAGIC_ARITY if raw

        return {
          'return' => return_type,
          'name'   => function_name,
          'args'   => args,
          'arity'  => arity
        }
      end

      raise SyntaxError, "Can't parse signature: #{sig}"
    end

prefix(code) click to toggle source

Adds any amount of text/code to the source

    # File lib/inline.rb, line 773
    def prefix(code)
      @pre << code
    end

reader(method, type, member = method) click to toggle source

Adds a reader for a C struct member wrapped via Data_Wrap_Struct. `method` is the ruby name to give the reader, `type` is the C type. Unless the C member name is overridden with `member`, the method name is used as the struct member. See accessor for an example.

    # File lib/inline.rb, line 449
    def reader(method, type, member = method)
      raise "struct name not set for reader #{method} #{type}" unless
        @struct_name

      c VALUE #{method}() {  #{@struct_name} *pointer;  Data_Get_Struct(self, #{@struct_name}, pointer);  return #{c2ruby type}(pointer->#{member});}
    end

remove_type_converter(type) click to toggle source

Unregisters C type-casts for `type`.

    # File lib/inline.rb, line 723
    def remove_type_converter(type)
      @type_map.delete type
    end

ruby2c(type) click to toggle source

Converts ruby type `type` to a C type

    # File lib/inline.rb, line 490
    def ruby2c(type)
      raise ArgumentError, "Unknown type #{type.inspect}" unless @type_map.has_key? type
      @type_map[type].first
    end

so_name() click to toggle source

    # File lib/inline.rb, line 382
    def so_name
      unless defined? @so_name then
        @so_name = "#{Inline.directory}/#{module_name}.#{RbConfig::CONFIG["DLEXT"]}"
      end
      @so_name
    end

strip_comments(src) click to toggle source

    # File lib/inline.rb, line 187
    def strip_comments(src)
      # strip c-comments
      src = src.gsub(%\s*/\*.*?\*/%, '')
      # strip cpp-comments
      src = src.gsub(%^\s*//.*?\n%, '')
      src = src.gsub(%[ \t]*//[^\n]*%, '')
      src
    end

writer(method, type, member = method) click to toggle source

Adds a writer for a C struct member wrapped via Data_Get_Struct. `method` is the ruby name to give the writer, `type` is the C type. Unless the C member name is overridden with `member`, the method name is used as the struct member. See accessor for an example.

    # File lib/inline.rb, line 470
    def writer(method, type, member = method)
      raise "struct name not set for writer #{method} #{type}" unless
        @struct_name

      c VALUE #{method}_equals(VALUE value) {  #{@struct_name} *pointer;  Data_Get_Struct(self, #{@struct_name}, pointer);  pointer->#{member} = #{ruby2c type}(value);  return value;}
    end

[Validate]

Generated with the Darkfish Rdoc Generator 2.