Class: Inline::C

Inherits:
Object
  • Object
show all
Includes:
ZenTestMapping
Defined in:
lib/inline.rb

Overview

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.

Constant Summary collapse

MAGIC_ARITY_THRESHOLD =
15
MAGIC_ARITY =
-1
TYPE_MAP =

Default C to ruby and ruby to C type map

{
  'char'               => [ 'NUM2CHR',        'CHR2FIX'      ],

  'char *'             => [ 'StringValuePtr', 'rb_str_new2'  ],

  'double'             => [ 'NUM2DBL',        'rb_float_new' ],

  'int'                => [ "FI\X2INT",       'INT2FIX'      ],
  'unsigned int'       => [ 'NUM2UINT',       'UINT2NUM'     ],
  'unsigned'           => [ 'NUM2UINT',       'UINT2NUM'     ],

  'long'               => [ 'NUM2LONG',       'LONG2NUM'     ],
  'unsigned long'      => [ 'NUM2ULONG',      'ULONG2NUM'    ],

  'long long'          => [ 'NUM2LL',         'LL2NUM'       ],
  'unsigned long long' => [ 'NUM2ULL',        'ULL2NUM'      ],

  'off_t'              => [ 'NUM2OFFT',       'OFFT2NUM'     ],

  'VALUE'              => [ '',               ''             ],
  # Can't do these converters because they conflict with the above:
  # ID2SYM(x), SYM2ID(x), F\IX2UINT(x)
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(mod) ⇒ C

Returns a new instance of C.

Raises:

  • (ArgumentError)


394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
# File 'lib/inline.rb', line 394

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
  @src = []
  @inc = []
  @sig = {}
  @flags = []
  @libs = []
  @init_extra = []
  @include_ruby_first = true
  @inherited_methods = {}
  @struct_name = nil

  @type_map = TYPE_MAP.dup
end

Instance Attribute Details

#flagsObject

Returns the value of attribute flags.



386
387
388
# File 'lib/inline.rb', line 386

def flags
  @flags
end

#init_extraObject

Returns the value of attribute init_extra.



386
387
388
# File 'lib/inline.rb', line 386

def init_extra
  @init_extra
end

#libsObject

Returns the value of attribute libs.



386
387
388
# File 'lib/inline.rb', line 386

def libs
  @libs
end

#modObject

Returns the value of attribute mod.



384
385
386
# File 'lib/inline.rb', line 384

def mod
  @mod
end

#rb_fileObject (readonly)

Returns the value of attribute rb_file.



384
385
386
# File 'lib/inline.rb', line 384

def rb_file
  @rb_file
end

#sigObject

Returns the value of attribute sig.



386
387
388
# File 'lib/inline.rb', line 386

def sig
  @sig
end

#srcObject

Returns the value of attribute src.



386
387
388
# File 'lib/inline.rb', line 386

def src
  @src
end

#struct_nameObject

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



392
393
394
# File 'lib/inline.rb', line 392

def struct_name
  @struct_name
end

Instance Method Details

#accessor(method, type, member = method) ⇒ Object

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.



432
433
434
435
# File 'lib/inline.rb', line 432

def accessor(method, type, member = method)
  reader method, type, member
  writer method, type, member
end

#add_compile_flags(*flags) ⇒ Object

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



660
661
662
# File 'lib/inline.rb', line 660

def add_compile_flags(*flags)
  @flags.push(*flags)
end

#add_id(name) ⇒ Object

Registers a static id_name for the symbol :name.



667
668
669
# File 'lib/inline.rb', line 667

def add_id name
  self.add_static "id_#{name}", "rb_intern(\"#{name}\")"
end

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



675
676
677
# File 'lib/inline.rb', line 675

def add_link_flags(*flags)
  @libs.push(*flags)
end

#add_static(name, init, type = "VALUE") ⇒ Object

Create a static variable and initialize it to a value.



682
683
684
685
# File 'lib/inline.rb', line 682

def add_static name, init, type = "VALUE"
  prefix      "static #{type} #{name};"
  add_to_init "#{name} = #{init};"
end

#add_to_init(*src) ⇒ Object

Adds custom content to the end of the init function.



690
691
692
# File 'lib/inline.rb', line 690

def add_to_init(*src)
  @init_extra.push(*src)
end

#add_type_converter(type, r2c, c2r) ⇒ Object

Registers C type-casts r2c and c2r for type.



697
698
699
700
# File 'lib/inline.rb', line 697

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) ⇒ Object

Registers C type alias_type as an alias of existing_type



705
706
707
708
709
710
# File 'lib/inline.rb', line 705

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

#buildObject

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



523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
# File 'lib/inline.rb', line 523

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 = %w(srcdir 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 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(' ')

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

      # TODO: remove after osx 10.5.2
      cmd += ' -flat_namespace -undefined suppress' if
        RUBY_PLATFORM =~ /darwin9\.[01]/
      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 = {}) ⇒ Object

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.



775
776
777
778
779
780
# File 'lib/inline.rb', line 775

def c src, options = {}
  options = {
    :expand_types => true,
  }.merge options
  self.generate src, options
end

#c2ruby(type) ⇒ Object

