Professional Web Applications Themes

Swig: Extra objects being created - Ruby

I'm finding that accessor methods on a wrapped C++ class end up creating extra objects on the Ruby side. Here's an example to illustrate: The following Ruby code uses two wrapped C++ classes (Point and Edge) in the shared library TSP.so. An Edge has two Point objects and the 'start_node' and 'end_node' methods on Edge return Point objects. #test_point.rb require 'TSP' include TSP def count_points np = 0 ObjectSpace.each_object(Point){|p| np+=1 } np end pa = Point.new(2,3) pb = Point.new(10,6) puts "number of points should be 2: count_points=#{count_points}" edge = Edge.new(pa,pb,(pa-pb)) puts "number of points should be 2: count_points=#{count_points}" puts edge.start_node ...

  1. #1

    Default Swig: Extra objects being created

    I'm finding that accessor methods on a wrapped C++ class end up creating
    extra objects on the Ruby side. Here's an example to illustrate:

    The following Ruby code uses two wrapped C++ classes (Point and Edge) in
    the shared library TSP.so. An
    Edge has two Point objects and the 'start_node' and 'end_node' methods on
    Edge return Point objects.

    #test_point.rb
    require 'TSP'

    include TSP

    def count_points
    np = 0
    ObjectSpace.each_object(Point){|p|
    np+=1
    }
    np
    end

    pa = Point.new(2,3)
    pb = Point.new(10,6)

    puts "number of points should be 2: count_points=#{count_points}"

    edge = Edge.new(pa,pb,(pa-pb))

    puts "number of points should be 2: count_points=#{count_points}"

    puts edge.start_node #<-here's where the trouble starts
    puts edge.end_node
    puts "number of points should be 2: count_points=#{count_points}"
    #end test_point.rb

    Running test_point.rb prints:
    making a new Point at 2,3
    making a new Point at 10,6
    number of points should be 2: count_points=2
    number of points should be 2: count_points=2
    (2,3)
    (10,6)
    number of points should be 2: count_points=4


    So after calling the accessor methods 'start_node' and 'end_node' the
    number of Point objects is 4 instead of 2. I think this is being caused
    by SWIG_NewPointerObj. Maybe there's no way around it. I would have
    expected that I would get a reference to an already existing Point object
    instead of a new Point object being created.

    Is there any way to do this so that no new Point objects are created when
    calling Edge#end_node, Edge#start_node? Since my script calls these
    methods a lot, lots of extra Point objects get created.

    Here are the point.h/cpp and edge.h/cpp files:

    #include <iostream>
    class Point
    {
    // attr_accessor :x, :y
    private:
    int x_;
    int y_;

    //Point(Point& x){};
    public:

    Point(int x,int y):x_(x),y_(y){std::cout << "making a new Point at "
    << x <<","<<y<< std::endl;}
    Point(Point& p){std::cout << "copy constructor" << std::endl;}

    //calculate the distance between this point and other point
    float operator -(Point& o);

    bool operator ==(Point& o);

    //accessors:
    int x(){ return x_; }
    int y(){ return y_; }


    };

    float Point::operator -(Point& o){
    return sqrt((x_ - o.x())*(x_ - o.x())+(y_-o.y())*(y_ - o.y()));
    };

    bool Point::operator ==(Point& o){
    return ((x_==o.x())&&(y_==o.y()));
    };


    #include "point.h"
    class Edge
    {
    private:
    Point* start_node_;
    Point* end_node_;
    float length_;
    float tao_;
    float quality_factor_;

    public:
    //constructor:

    Edge(Point* s_node, Point* e_node, float len, float tao=TAO0);

    //accessors
    float length() const{return length_;}
    Point* start_node() const {return start_node_;}
    Point* end_node() const {return end_node_;}
    float quality_factor() const { return quality_factor_;}
    float tao() const {return tao_;}
    void set_tao(float new_tao){ tao_ = new_tao; }

    void calc_quality_factor();

    };

    #include "edge.h"
    #include <math.h>

    Edge::Edge(Point* s_node, Point* e_node, float len, float
    tao):start_node_(s_node),end_node_(e_node),length_ (len),tao_(tao){
    this->calc_quality_factor();
    }

    void Edge::calc_quality_factor(){
    quality_factor_=(tao_ * (pow((1.0/length_),BETA)) );

    }
    ///end C++ code

    Here is the TSP.i file:

    %module TSP
    %{
    #include "constants.h"
    #include "point.h"
    #include "edge.h"
    %}
    %alias Edge::set_tao "tao=";
    %include "constants.h"
    %include "point.h"
    %include "edge.h"
    Phil Guest

  2. #2

    Default Re: Swig: Extra objects being created

    Phil Tomson wrote:
     

    <snip>
     

    No, you are correct that every time the accessor (or any other method
    that returns a pointer to some object) is called, you'll get a new Ruby
    instance that wraps that C++ object.
     

    I have a workaround for this in FXRuby, but it is a little complicated.
    Generally speaking, I maintain a little hash table that maps the C++
    pointers to the corresponding Ruby objects. When it's time to return a
    Ruby object from one of these accessors, I first check that hash table
    to see if there's already a "live" Ruby instance corresponding to the
    underlying C++ object and, if so, return that Ruby instance. Otherwise,
    I call SWIG_NewPointerObj() as usual and store this new association in
    the hash.

    It might be possible to extend SWIG to automate this process, I'm not
    sure. We'd have to think about the implications for garbage collection
    as well; for example, if the Ruby object gets garbage-collected, we need
    to be sure to remove it from the hash table since it's no longer "alive".

    Lyle Guest

  3. #3

    Default Re: Swig: Extra objects being created

    In article <net>,
    Lyle Johnson <net> wrote: 
    >
    ><snip>

    >
    >No, you are correct that every time the accessor (or any other method
    >that returns a pointer to some object) is called, you'll get a new Ruby
    >instance that wraps that C++ object.

    >
    >I have a workaround for this in FXRuby, but it is a little complicated.
    >Generally speaking, I maintain a little hash table that maps the C++
    >pointers to the corresponding Ruby objects. When it's time to return a
    >Ruby object from one of these accessors, I first check that hash table
    >to see if there's already a "live" Ruby instance corresponding to the
    >underlying C++ object and, if so, return that Ruby instance. Otherwise,
    >I call SWIG_NewPointerObj() as usual and store this new association in
    >the hash.
    >
    >It might be possible to extend SWIG to automate this process, I'm not
    >sure. We'd have to think about the implications for garbage collection
    >as well; for example, if the Ruby object gets garbage-collected, we need
    >to be sure to remove it from the hash table since it's no longer "alive".
    >[/ref]

    Lyle,

    Since a lot of my outgoing email seems to be bouncing today for whatever
    reason, I'll try posting here...

    I hand edited the wrap file so that no new objects are being created.
    Basically, I just added some Ruby instance variables to shadow the C++
    ones. Here's the Edge constructor:

    static VALUE
    _wrap_new_Edge(int argc, VALUE *argv, VALUE self) {
    Point *arg1 = (Point *) 0 ;
    Point *arg2 = (Point *) 0 ;
    float arg3 ;
    float arg4 = (float) 1.0/(80*8000) ;
    Edge *result;

    if ((argc < 3) || (argc > 4))
    rb_raise(rb_eArgError, "wrong # of arguments(%d for 3)",argc);
    SWIG_ConvertPtr(argv[0], (void **) &arg1, SWIGTYPE_p_Point, 1);
    SWIG_ConvertPtr(argv[1], (void **) &arg2, SWIGTYPE_p_Point, 1);
    arg3 = (float) NUM2DBL(argv[2]);
    if (argc > 3) {
    arg4 = (float) NUM2DBL(argv[3]);
    }
    result = (Edge *)new Edge(arg1,arg2,arg3,arg4);
    DATA_PTR(self) = result;
    //added the following two lines:
    rb_iv_set(self,"start_node",argv[0]);
    rb_iv_set(self,"end_node",argv[1]);
    return self;
    }


    Here's one of the accessors:

    static VALUE
    _wrap_Edge_start_node(int argc, VALUE *argv, VALUE self) {
    Edge *arg1 = (Edge *) 0 ;
    Point *result;
    VALUE vresult = Qnil;

    if ((argc < 0) || (argc > 0))
    rb_raise(rb_eArgError, "wrong # of arguments(%d for 0)",argc);
    //replace these lines:
    //SWIG_ConvertPtr(self, (void **) &arg1, SWIGTYPE_p_Edge, 1);
    //result = (Point *)((Edge const *)arg1)->start_node();
    //vresult = SWIG_NewPointerObj((void *) result, SWIGTYPE_p_Point,0);

    //with this one:
    vresult = rb_iv_get(self,"start_node");
    return vresult;
    }


    Works great.

    Is there any way to change Swig's behavior so that I can automate this so
    I don't have to go in an hand-edit the _wrap file?

    I imagine that I need to change some swg files under the SWIG/Lib/Ruby
    directory, but I'm not sure where to start. Pointers would be much
    appreciated.

    Phil
    Phil Guest

Similar Threads

  1. Ai CS2 - Objects Created With Transform Effect Do Not Print
    By Dusko_Savatovic@adobeforums.com in forum Adobe Illustrator Windows
    Replies: 4
    Last Post: October 13th, 08:07 PM
  2. Illustartor CS - Scaling Objects created with 3d effects.
    By Anlina_Sheng@adobeforums.com in forum Adobe Illustrator Windows
    Replies: 2
    Last Post: May 10th, 09:46 PM
  3. Replies: 3
    Last Post: August 6th, 01:04 PM
  4. SWIG and "connect"? [Was: SWIG and char * parameters]
    By YANAGAWA Kazuhisa in forum Ruby
    Replies: 0
    Last Post: July 25th, 09:31 AM

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139