Learning extending ruby

Hello!

Could somebody help me with this small piece of code?
I’m learning how to extend ruby with C code.
What I want to do here is creating a class in C and use it in ruby.
The class should have a instance variable named myvar. This works so far.
But now how do I create the accessor methods for this variable?

Thank you!
Dominik

Here is the init method in C:

static VALUE t_init(VALUE self)
{
VALUE str = rb_str_new2(“default string”);
rb_iv_set(self, “@myvar”, str);
rb_define_attr(str, “myvar”, 1, 1);
return self;
}

BTW: Is there any good doc beside the prag. prog. guide about extending ruby?

Hello!

Could somebody help me with this small piece of code?
I’m learning how to extend ruby with C code.
What I want to do here is creating a class in C and use it in ruby.

you want this?
VALUE rb_define_class(const char *name, VALUE super)

BTW: Is there any good doc beside the prag. prog. guide about extending ruby?

README.EXT may help
http://www.ruby-lang.org/cgi-bin/cvsweb.cgi/ruby/README.EXT?rev=1.18

···

il 28 Apr 2004 00:02:46 -0700, dwerder@gmx.net (Dominik Werder) ha scritto::

static VALUE t_init(VALUE self)
{
VALUE str = rb_str_new2(“default string”);
rb_iv_set(self, “@myvar”, str);
rb_define_attr(str, “myvar”, 1, 1);
return self;
}

BTW: Is there any good doc beside the prag. prog. guide about extending ruby?

Have a look at:

···


Lewis’s Law of Travel:
The first piece of luggage out of the chute doesn’t belong to
anyone, ever.
Rasputin :: Jack of All Trades - Master of Nuns

I don’t mean to discourage you from learning, but you might want to
look into using SWIG (www.swig.org) instead of writing extensions by
hand. I can say from personal experience that it will save you lots of
headaches.

Nathan

dwerder@gmx.net (Dominik Werder) wrote in message news:31033452.0404272302.53dfa004@posting.google.com

···

Hello!

Could somebody help me with this small piece of code?
I’m learning how to extend ruby with C code.
What I want to do here is creating a class in C and use it in ruby.
The class should have a instance variable named myvar. This works so far.
But now how do I create the accessor methods for this variable?

Thank you!
Dominik

Here is the init method in C:

static VALUE t_init(VALUE self)
{
VALUE str = rb_str_new2(“default string”);
rb_iv_set(self, “@myvar”, str);
rb_define_attr(str, “myvar”, 1, 1);
return self;
}

BTW: Is there any good doc beside the prag. prog. guide about extending ruby?

Sorry, my description wasnt good enough…

At the bottom of the message is the complete C source code.
The problem is where I put the big comment. At that line, I try to
create attribute accessor methods so that I can easily read and write
this variable (“myvar”) from ruby land…

I thought rb_define_attr() works like:
class Test
attr_reader :myvar
end
doesn’t it?

#include “/usr/local/lib/ruby/1.8/i686-linux/ruby.h”

VALUE cRubyTest;

static VALUE t_init(VALUE self)
{
VALUE str=rb_str_new2(“default string”);
rb_define_attr(str, “myvar”, 1, 1); # <------- PROBLEM!
rb_iv_set(self, “@myvar”, str);
return self;
}

static VALUE t_add(VALUE self, VALUE anObject)
{
VALUE arr;
arr = rb_iv_get(self, “@arr”);
rb_ary_push(arr, anObject);
rb_eval_string(“puts “testing…””);
return arr;
}

static VALUE t_change(VALUE self) {
rb_iv_set(self, “@myvar”, rb_str_new2(“new string”));
return self;
}

void Init_cRubyTest() {
cRubyTest = rb_define_class(“Test”, rb_cObject);
rb_define_method(cRubyTest, “initialize”, t_init, 0);
rb_define_method(cRubyTest, “add”, t_add, 1);
rb_define_method(cRubyTest, “change”, t_change, 0);
}

Nathan Weston wrote:

I don’t mean to discourage you from learning, but you might want to
look into using SWIG (www.swig.org) instead of writing extensions by
hand. I can say from personal experience that it will save you lots of
headaches.

Nathan

dwerder@gmx.net (Dominik Werder) wrote in message
news:31033452.0404272302.53dfa004@posting.google.com

Hello!

Could somebody help me with this small piece of code?
I’m learning how to extend ruby with C code.
What I want to do here is creating a class in C and use it in ruby.
The class should have a instance variable named myvar. This works so far.
But now how do I create the accessor methods for this variable?

Thank you!
Dominik

Here is the init method in C:

static VALUE t_init(VALUE self)
{
VALUE str = rb_str_new2(“default string”);
rb_iv_set(self, “@myvar”, str);
rb_define_attr(str, “myvar”, 1, 1);
return self;
}