Converts C type type to a ruby type

Raises:

  • (ArgumentError)


492
493
494
495
# File 'lib/inline.rb', line 492

def c2ruby(type)
  raise ArgumentError, "Unknown type #{type.inspect}" unless @type_map.has_key? type
  @type_map[type].last
end

#c_raw(src, options = {}) ⇒ Object

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.



799
800
801
# File 'lib/inline.rb', line 799

def c_raw src, options = {}
  self.generate src, options
end

#c_raw_singleton(src, options = {}) ⇒ Object

Same as c_raw, but adds a class function.



806
807
808
809
810
811
# File 'lib/inline.rb', line 806

def c_raw_singleton src, options = {}
  options = {
    :singleton => true,
  }.merge options
  self.generate src, options
end

#c_singleton(src, options = {}) ⇒ Object

Same as c, but adds a class function.



785
786
787
788
789
790
791
# File 'lib/inline.rb', line 785

def c_singleton src, options = {}
  options = {
    :expand_types => true,
    :singleton    => true,
  }.merge options
  self.generate src, options
end

#crap_for_windozeObject

Returns extra compilation flags for windoze platforms. Ugh.



640
641
642
643
644
645
646
647
648
649
650
651
652
653
# File 'lib/inline.rb', line 640

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 = {}) ⇒ Object

def parse_signature

Raises:

  • (ArgumentError)


239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
# 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.*?\{)/m 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_extObject

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



309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
# 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
  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) ⇒ Object

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



750
751
752
# File 'lib/inline.rb', line 750

def include(header)
  @inc << "#include #{header}"
end

#include_ruby_lastObject

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



758
759
760
# File 'lib/inline.rb', line 758

def include_ruby_last
  @include_ruby_first = false
end

#loadObject

Loads the generated code back into ruby



516
517
518
# File 'lib/inline.rb', line 516

def load
  require "#{so_name}" or raise LoadError, "require on #{so_name} failed"
end

#load_cacheObject

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



500
501
502
503
504
505
506
507
508
509
510
511
# File 'lib/inline.rb', line 500

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 ?d, dir and $VERBOSE
      return true
    end
  rescue LoadError
  end
  return false
end

#map_c_const(names_and_types) ⇒ Object

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]


739
740
741
742
743
744
# File 'lib/inline.rb', line 739

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) ⇒ Object

Maps RubyConstants to cRubyConstants.



722
723
724
725
726
727
# File 'lib/inline.rb', line 722

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_nameObject



367
368
369
370
371
372
373
374
375
# File 'lib/inline.rb', line 367

def module_name
  unless defined? @module_name then
    module_name = @mod.name.gsub('::','__')
    md5 = Digest::MD5.new
    @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) ⇒ Object

Raises:

  • (SyntaxError)


196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# 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) ⇒ Object

Adds any amount of text/code to the source



765
766
767
# File 'lib/inline.rb', line 765

def prefix(code)
  @src << code
end

#reader(method, type, member = method) ⇒ Object

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.



443
444
445
446
447
448
449
450
451
452
453
454
455
456
# File 'lib/inline.rb', line 443

def reader(method, type, member = method)
  raise "struct name not set for reader #{method} #{type}" unless
    @struct_name

  c <<-C
VALUE #{method}() {
  #{@struct_name} *pointer;

  Data_Get_Struct(self, #{@struct_name}, pointer);

  return #{c2ruby type}(pointer->#{member});
}
  C
end

#remove_type_converter(type) ⇒ Object

Unregisters C type-casts for type.



715
716
717
# File 'lib/inline.rb', line 715

def remove_type_converter(type)
  @type_map.delete type
end

#ruby2c(type) ⇒ Object

Converts ruby type type to a C type

Raises:

  • (ArgumentError)


484
485
486
487
# File 'lib/inline.rb', line 484

def ruby2c(type)
  raise ArgumentError, "Unknown type #{type.inspect}" unless @type_map.has_key? type
  @type_map[type].first
end

#so_nameObject



377
378
379
380
381
382
# File 'lib/inline.rb', line 377

def so_name
  unless defined? @so_name then
    @so_name = "#{Inline.directory}/#{module_name}.#{RbConfig::CONFIG["DLEXT"]}"
  end
  @so_name
end

#strip_comments(src) ⇒ Object



187
188
189
190
191
192
193
194
# File 'lib/inline.rb', line 187

def strip_comments(src)
  # strip c-comments
  src = src.gsub(%r%\s*/\*.*?\*/%m, '')
  # strip cpp-comments
  src = src.gsub(%r%^\s*//.*?\n%, '')
  src = src.gsub(%r%[ \t]*//[^\n]*%, '')
  src
end

#writer(method, type, member = method) ⇒ Object

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.



464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
# File 'lib/inline.rb', line 464

def writer(method, type, member = method)
  raise "struct name not set for writer #{method} #{type}" unless
    @struct_name

  c <<-C
VALUE #{method}_equals(VALUE value) {
  #{@struct_name} *pointer;

  Data_Get_Struct(self, #{@struct_name}, pointer);

  pointer->#{member} = #{ruby2c type}(value);

  return value;
}
  C
end