From: Joel VanderWerf Date: 2004-05-01T08:19:03+09:00 Subject: Re: Learning extending ruby 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 # ==> 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\"]"