It’s definitely worth learning the ruby API itself, first. This is from
the Pickaxe chapter on extending ruby:

···

void rb_define_attr(VALUE variable, const char *name, int read, int write)

Creates accessor methods for the given variable, with the given name. If
read is nonzero, create a read method; if write is nonzero, create a
write method.

This is apparently a typo, since class.c has:

rb_define_attr(klass, name, read, write)
VALUE klass;
const char *name;
int read, write;

So the first argument should be the class you are defining the attr in.
This should be called in your Init_module method

If you want to access data faster from C, you may want to define your
own DATA class and store your variable in the C data structure part of
the object, rather than in ruby ivars. (See README.EXT in the ruby
distribution for details.) That saves you the time it takes to do lookup
in the ivar table. OTOH, if you go this way you have to make sure the
garbage collector knows about any ruby objects stored in the data
struct–you have to define mark() and free() functions.

SWIG can help automate the process of wrapping classes and defining
accessors, which is especially nice if you have a huge library to wrap.

If you are starting from scratch to develop an extension, you might want
to look at my cgenerator library, which takes a different, more dynamic,
approach than SWIG:

http://raa.ruby-lang.org/list.rhtml?name=cgenerator

Using cgen, you can “include CShadow” in one of your classes, and define
attributes that are stored in a C data structure rather than as ivars.
The memory management stuff is handled automatically. Inheritance is
also handled transparently. Just call #commit on your class, and it
compiles and loads the library.

Here’s an example based on the code you wrote:

require ‘cgen/cshadow’

class MyClass
include CShadow
shadow_attr_accessor :my_c_string => “char *my_string”
shadow_attr_accessor :my_ruby_string => String
shadow_attr_accessor :my_ruby_object => Object

 def initialize
   # Must use 'self.varname = value', or else ruby will assign to
   # local var
   self.my_c_string = "default string"
     # stored as null-terminated char *

   self.my_ruby_string = "default string"
     # stored as ruby VALUE string, after checking type is String

   self.my_ruby_object = "default string"
     # stored as ruby VALUE string, no type checking
 end

 def inspect
   "<#{self.class}: %p, %p, %p>" %
     [my_c_string, my_ruby_string, my_ruby_object]
 end

 # easy way to add a method written in C
 define_c_method :foo do

   c_array_args { # interface to rb_scan_args
     required :x
     optional :y
     rest :stuff
     block :block
     typecheck :x => Numeric
     default :y => "INT2NUM(NUM2INT(x) + 1)"
   }

   declare :result   => "VALUE   result"

   body %{
     result = rb_funcall(block, #{declare_symbol :call}, 4,
                         shadow->my_ruby_string,
                         x, y, stuff);
   }
   # note use of shadow var to access shadow attrs

   returns "result"
 end

end

MyClass.commit # write C lib, compile, load

my_obj = MyClass.new
p my_obj

==> <MyClass: “default string”, “default string”, “default string”>

block = proc do |str, x, y, stuff|
“%s, %p, %p, %p” % [str, x, y, stuff]
end

p my_obj.foo(1, &block)

==> “default string, 1, 2,

p my_obj.foo(5, 0, “stuff”, “more stuff”, &block)

==> “default string, 5, 0, ["stuff", "more stuff"]”

class Test
  attr_reader :myvar
end

When you write this, you call the class Method ::attr_reader. This mean
that in your case, you must call rb_define_attr() with cRubyTest

void Init_cRubyTest() {
  cRubyTest = rb_define_class("Test", rb_cObject);

     rb_define_attr(cRubyTest, "myvar", Qtrue, Qfalse);

  rb_define_method(cRubyTest, "initialize", t_init, 0);
  rb_define_method(cRubyTest, "add", t_add, 1);
  rb_define_method(cRubyTest, "change", t_change, 0);
}

Guy Decoux

Hi,

···

On Wed, 28 Apr 2004 08:44:18 -0700, Dominik Werder wrote:

rb_define_attr(str, “myvar”, 1, 1); # <------- PROBLEM!

The attr methods work on the class, not on the instance.
So try this instead (from Init_cRubyTest):

rb_define_attr(cRubyTest, “myvar”, 1, 1);

Kristof

Works like a wonder, thank you very much!

···

void Init_cRubyTest() {
cRubyTest = rb_define_class(“Test”, rb_cObject);

 rb_define_attr(cRubyTest, "myvar", Qtrue, Qfalse);

rb_define_method(cRubyTest, “initialize”, t_init, 0);
rb_define_method(cRubyTest, “add”, t_add, 1);
rb_define_method(cRubyTest, “change”, t_change, 0);
}