rb_hash_aref question: symbols vs strings

D

Daniel Berger

Hi all,

I'm writing an extension, and I'm having a little trouble with using a
hash for keyword arguments. I want to allow a API like this:

Foo.test(
:bar => "hello",
'baz' => "world"
)

In the extension to get the key value I do this:

VALUE rbBar = rb_hash_aref(myHash,rb_str_new2("bar"));

If the key "bar" is a string, that works fine. However, that returns
nil (not a symbol) if "bar" is a symbol. I know I can do rb_iterate
and I can detect if hash keys are symbols, but I don't know how to
permanately change the key back to a string, i.e. it appears to pass a
copy of the hash, rather than a reference.

// Iterate over hash. Assume that rb_sym2str() works properly.
static VALUE parse_hash(VALUE array, VALUE class)
{
VALUE key, tkey, val;

key = rb_ary_entry(array, 0); // Get key
val = rb_ary_entry(array, 1); // Get value

if(TYPE(key) == T_SYMBOL){
key = rb_sym2str(key); // Convert T_SYMBOL to T_STRING
printf("Key is now: %s\n",STR2CSTR(key)); // Verify string
rb_ary_store(array,0,key); // Doesn't work
}

return array;
}

Any ideas?

Regards,

Dan
 
N

nobu.nokada

Hi,

At Fri, 28 Nov 2003 11:32:08 +0900,
Daniel said:
If the key "bar" is a string, that works fine. However, that returns
nil (not a symbol) if "bar" is a symbol. I know I can do rb_iterate
and I can detect if hash keys are symbols, but I don't know how to
permanately change the key back to a string, i.e. it appears to pass a
copy of the hash, rather than a reference.

Do you use Hash#each_pair or similar? They just ignore
returned values from yielded blocks.
// Iterate over hash. Assume that rb_sym2str() works properly.

No such function. Instead, you can use rb_to_id() vice versa.

===File foo.c===============================================
#include <ruby.h>

enum {
Foo_test_bar,
Foo_test_baz,
Foo_test_MAX
};

typedef VALUE Foo_test_options[Foo_test_MAX];

static ID id_Foo_test[Foo_test_MAX];

static VALUE
parse_hash(VALUE array, Foo_test_options opt)
{
ID key = rb_to_id(RARRAY(array)->ptr[0]);
VALUE val = RARRAY(array)->ptr[1];
int i;

for (i = 0; i < Foo_test_MAX; ++i) {
if (key == id_Foo_test) {
opt = val;
return Qnil;
}
}
rb_raise(rb_eArgError, "unknown option - %s", rb_id2name(key));
return Qnil; /* not reached */
}

static VALUE
foo_s_test(VALUE self, VALUE args)
{
Foo_test_options opt;
VALUE a[3];
int i;

Check_Type(args, T_HASH);
for (i = 0; i < Foo_test_MAX; ++i) {
opt = Qundef;
}
rb_iterate(rb_each, args, parse_hash, (VALUE)opt);
a[0] = rb_str_new2("%p=%p\n");
for (i = 0; i < Foo_test_MAX; ++i) {
if (opt != Qundef) {
a[1] = ID2SYM(id_Foo_test);
a[2] = opt;
rb_io_printf(3, a, rb_stdout);
}
}
return self;
}

void
Init_foo(void)
{
VALUE foo = rb_define_class("Foo", rb_cObject);
rb_define_singleton_method(foo, "test", foo_s_test, 1);
id_Foo_test[Foo_test_bar] = rb_intern("bar");
id_Foo_test[Foo_test_baz] = rb_intern("baz");
}
============================================================

You can use struct instead of array.
 
P

Park Heesob

Hi,

Hi all,

I'm writing an extension, and I'm having a little trouble with using a
hash for keyword arguments. I want to allow a API like this:

Foo.test(
:bar => "hello",
'baz' => "world"
)

In the extension to get the key value I do this:

VALUE rbBar = rb_hash_aref(myHash,rb_str_new2("bar"));

If the key "bar" is a string, that works fine. However, that returns
nil (not a symbol) if "bar" is a symbol. I know I can do rb_iterate
and I can detect if hash keys are symbols, but I don't know how to
permanately change the key back to a string, i.e. it appears to pass a
copy of the hash, rather than a reference.

// Iterate over hash. Assume that rb_sym2str() works properly.
static VALUE parse_hash(VALUE array, VALUE class)
{
VALUE key, tkey, val;

key = rb_ary_entry(array, 0); // Get key
val = rb_ary_entry(array, 1); // Get value

if(TYPE(key) == T_SYMBOL){
key = rb_sym2str(key); // Convert T_SYMBOL to T_STRING
printf("Key is now: %s\n",STR2CSTR(key)); // Verify string
rb_ary_store(array,0,key); // Doesn't work
}

return array;
}

Any ideas?

Regards,

Dan
Here is my solution:

hash = Foo.test(
:bar => "hello",
'baz' => "world"
)

static VALUE
test(VALUE klass,VALUE myHash)
{

int i;
VALUE ary = rb_funcall(myHash,rb_intern("keys"),0);

for(i=0;i<RARRAY(ary)->len;i++)
{
if(TYPE(RARRAY(ary)->ptr)==T_STRING)
printf("key = %s, val = %s\n",
STR2CSTR(RARRAY(ary)->ptr),
STR2CSTR(rb_hash_aref(myHash,RARRAY(ary)->ptr)));
if(TYPE(RARRAY(ary)->ptr)==T_SYMBOL)
printf("key = %s, val = %s\n",

STR2CSTR(rb_funcall(RARRAY(ary)->ptr,rb_intern("inspect"),0)),
STR2CSTR(rb_hash_aref(myHash,RARRAY(ary)->ptr)));
}

rb_hash_aset(myHash, rb_str_new2("baz"), rb_str_new2("aaa"));
rb_hash_aset(myHash, rb_eval_string(":bar"), rb_str_new2("bbb"));

for(i=0;i<RARRAY(ary)->len;i++)
{
if(TYPE(RARRAY(ary)->ptr)==T_STRING)
printf("key = %s, val = %s\n",
STR2CSTR(RARRAY(ary)->ptr),
STR2CSTR(rb_hash_aref(myHash,RARRAY(ary)->ptr)));
if(TYPE(RARRAY(ary)->ptr)==T_SYMBOL)
printf("key = %s, val = %s\n",

STR2CSTR(rb_funcall(RARRAY(ary)->ptr,rb_intern("inspect"),0)),
STR2CSTR(rb_hash_aref(myHash,RARRAY(ary)->ptr)));
}

return myHash;
}

Regards,

Park Heesob
 

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,967
Messages
2,570,148
Members
46,694
Latest member
LetaCadwal

Latest Threads

Top