Skip to content

Commit

Permalink
Added accessors for members Data_Wrap_Structs
Browse files Browse the repository at this point in the history
[git-p4: depot-paths = "//src/RubyInline/dev/": change = 4354]
  • Loading branch information
drbrain committed Oct 14, 2008
1 parent c88e0ed commit b19e0df
Show file tree
Hide file tree
Showing 3 changed files with 292 additions and 7 deletions.
1 change: 1 addition & 0 deletions History.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Inline::C now uses ZenTestMapping to automatically map C functions to ruby
names.
* Inline::C#map_c_const can now map to a ruby constant name.
* Added accessors for members of Data_Wrap_Structs. See Inline::C#accessor.

=== 3.7.0 / 2008-06-09

Expand Down
76 changes: 69 additions & 7 deletions lib/inline.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ def self.directory
@@directory
end

##
# 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
Expand All @@ -123,8 +124,6 @@ class C

include ZenTestMapping

protected unless $TESTING

MAGIC_ARITY_THRESHOLD = 15
MAGIC_ARITY = -1

Expand Down Expand Up @@ -275,12 +274,14 @@ def so_name
end

attr_reader :rb_file, :mod
if $TESTING then
attr_writer :mod
attr_accessor :src, :sig, :flags, :libs, :init_extra
end
attr_writer :mod
attr_accessor :src, :sig, :flags, :libs, :init_extra

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

attr_accessor :struct_name

def initialize(mod)
raise ArgumentError, "Class/Module arg is required" unless Module === mod
Expand All @@ -305,6 +306,67 @@ def initialize(mod)
@inherited_methods = {}
end

##
# 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.

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

##
# 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.

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

##
# 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.

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

##
# Converts ruby type +type+ to a C type

Expand Down
222 changes: 222 additions & 0 deletions test/test_inline.rb
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,216 @@ def test_initialize
assert_equal [], x.libs
end

def test_accessor
builder = Inline::C.new self.class

builder.struct_name = 'MyStruct'
builder.accessor 'method_name', 'int'

source = util_strip_lines builder.src

expected = []
expected << <<-READER
# line N "./lib/inline.rb"
static VALUE method_name(VALUE self) {
MyStruct *pointer;
Data_Get_Struct(self, MyStruct, pointer);
return (INT2FIX(pointer->method_name));
}
READER

expected << <<-WRITER
# line N "./lib/inline.rb"
static VALUE method_name_equals(VALUE self, VALUE _value) {
VALUE value = (_value);
MyStruct *pointer;
Data_Get_Struct(self, MyStruct, pointer);
pointer->method_name = FIX2INT(value);
return (value);
}
WRITER

assert_equal expected, source
end

def test_accessor_member_name
builder = Inline::C.new self.class

builder.struct_name = 'MyStruct'
builder.accessor 'method_name', 'int', 'member_name'

source = util_strip_lines builder.src

expected = []
expected << <<-READER
# line N "./lib/inline.rb"
static VALUE method_name(VALUE self) {
MyStruct *pointer;
Data_Get_Struct(self, MyStruct, pointer);
return (INT2FIX(pointer->member_name));
}
READER

expected << <<-WRITER
# line N "./lib/inline.rb"
static VALUE method_name_equals(VALUE self, VALUE _value) {
VALUE value = (_value);
MyStruct *pointer;
Data_Get_Struct(self, MyStruct, pointer);
pointer->member_name = FIX2INT(value);
return (value);
}
WRITER

assert_equal expected, source
end

def test_accessor_no_struct_name
builder = Inline::C.new self.class

e = assert_raises RuntimeError do
builder.accessor 'method_name', 'int'
end

assert_equal "struct name not set for reader method_name int", e.message
end

def test_reader
builder = Inline::C.new self.class

builder.struct_name = 'MyStruct'
builder.reader 'method_name', 'int'

source = util_strip_lines builder.src

expected = []
expected << <<-READER
# line N "./lib/inline.rb"
static VALUE method_name(VALUE self) {
MyStruct *pointer;
Data_Get_Struct(self, MyStruct, pointer);
return (INT2FIX(pointer->method_name));
}
READER

assert_equal expected, source
end

def test_reader_member_name
builder = Inline::C.new self.class

builder.struct_name = 'MyStruct'
builder.reader 'method_name', 'int', 'member_name'

source = util_strip_lines builder.src

expected = []
expected << <<-READER
# line N "./lib/inline.rb"
static VALUE method_name(VALUE self) {
MyStruct *pointer;
Data_Get_Struct(self, MyStruct, pointer);
return (INT2FIX(pointer->member_name));
}
READER

assert_equal expected, source
end

def test_reader_no_struct_name
builder = Inline::C.new self.class

e = assert_raises RuntimeError do
builder.reader 'method_name', 'int'
end

assert_equal "struct name not set for reader method_name int", e.message
end

def test_writer
builder = Inline::C.new self.class

builder.struct_name = 'MyStruct'
builder.writer 'method_name', 'int'

source = util_strip_lines builder.src

expected = []
expected << <<-WRITER
# line N "./lib/inline.rb"
static VALUE method_name_equals(VALUE self, VALUE _value) {
VALUE value = (_value);
MyStruct *pointer;
Data_Get_Struct(self, MyStruct, pointer);
pointer->method_name = FIX2INT(value);
return (value);
}
WRITER

assert_equal expected, source
end

def test_writer_member_name
builder = Inline::C.new self.class

builder.struct_name = 'MyStruct'
builder.writer 'method_name', 'int', 'member_name'

source = util_strip_lines builder.src

expected = []
expected << <<-WRITER
# line N "./lib/inline.rb"
static VALUE method_name_equals(VALUE self, VALUE _value) {
VALUE value = (_value);
MyStruct *pointer;
Data_Get_Struct(self, MyStruct, pointer);
pointer->member_name = FIX2INT(value);
return (value);
}
WRITER

assert_equal expected, source
end

def test_writer_no_struct_name
builder = Inline::C.new self.class

e = assert_raises RuntimeError do
builder.writer 'method_name', 'int'
end

assert_equal "struct name not set for writer method_name int", e.message
end

def test_ruby2c
x = Inline::C.new(self.class)
assert_equal 'NUM2CHR', x.ruby2c("char")
Expand Down Expand Up @@ -249,6 +459,7 @@ def test_parse_signature_register

def util_generate(src, expected, expand_types=true)
result = @builder.generate src, expand_types
result = util_strip_lines result
result.gsub!(/\# line \d+/, '# line N')
expected = "# line N \"#{$0}\"\n" + expected
assert_equal(expected, result)
Expand All @@ -258,6 +469,17 @@ def util_generate_raw(src, expected)
util_generate(src, expected, false)
end

def util_strip_lines(src)
case src
when String then
src.gsub(/\# line \d+/, '# line N')
when Array then
src.map do |chunk|
util_strip_lines chunk
end
end
end

# Ruby Arity Rules, from the mouth of Matz:
# -2 = ruby array argv
# -1 = c array argv
Expand Down

0 comments on commit b19e0df

Please sign in to comment.