Learning extending ruby

D

Dominik Werder

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?
 
G

gabriele renzi

il 28 Apr 2004 00:02:46 -0700, (e-mail address removed) (Dominik Werder) ha
scritto::
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
 
D

Dominik Werder

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);
}
 
T

ts

D> class Test
D> attr_reader :myvar
D> 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

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

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

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


Guy Decoux
 
K

Kristof Bastiaensen

Hi,

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
 
N

Nathan Weston

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
 
J

Joel VanderWerf

Nathan said:
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

(e-mail address removed) (Dominik Werder) wrote in message

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\"]"
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top