[NUBY] Iteration in extensions

F

Fredrik Jagenheim

I can't get this simple thing to work. My extension uses two
functions, 'add_value' and 'add_values'. 'add_value' takes one value
and 'add_values' takes an array of values.

The idea is to have add_values to call add_value for each of the
values. Simple enough...

static VALUE add_value(VALUE self, VALUE val)
{
# Code that works
}

static VALUE add_values(VALUE self, VALUE vals)
{
rb_iterate(rb_each, vals, add_value, 0);
}

'add_value' works fine, but when I call 'add_values', I get this:
TypeError: wrong argument type String (expected Data)


I guess I just don't understand how the rb_iterate is supposed to work...

Any pointers would be appreciated.
//F
 
D

daz

Fredrik said:
I can't get this simple thing to work. My extension uses two
functions, 'add_value' and 'add_values'. 'add_value' takes one value
and 'add_values' takes an array of values.

The idea is to have add_values to call add_value for each of the
values. Simple enough...

static VALUE add_value(VALUE self, VALUE val)
{
# Code that works
}

static VALUE add_values(VALUE self, VALUE vals)
{
rb_iterate(rb_each, vals, add_value, 0);
}

'add_value' works fine, but when I call 'add_values', I get this:
TypeError: wrong argument type String (expected Data)

You don't show how you defined 'add_values' or how you're calling it
from Ruby, but I think your initial array is in 'self' ...
.... 'vals' could contain garbage ?


Here's an example from enum.c (Enumerable#reject)
with some printf() statements added.

/* this line from later in enum.c */
rb_define_method(rb_mEnumerable,"reject", enum_reject, 0);


static VALUE
reject_i(i, ary)
VALUE i, ary;
{
printf("debug; i == %s\n", RSTRING(rb_inspect(i))->ptr);

if (!RTEST(rb_yield(i))) {
printf("debug; saving %s\n", RSTRING(rb_inspect(i))->ptr);
rb_ary_push(ary, i);
}
return Qnil;
}

static VALUE
enum_reject(obj) /* -- Enumerable#reject -- */
VALUE obj;
{
VALUE ary = rb_ary_new();

printf("debug; iter >>\n");

rb_iterate(rb_each, obj, reject_i, ary);

printf("debug; << iter\n");

return ary;
}


#-------------------
#
# Ruby:
# Array#reject doesn't use rb_iterate, so
# contrive to use Enumerable#reject which
# has the debug statements.

class MyArr
include Enumerable

def initialize(*args)
@arr = Array.new(args)
end

# Enumerable mix-in needs MyArr#each
def each
# Use Array#each
@arr.each {|e| yield e}
end
end

enum = MyArr.new(5,7,1,3,9,2)
p enum.reject{|val| val > 5} #-> [5, 1, 3, 2]


#-> debug; iter >>
#-> debug; i == 5
#-> debug; saving 5
#-> debug; i == 7
#-> debug; i == 1
#-> debug; saving 1
#-> debug; i == 3
#-> debug; saving 3
#-> debug; i == 9
#-> debug; i == 2
#-> debug; saving 2
#-> debug; << iter
#-> [5, 1, 3, 2]


:daz
 
F

Fredrik Jagenheim

You don't show how you defined 'add_values' or how you're calling it
from Ruby, but I think your initial array is in 'self' ...

Here is a more complete example of what I'm trying to do.
Here's an example from enum.c (Enumerable#reject)
with some printf() statements added.

I'm afraid I didn't understand how to translate your example into mine.

This is basically what I'm trying to do:

-------8<------ Bitmask.c
#include "ruby.h"

static VALUE
bm_new(VALUE class)
{
long* bitmask;
bitmask = ALLOC(long);
*bitmask = 0;
VALUE data = Data_Wrap_Struct(class, 0, free, bitmask);
return data;
}

static VALUE
bm_add_bit(VALUE self, VALUE bit)
{
long* bitmask;
int int_bit = NUM2INT(bit);
printf("in to add_bit: %s %s\n",
RSTRING(rb_inspect(self))->ptr,
RSTRING(rb_inspect(bit))->ptr);
Data_Get_Struct(self, long, bitmask);
*bitmask |= 1 << int_bit;
return self;
}

