Forum: Ruby Accessing C structures in Ruby

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Joe Van D. (Guest)
on 2005-12-13 05:20
(Received via mailing list)
(I've already looked at Swig, btw.  I'd like to do this one by hand.)

I have the following:

typedef struct {
  double x;
  double y;
  double z;
} VelocityRecord;

typedef struct {
  double x;
  double y;
  double z;
} PositionRecord;

typedef struct {
  int id;
  int type;
  PositionRecord position;
  VelocityRecord velocity;
} Player;

Player Players[10];


The number of Players is fixed ahead of time.  I want to be able to
access that data from Ruby.  Ideally, something like:

module Simulation
    attr_accessor :players  # Other stuff will be here
    class Player
        class VelocityRecord
            attr_accessor :x, :y :z
        end
        class PositionRecord
          attr_accessor :x, :y, :z
        end
    end
end
Joe Van D. (Guest)
on 2005-12-13 05:57
(Received via mailing list)
On 12/12/05, Joe Van D. <removed_email_address@domain.invalid> wrote:
> typedef struct {
> } Player;
>         class VelocityRecord
>             attr_accessor :x, :y :z
>         end
>         class PositionRecord
>           attr_accessor :x, :y, :z
>         end
>     end
> end

Hm, on second thought, it's probably a lot easier to do:

class Player
  attr_reader :x_position, :y_position, :z_position, :x_velocity,
:y_velocity, :z_velocity
end

So, maybe something like this?

void Init_Simulation()
{
  rb_define_module(mSimulation, "Simulation");
  rb_define_class_under(cPlayer, "Player");

  rb_define_method(cPlayer, "x_position", get_x_position, 0);
  rb_define_method(cPlayer, "y_position", get_y_position, 0);
  rb_define_method(cPlayer, "z_position", get_z_position, 0);
  rb_define_method(cPlayer, "x_velocity", get_x_velocity, 0);
  rb_define_method(cPlayer, "y_velocity", get_y_velocity, 0);
  rb_define_method(cPlayer, "z_velocity", get_z_velocity, 0);
}
Eero S. (Guest)
on 2005-12-13 06:07
Joe Van D. wrote:
> (I've already looked at Swig, btw.  I'd like to do this one by hand.)
>
> I have the following:
>
> typedef struct {
>   double x;
>   double y;
>   double z;
> } VelocityRecord;
>
> typedef struct {
>   double x;
>   double y;
>   double z;
> } PositionRecord;
>
> typedef struct {
>   int id;
>   int type;
>   PositionRecord position;
>   VelocityRecord velocity;
> } Player;
>
> Player Players[10];
>
>
> The number of Players is fixed ahead of time.  I want to be able to
> access that data from Ruby.  Ideally, something like:
>
> module Simulation
>     attr_accessor :players  # Other stuff will be here
>     class Player
>         class VelocityRecord
>             attr_accessor :x, :y :z
>         end
>         class PositionRecord
>           attr_accessor :x, :y, :z
>         end
>     end
> end

My typical recommendation is to just wrap a pointer to
the struct and pass that around. Then, for each method
just extract the pointer, access the correct field and
generate a VALUE out of that (inside the function that
corresponds to the method).


E
Joe Van D. (Guest)
on 2005-12-13 06:33
(Received via mailing list)
On 12/12/05, Joe Van D. <removed_email_address@domain.invalid> wrote:
> >
> >   VelocityRecord velocity;
> >     class Player
>
>   rb_define_class_under(cPlayer, "Player");
>
>   rb_define_method(cPlayer, "x_position", get_x_position, 0);
>   rb_define_method(cPlayer, "y_position", get_y_position, 0);
>   rb_define_method(cPlayer, "z_position", get_z_position, 0);
>   rb_define_method(cPlayer, "x_velocity", get_x_velocity, 0);
>   rb_define_method(cPlayer, "y_velocity", get_y_velocity, 0);
>   rb_define_method(cPlayer, "z_velocity", get_z_velocity, 0);
> }

So, I have a bunch of functions that look like this now:

VALUE get_player_x_pos(VALUE self)
{
  Player* p;
  Data_Get_Struct(self, Player, p);
   rb_float_new(p->position.x);
}

That the way to go?
Joe Van D. (Guest)
on 2005-12-13 07:00
(Received via mailing list)
On 12/12/05, Eero S. <removed_email_address@domain.invalid> wrote:
> >
> >   VelocityRecord velocity;
> >     class Player
> the struct and pass that around. Then, for each method
> just extract the pointer, access the correct field and
> generate a VALUE out of that (inside the function that
> corresponds to the method).

