diff --git a/examples/grm.rb b/examples/grm.rb index a6cc49ee..5d9b185f 100644 --- a/examples/grm.rb +++ b/examples/grm.rb @@ -11,14 +11,8 @@ y = n.times.map { |i| Math.cos(x[i]) * x[i] } z = n.times.map { |i| Math.sin(x[i]) * x[i] } -args = GRM.args_new -GRM.args_push(args, 'kind', 's', :const_string, 'plot3') -GRM.args_push(args, 'x', 'nD', :int, n, :voidp, x.pack('d*')) -GRM.args_push(args, 'y', 'nD', :int, n, :voidp, y.pack('d*')) -GRM.args_push(args, 'z', 'nD', :int, n, :voidp, z.pack('d*')) - -GRM.plot(args) - +GRM.plot(kind: "plot3", + x: x, + y: y, + z: z) gets - -GRM.args_delete(args) diff --git a/lib/gr_commons/define_methods.rb b/lib/gr_commons/define_methods.rb index db4a672d..1d42f8d1 100644 --- a/lib/gr_commons/define_methods.rb +++ b/lib/gr_commons/define_methods.rb @@ -20,7 +20,11 @@ def define_ffi_methods(ffi_module, prefix: '', default_type: :double) when ->(x) { defined?(Numo::NArray) && x.is_a?(Numo::NArray) } GRCommonUtils.public_send(default_type, arg) else - arg + if arg.respond_to?(:to_gr) + arg.to_gr + else + arg + end end end ffi_module.public_send(method, *args) diff --git a/lib/grm.rb b/lib/grm.rb index 2f164c26..bbf220cc 100644 --- a/lib/grm.rb +++ b/lib/grm.rb @@ -61,6 +61,112 @@ class << self # a Fiddley::MemoryPointer in the GRBase class. extend GRMBase + class Args + class << self + def try_convert(value) + case value + when Hash + new(**value) + else + nil + end + end + end + + def initialize(**args) + @args = GRM.args_new + @args.free = FFI["grm_args_delete"] + @references = [] + args.each do |key, value| + push(key, value) + end + end + + def push(key, value) + key = key.to_s if key.is_a?(Symbol) + case value + when String + GRM.args_push(@args, key, "s", :const_string, value) + when Integer + GRM.args_push(@args, key, "i", :int, value) + when Float + GRM.args_push(@args, key, "d", :double, value) + when Args + GRM.args_push(@args, key, "a", :voidp, value.address) + value.to_gr.free = nil + when Array + case value[0] + when String + addresses = value.collect {|v| Fiddle::Pointer[v].to_i} + GRM.args_push(@args, key, "nS", + :int, value.size, + :voidp, addresses.pack("J*")) + when Integer + GRM.args_push(@args, key, "nI", + :int, value.size, + :voidp, value.pack("i*")) + when Float + GRM.args_push(@args, key, "nD", + :int, value.size, + :voidp, value.pack("d*")) + when Args + GRM.args_push(@args, key, "nA", + :int, value.size, + :voidp, value.collect(&:address).pack("J*")) + value.each do |v| + v.to_gr.free = nil + end + else + vs = value.collect {|v| Args.new(**v)} + @references.concat(vs) + GRM.args_push(@args, key, "nA", + :int, value.size, + :voidp, vs.collect(&:address).pack("J*")) + vs.each do |v| + v.to_gr.free = nil + end + end + else + v = Args.new(**value) + @references << v + GRM.args_push(@args, key, "a", :voidp, v.address) + v.to_gr.free = nil + end + end + + def clear + GRM.args_clear(@args) + @references.clear + end + + def address + @args.to_i + end + + def to_gr + @args + end + end + class << self + def merge(args=nil) + super(Args.try_convert(args) || args) + end + + def merge_extended(args=nil, hold=0, identifiator=nil) + super(Args.try_convert(args) || args, hold, identificator) + end + + def merge_hold(args=nil) + super(Args.try_convert(args) || args) + end + + def merge_named(args=nil, identifiator=nil) + super(Args.try_convert(args) || args, identificator) + end + + def plot(args=nil) + super(Args.try_convert(args) || args) + end end end diff --git a/test/grm_test.rb b/test/grm_test.rb index c3f380c0..7df0b238 100644 --- a/test/grm_test.rb +++ b/test/grm_test.rb @@ -26,4 +26,116 @@ def test_grm_ffi_lib def test_version assert_kind_of(String, GRM::VERSION) end + + class ArgsTest < self + def test_empty + GRM::Args.new + end + + def test_string + args = GRM::Args.new(x: "hello") + Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP, Fiddle::RUBY_FREE) do |output| + GRM.args_values(args, "x", "s", :voidp, output) + assert_equal("hello", Fiddle::Pointer.read(output[0, output.size].unpack("J")[0], 5)) + end + end + + def test_integer + args = GRM::Args.new(x: 29) + Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT, Fiddle::RUBY_FREE) do |output| + GRM.args_values(args, "x", "i", :voidp, output) + assert_equal([29], output[0, output.size].unpack("i")) + end + end + + def test_float + args = GRM::Args.new(x: 2.9) + Fiddle::Pointer.malloc(Fiddle::SIZEOF_DOUBLE, Fiddle::RUBY_FREE) do |output| + GRM.args_values(args, "x", "d", :voidp, output) + assert_equal([2.9], output[0, output.size].unpack("d")) + end + end + + def test_args + sub_args = GRM::Args.new + args = GRM::Args.new(x: sub_args) + Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP, Fiddle::RUBY_FREE) do |output| + GRM.args_values(args, "x", "a", :voidp, output) + assert_equal(sub_args.address, output[0, output.size].unpack("J")[0]) + end + end + + def test_hash + args = GRM::Args.new(x: {sub: 29}) + Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP, Fiddle::RUBY_FREE) do |output| + GRM.args_values(args, "x", "a", :voidp, output) + address = output[0, output.size].unpack("J")[0] + Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT, Fiddle::RUBY_FREE) do |sub_output| + GRM.args_values(address, "sub", "i", :voidp, sub_output) + assert_equal([29], sub_output[0, sub_output.size].unpack("i")) + end + end + end + + def test_array_string + args = GRM::Args.new(x: ["hello", "world"]) + Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP, Fiddle::RUBY_FREE) do |output| + GRM.args_values(args, "x", "S", :voidp, output) + address = output[0, output.size].unpack("J")[0] + value_addresses = Fiddle::Pointer.read(address, Fiddle::SIZEOF_VOIDP * 2).unpack("J*") + values = value_addresses.collect do |value_address| + Fiddle::Pointer.read(value_address, 5) + end + assert_equal(["hello", "world"], values) + end + end + + def test_array_integer + args = GRM::Args.new(x: [2, 9]) + Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP, Fiddle::RUBY_FREE) do |output| + GRM.args_values(args, "x", "I", :voidp, output) + address = output[0, output.size].unpack("J")[0] + assert_equal([2, 9], Fiddle::Pointer.read(address, Fiddle::SIZEOF_INT * 2).unpack("i*")) + end + end + + def test_array_float + args = GRM::Args.new(x: [2.9, -9.2]) + Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP, Fiddle::RUBY_FREE) do |output| + GRM.args_values(args, "x", "D", :voidp, output) + address = output[0, output.size].unpack("J")[0] + assert_equal([2.9, -9.2], + Fiddle::Pointer.read(address, Fiddle::SIZEOF_DOUBLE * 2).unpack("d*")) + end + end + + def test_array_args + sub_args1 = GRM::Args.new + sub_args2 = GRM::Args.new + args = GRM::Args.new(x: [sub_args1, sub_args2]) + Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP, Fiddle::RUBY_FREE) do |output| + GRM.args_values(args, "x", "A", :voidp, output) + address = output[0, output.size].unpack("J")[0] + assert_equal([sub_args1.address, sub_args2.address], + Fiddle::Pointer.read(address, Fiddle::SIZEOF_VOIDP * 2).unpack("J*")) + end + end + + def test_array_hash + args = GRM::Args.new(x: [{sub1: 29}, {sub2: 2.9}]) + Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP, Fiddle::RUBY_FREE) do |output| + GRM.args_values(args, "x", "A", :voidp, output) + address = output[0, output.size].unpack("J")[0] + args_addresses = Fiddle::Pointer.read(address, Fiddle::SIZEOF_VOIDP * 2).unpack("J*") + Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT, Fiddle::RUBY_FREE) do |sub1_output| + GRM.args_values(args_addresses[0], "sub1", "i", :voidp, sub1_output) + assert_equal([29], sub1_output[0, sub1_output.size].unpack("i")) + end + Fiddle::Pointer.malloc(Fiddle::SIZEOF_DOUBLE, Fiddle::RUBY_FREE) do |sub2_output| + GRM.args_values(args_addresses[1], "sub2", "d", :voidp, sub2_output) + assert_equal([2.9], sub2_output[0, sub2_output.size].unpack("d")) + end + end + end + end end