static VALUE
bm_add_bits(VALUE self, VALUE bits)
{
printf("in to add_bits: %s\n", RSTRING(rb_inspect(bits))->ptr);
rb_iterate(rb_each, bits, bm_add_bit, 1);
return self;
}

static VALUE
bm_value(VALUE self)
{
long* bitmask;
Data_Get_Struct(self, long, bitmask);
return INT2NUM(*bitmask);
}

VALUE cBitmask;

void Init_Bitmask() {
cBitmask = rb_define_class("Bitmask", rb_cObject);
rb_define_singleton_method(cBitmask, "new", bm_new, 0);
rb_define_method(cBitmask, "add_bit", bm_add_bit, 1);
rb_define_method(cBitmask, "add_bits", bm_add_bits, 1);
rb_define_method(cBitmask, "value", bm_value, 0);
}


-------8<------ test_Bitmask.rb

require 'Bitmask'

bm = Bitmask.new
bm.add_bit(2)
bm.add_bit(4)
puts bm.value

bm = Bitmask.new
bm.add_bits([2, 4])
puts bm.value
 
T

Tim Hunter

Fredrik said:
You don't show how you defined 'add_values' or how you're calling it
from Ruby, but I think your initial array is in 'self' ...

Here is a more complete example of what I'm trying to do.
Here's an example from enum.c (Enumerable#reject)
with some printf() statements added.

I'm afraid I didn't understand how to translate your example into mine.

This is basically what I'm trying to do:

-------8<------ Bitmask.c
#include "ruby.h"

static VALUE
bm_new(VALUE class)
{
long* bitmask;
bitmask = ALLOC(long);
*bitmask = 0;
VALUE data = Data_Wrap_Struct(class, 0, free, bitmask);
return data;
}

static VALUE
bm_add_bit(VALUE self, VALUE bit)
{
long* bitmask;
int int_bit = NUM2INT(bit);
printf("in to add_bit: %s %s\n",
RSTRING(rb_inspect(self))->ptr,
RSTRING(rb_inspect(bit))->ptr);
Data_Get_Struct(self, long, bitmask);
*bitmask |= 1 << int_bit;
return self;
}

static VALUE
bm_add_bits(VALUE self, VALUE bits)
{
printf("in to add_bits: %s\n", RSTRING(rb_inspect(bits))->ptr);
rb_iterate(rb_each, bits, bm_add_bit, 1);
return self;
}

static VALUE
bm_value(VALUE self)
{
long* bitmask;
Data_Get_Struct(self, long, bitmask);
return INT2NUM(*bitmask);
}

VALUE cBitmask;

void Init_Bitmask() {
cBitmask = rb_define_class("Bitmask", rb_cObject);
rb_define_singleton_method(cBitmask, "new", bm_new, 0);
rb_define_method(cBitmask, "add_bit", bm_add_bit, 1);
rb_define_method(cBitmask, "add_bits", bm_add_bits, 1);
rb_define_method(cBitmask, "value", bm_value, 0);
}


-------8<------ test_Bitmask.rb

require 'Bitmask'

bm = Bitmask.new
bm.add_bit(2)
bm.add_bit(4)
puts bm.value

bm = Bitmask.new
bm.add_bits([2, 4])
puts bm.value

Hmmm...the Pickaxe says this about rb_iterate:
-
VALUE rb_iterate(VALUE (*method)(), VALUE args,
VALUE (*block)(), VALUE arg2);

Invokes `method' with argument `args' and block `block'. A yield from that
method will invoke `block' with the argument given to yield, and a second
argument `arg2'.