Can you share more details?  Is that essentially what I've done in my
later posts on this?
Joe Van D. (Guest)
on 2005-12-13 07:15
(Received via mailing list)
On 12/12/05, Joe Van D. <removed_email_address@domain.invalid> wrote:
> > > } VelocityRecord;
> > >   PositionRecord position;
> > >     attr_accessor :players  # Other stuff will be here
> > My typical recommendation is to just wrap a pointer to
> > the struct and pass that around. Then, for each method
> > just extract the pointer, access the correct field and
> > generate a VALUE out of that (inside the function that
> > corresponds to the method).
>
> Can you share more details?  Is that essentially what I've done in my
> later posts on this?

Here's what I have so far.  Seems to work well:

#include "ruby.h"
#include "simulation.h"

VALUE get_frame_count(VALUE self) { return INT2NUM(HIFEN2_Count); }
VALUE get_mission_time(VALUE self) { return INT2NUM(HIFEN2_Mission); }
VALUE get_max_players(VALUE self) { return INT2NUM(MAX_PLAYERS); }

VALUE player_new(VALUE self, VALUE rb_i)
{
  int i = NUM2INT(rb_i);
  return Data_Wrap_Struct(self, NULL, NULL, &Players[i]);
}

Player* get_player(VALUE self)
{
  Player *p;
  Data_Get_Struct(self, Player, p);
  return p;
}

VALUE get_player_id(VALUE self) { return INT2NUM(get_player(self)->id);
}

VALUE get_player_x_pos(VALUE self) { return
rb_float_new(get_player(self)->position.x); }
VALUE get_player_y_pos(VALUE self) { return
rb_float_new(get_player(self)->position.y); }
VALUE get_player_z_pos(VALUE self) { return
rb_float_new(get_player(self)->position.z); }

VALUE get_player_x_vel(VALUE self) { return
rb_float_new(get_player(self)->velocity.x); }
VALUE get_player_y_vel(VALUE self) { return
rb_float_new(get_player(self)->velocity.y); }
VALUE get_player_z_vel(VALUE self) { return
rb_float_new(get_player(self)->velocity.z); }

VALUE cSimulation;
VALUE cPlayer;

void Init_Sim()
{
  cSimulation = rb_define_class("Simulation", rb_cObject);
  rb_define_method(cSimulation, "frame_count", get_frame_count, 0);
  rb_define_method(cSimulation, "mission_time", get_mission_time, 0);
  rb_define_method(cSimulation, "max_players", get_max_players, 0);

  cPlayer = rb_define_class("Player", rb_cObject);
  rb_define_singleton_method(cPlayer, "new", player_new, 1);
  rb_define_method(cPlayer, "player_id", get_player_id, 0);

  rb_define_method(cPlayer, "x_position", get_player_x_pos, 0);
  rb_define_method(cPlayer, "y_position", get_player_y_pos, 0);
  rb_define_method(cPlayer, "z_position", get_player_z_pos, 0);

  rb_define_method(cPlayer, "x_velocity", get_player_x_vel, 0);
  rb_define_method(cPlayer, "y_velocity", get_player_y_vel, 0);
  rb_define_method(cPlayer, "z_velocity", get_player_z_vel, 0);
}
Eero S. (Guest)
on 2005-12-13 07:54
(Received via mailing list)
On 2005.12.13 14:14, Joe Van D. <removed_email_address@domain.invalid> wrote:
> > > >   double z;
> > > >   int type;
> > > > module Simulation
> > >
>
>   return Data_Wrap_Struct(self, NULL, NULL, &Players[i]);
>
> rb_float_new(get_player(self)->velocity.y); }
>   rb_define_method(cSimulation, "mission_time", get_mission_time, 0);
>   rb_define_method(cPlayer, "x_velocity", get_player_x_vel, 0);
>   rb_define_method(cPlayer, "y_velocity", get_player_y_vel, 0);
>   rb_define_method(cPlayer, "z_velocity", get_player_z_vel, 0);
> }

Yep, that looks fine to me. There are certainly other ways but aside
from some repetitiveness, this is easy and versatile (particularly if
you need to pass data to C libraries as well). If you have lots and
lots of functions like that, you could even do with a little #define.


E
Martin DeMello (Guest)
on 2005-12-13 12:00
(Received via mailing list)
Eero S. <removed_email_address@domain.invalid> wrote:
>
> Yep, that looks fine to me. There are certainly other ways but aside
> from some repetitiveness, this is easy and versatile (particularly if
> you need to pass data to C libraries as well). If you have lots and
> lots of functions like that, you could even do with a little #define.

