Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I get swig wrap std::vector to Ruby class?

Tags:

ruby

stl

swig

I have an application with an embedded Ruby interpreter, and interfaces to STL classes generated by swig. Pretty much everything worked out fine thanks to swig, except for one thing:

%module Stuff
%import "std_vector.i"
namespace std
{
  %template(Vectord) vector<double>;
}; 

%inline%{
  std::vector<double> test;
%}

When I try to use this in Ruby the type Stuff::Vectord exists, but it is not the return type of the generated singleton method test. Looking at the generated C wrapper file I can see the class Vectord and its methods getting defined, but looking at _wrap_test_get I do not see anything returning sth of class Stuff::Vectord.

What do I have to do to get test typed as Vectord?

like image 516
jackson Avatar asked Jan 31 '11 10:01

jackson


1 Answers

First, you are correct. The ruby generated Stuff::test object is not of type Vectord.

However, it is of a type that behaves exactly like Vectord which is the most important question for a duck typed language like Ruby.

Correction to Stuff.i

There is one minor problem in your example. The %import <stl_vector.i> statement should be %include <stl_vector.i>.

The correct complete example is:

%module Stuff
%include "std_vector.i"
namespace std
{
  %template(Vectord) vector<double>;
}; 

%inline%{
  std::vector<double> test;
%}

Building the Module

The swig wrapper can be generated with

swig -ruby -c++ Stuff.i

And compiled (with g++) with the statement:

g++ Stuff_wrap.cxx -shared -fPIC -o Stuff.so -I /usr/lib/ruby/1.8/x86_64-linux/

Using the Module

jason@jason-VirtualBox:~$ irb
irb(main):001:0> require 'Stuff'
=> true
irb(main):003:0> Stuff::test.class
=> Array

As we can see, the object returned by Stuff::test is of type Array. However, unlike a regular Ruby array, this array can only be set to a value that is convertible to a std::vector<double>

irb(main):002:0> Stuff::test = [5,4,3]
=> [5, 4, 3]
irb(main):003:0> Stuff::test = [5.0, "5.0"]
TypeError: double
    from (irb):3:in `test='
    from (irb):3
    from :0

Also, it can be converted directly to and from std::vector<double> objects.

irb(main):002:0> vec = Stuff::Vectord.new()
=> std::vector<double,std::allocator< double > > []
irb(main):003:0> vec << 5.0
=> 5.0
irb(main):004:0> vec << 2.3
=> 2.3
irb(main):005:0> vec
=> std::vector<double,std::allocator< double > > [5.0,2.3]
irb(main):006:0> Stuff::test = vec
=> std::vector<double,std::allocator< double > > [5.0,2.3]
irb(main):007:0> vec2 = Stuff::Vectord.new(Stuff::test)
=> std::vector<double,std::allocator< double > > [5.0,2.3]
irb(main):008:0> Stuff::test.class
=> Array
irb(main):009:0> vec2.class
=> Stuff::Vectord
irb(main):010:0> Stuff::test
=> [5.0, 2.3]

Other SWIG Targets

It's important to note that the statically typed SWIG targets of C# and Java produce the results you were expecting. If you ever have a question about how your types are being handled in a SWIG output, it is almost always a good idea to compare the C# or Java output against what you are expecting.

Conclusion

While Stuff::test and Vectord are two different types, they both are arrays that can only store doubles.

They are almost indistinguishable inside of the Ruby code. The code produced does exactly what you want it to do.

like image 160
lefticus Avatar answered Nov 02 '22 15:11

lefticus