I understand that rb_iterate invokes `method' using the current self, that
is, the self that gets add_bits. Since you're passing rb_each, your class
needs to define an each method. That each method must yield and pass an
argument, which (normally) is the next member in some collection. Lastly,
the fourth arg, which becomes the 2nd argument to the block, must be a Ruby
VALUE. An integer 1 probably isn't what you want.

Overall it looks like rb_iterate isn't what you need. You want to call
add_bit for each element in the array, so I'd do something like this:

for (x = 0; x < RARRAY(bits)->len; x++)
{
VALUE bit = rb_ary_entry(bits, x);
(void) bm_add_bit(self, bit);
}

Or, if you want to allow for somebody subclassing your class and overriding
add_bit, replace the direct call to bm_add_bit with:

(void) rb_funcall(self, rb_intern("add_bit"), 1, bit);
 
T

ts

F> This is basically what I'm trying to do:

Well, in ruby

F> -------8<------ Bitmask.c
F> #include "ruby.h"

F> static VALUE
F> bm_new(VALUE class)
F> {
F> long* bitmask;
F> bitmask = ALLOC(long);
F> *bitmask = 0;
F> VALUE data = Data_Wrap_Struct(class, 0, free, bitmask);
F> return data;
F> }

bitmask = 0

F> static VALUE
F> bm_add_bit(VALUE self, VALUE bit)
F> {
F> long* bitmask;
F> int int_bit = NUM2INT(bit);
F> printf("in to add_bit: %s %s\n",
F> RSTRING(rb_inspect(self))->ptr,
F> RSTRING(rb_inspect(bit))->ptr);
F> Data_Get_Struct(self, long, bitmask);
F> *bitmask |= 1 << int_bit;
F> return self;
F> }

bitmask |= 1 << bit

F> static VALUE
F> bm_add_bits(VALUE self, VALUE bits)
F> {
F> printf("in to add_bits: %s\n", RSTRING(rb_inspect(bits))->ptr);
F> rb_iterate(rb_each, bits, bm_add_bit, 1);
F> return self;
F> }

bits.each {|b| bitmask |= 1 << b }

F> static VALUE
F> bm_value(VALUE self)
F> {
F> long* bitmask;
F> Data_Get_Struct(self, long, bitmask);
F> return INT2NUM(*bitmask);
F> }

bitmask



Guy Decoux
 
F

Fredrik Jagenheim

F> This is basically what I'm trying to do:

Well, in ruby
[snip solution in ruby]

I had to interface with a C-library that took, among other things, an
array of bitmaps as input. Otherwise, I would have done it in Ruby, of
course.

As for the solution, Tim was correct in that rb_iterate wasn't what I
neded. The for loop solved the problem I had.

Thanks everyone,
//Fredrik
 
T

ts

F> I had to interface with a C-library that took, among other things, an
F> array of bitmaps as input. Otherwise, I would have done it in Ruby, of
F> course.


svg% cat aa.c
#include "ruby.h"

static VALUE
bm_new(VALUE class)
{
long* bitmask;
bitmask = ALLOC(long);
*bitmask = 0;
VALUE data = Data_Wrap_Struct(class, 0, free, bitmask);
return data;
}

static VALUE
bm_add_bit(VALUE self, VALUE bit)
{
long* bitmask;
int int_bit = NUM2INT(bit);
printf("in to add_bit: %s %s\n",
RSTRING(rb_inspect(self))->ptr,
RSTRING(rb_inspect(bit))->ptr);
Data_Get_Struct(self, long, bitmask);
*bitmask |= 1 << int_bit;
return self;
}

static VALUE
bm_bit_add(VALUE bit, VALUE self)
{
return bm_add_bit(self, bit);
}

static VALUE
bm_add_bits(VALUE self, VALUE bits)
{
printf("in to add_bits: %s\n", RSTRING(rb_inspect(bits))->ptr);
rb_iterate(rb_each, bits, bm_bit_add, self);
return self;
}

static VALUE
bm_value(VALUE self)
{
long* bitmask;
Data_Get_Struct(self, long, bitmask);
return INT2NUM(*bitmask);
}

static VALUE cBitmask;

void Init_aa() {
cBitmask = rb_define_class("Bitmask", rb_cObject);
rb_define_singleton_method(cBitmask, "new", bm_new, 0);
rb_define_method(cBitmask, "add_bit", bm_add_bit, 1);
rb_define_method(cBitmask, "add_bits", bm_add_bits, 1);
rb_define_method(cBitmask, "value", bm_value, 0);
}
svg%

svg% cat b.rb
#!/usr/bin/ruby
require 'aa'

bm = Bitmask.new
bm.add_bit(2)
bm.add_bit(4)
puts bm.value

bm = Bitmask.new
bm.add_bits([2, 4])
puts bm.value

svg%

svg% b.rb
in to add_bit: #<Bitmask:0x40099cf0> 2
in to add_bit: #<Bitmask:0x40099cf0> 4
20
in to add_bits: [2, 4]
in to add_bit: #<Bitmask:0x40099c64> 2
in to add_bit: #<Bitmask:0x40099c64> 4
20
svg%


Guy Decoux
 
D

daz

Fredrik said:
ts said:
Well, in ruby
[snip solution in ruby]

I had to interface with a C-library that took, among other things, an
array of bitmaps as input. Otherwise, I would have done it in Ruby, of
course.

I assumed it was mainly for experimentation until you said that.

Your example showed the first mutable Fixnum object that I've seen
and you must have wondered why a simple task was so tough to get
right in an extension.

Have a look at the same thing implemented (for amusement only) as an
extension to the Integer class (Fixnum/Bignum's parent). Notice how
the input array is created from a sequence of parameters in both the
Ruby and C versions, so you call with (1,2,3) instead of ([1,2,3]).
(Method Bitmask#add_bits is defined with "-1" arguments.)

Method Bitmask#add_bit in Ruby is now:
bitmask | 1 << bit
not:
bitmask |= 1 << bit

which means that you would need bitmask = bitmask.add_bit(...) when
accumulating single results. Apart from this "new" problem, you
should find that things look simpler (e.g. no Bitmask.new).
As for the solution, Tim was correct in that rb_iterate wasn't what I
neded. The for loop solved the problem I had.

OK, I saw that, but you may be tempted to try another approach.
A similar loop to Tim's is still there.

It's a kind of "Hello bit-World" extension if anyone needs one :)
Thanks everyone,
//Fredrik

and thank you,

:daz


-------8<------ Bitmask.c

#include "ruby.h"

static ID id_lshift, id_or;

static VALUE
add_bit(VALUE obj, VALUE bit) /* OK with Fixnum or Bignum */
{
VALUE bit_or = rb_funcall(INT2FIX(1), id_lshift, 1, bit);

return rb_funcall(obj, id_or, 1, bit_or);
}

static VALUE
bm_add_bit(VALUE self, VALUE bit)
{
return add_bit(self, bit);
}

static VALUE
bm_add_bits(int argc, VALUE *argv, VALUE self)
{
VALUE arr_bits, result = self;
int x;

rb_scan_args(argc, argv, "0*", &arr_bits);

for (x = 0; x < RARRAY(arr_bits)->len; x++)
result = add_bit(result, RARRAY(arr_bits)->ptr[x]);

return result;
}

void Init_Bitmask() {
rb_define_method(rb_cInteger, "add_bit", bm_add_bit, 1);
rb_define_method(rb_cInteger, "add_bits", bm_add_bits, -1);

id_lshift = rb_intern("<<");
id_or = rb_intern("|");
}


/* E N D */


/***
###----- extconf.rb

require 'mkmf'
create_makefile('Bitmask')

# - to build ...
# make
# - then ...
# make install

###----- test_Bitmask.rb

require 'Bitmask'

class Integer
def add_rubits(*args) ### Ruby add_bits
res = self
args.each {|bx| res = res.add_bit(bx)}
res
end
end

show = lambda{ |res| puts 'value: 0x%03x [%08b]' % [res, res] }

bm = 0

show[ bm = bm.add_bit(2) ]
show[ bm = bm.add_bit(4) ]

puts '------------'
show[ 0.add_bits(2, 4) ]

puts '------------'
MSK1 = 0b11110011
bm = MSK1; show[ bm.add_bits( 31, 33) ]
bm = MSK1; show[ bm.add_rubits(31, 33) ] # Ruby
bit_arr = [31, 33]
bm = MSK1; show[ bm.add_bits(*bit_arr) ]

puts '------------'
MSK2 = 2**41
bm = MSK2; show[ bm.add_bits( 19, 3, 9, 17, 6, 18) ]
bm = MSK2; show[ bm.add_rubits(19, 3, 9, 17, 6, 18) ] # Ruby


***/
 

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

Forum statistics

Threads
474,149
Messages
2,570,843
Members
47,390
Latest member
RobertMart

Latest Threads

Top