http://redshift.sourceforge.net/cgen/ might be helpful (not used it
myself, but this seems like a prime use case)

martin
Joel VanderWerf (Guest)
on 2005-12-13 20:00
(Received via mailing list)
Martin DeMello wrote:
>
> martin

I would have mentioned it but the OP wanted to do things the hard way :)

Cgen takes care of accessors (including type checking/conversion,
marshalling, mark/free when accessors are typed to refer to objects),
and also manages inheritance of structure members in parallel with the
ruby inheritance hierarchy. It's got a nice wrapper around
rb_parse_args. It's also good if you want to dynamically generate the
extension in response to loaded code, user input, etc. I've been using
it for over 4 years now. The docs need work, though...

An example (simplified from
http://redshift.sourceforge.net/cgen/examples/comp...

require 'cgen/cshadow'

class MyComplex < Numeric
   include CShadow
   shadow_attr_accessor :re => "double re", :im => "double im"
end

MyComplex.commit

z = MyComplex.new
z.re = 5
z.im = 1.3
p z

__END__

Output:

#<MyComplex:0x2b04630 im=1.3, re=5.0>



The full example also shows how to add methods written in C (but
embedded in your ruby source) to do abs() and in-place multiplication.



Running this example generates a directory with the following contents,
which is used to build the extension (all of this happens during the
commit call):

C:\ruby\prj\cgen\examples>ls -l MyComplex
total 220
-rw-rw-rw-  1 vjoel 0    45 2005-12-13 09:42 extconf.rb
-rw-rw-rw-  1 vjoel 0   150 2005-12-13 09:42 make.log
-rw-rw-rw-  1 vjoel 0  3619 2005-12-13 09:42 Makefile
-rw-rw-rw-  1 vjoel 0    25 2005-12-13 09:39 MyComplex-i386-mswin32.def
-rwxrwxrwx  1 vjoel 0  3589 2005-12-13 09:39 MyComplex.c
-rw-rw-rw-  1 vjoel 0   589 2005-12-13 09:40 MyComplex.exp
-rw-rw-rw-  1 vjoel 0   762 2005-12-13 09:39 MyComplex.h
-rw-rw-rw-  1 vjoel 0  1976 2005-12-13 09:40 MyComplex.lib
-rw-rw-rw-  1 vjoel 0 17259 2005-12-13 09:39 MyComplex.obj
-rw-rw-rw-  1 vjoel 0 91136 2005-12-13 09:40 MyComplex.pdb
-rw-rw-rw-  1 vjoel 0 20546 2005-12-13 09:40 MyComplex.so
-rw-rw-rw-  1 vjoel 0 53248 2005-12-13 09:39 vc60.pdb


Cgen is careful not to repeat all this work the second time you run the
same program--it writes files only if they have changed.

And just to give an idea of what functions it handles:

C:\ruby\prj\cgen\examples\MyComplex>grep "^\w" MyComplex.c
static void mark_MyComplex_Shadow(MyComplex_Shadow *shadow);
static void free_MyComplex_Shadow(MyComplex_Shadow *shadow);
VALUE module_MyComplex;
void Init_MyComplex(void)
static void mark_MyComplex_Shadow(MyComplex_Shadow *shadow)
static void free_MyComplex_Shadow(MyComplex_Shadow *shadow)
VALUE __dump__data_module_MyComplex_method(VALUE self)
VALUE __load__data_module_MyComplex_method(VALUE self, VALUE from_array)
VALUE im_module_MyComplex_method(VALUE self)
VALUE im_equals_module_MyComplex_method(int argc, VALUE *argv, VALUE
self)
VALUE re_module_MyComplex_method(VALUE self)
VALUE re_equals_module_MyComplex_method(int argc, VALUE *argv, VALUE
self)
VALUE new_module_MyComplex_singleton_method(int argc, VALUE *argv, VALUE
self)
VALUE alloc_func_module_MyComplex(VALUE klass)



Here's an example (sample.rb) showing the argument parsing wrapper:

lib2.define_c_singleton_method(Point, :test).instance_eval {
   c_array_args {
     required  :arg0, :arg1
     optional  :arg2, :arg3, :arg4
     typecheck :arg2 => Numeric, :arg3 => Numeric
     default   :arg3 => "INT2NUM(7)",
               :arg4 => "INT2NUM(NUM2INT(arg2) + NUM2INT(arg3))"
     rest      :rest
     block     :block
   }
   body %{\
     rb_funcall(block, #{declare_symbol :call}, 6,
                arg0, arg1, arg2, arg3, arg4, rest);
   }
}
This topic is locked and can not be replied to.