山本です。 >>varはvariable.cの中で使っているはずですが。いつのまにか直し >>ちゃったのかな? > >ああ、そういえば rb_define_virtual_variable の仕様がよくわからないまま、 >rb_define_virtual_variable で > >struct global_variable { > int counter; > void *data; > >の data にアクセスしているファイルもないし、デフォルトの挙動も getter が >Qfalse(0) を返すような挙動でよくわからなかったので、 >とりあえず引数なし関数ポインタを追放するとどんな感じになるか知るのを優先して >内部ストレージ (data) には触れない実装にしてしまいました。(virtual というぐらいだし) > ># virtual でも、内部ストレージを使うんでしょうか? 頭が型変換でスパゲッティになってましたが、だんだん整理がついてきました。 virtual でも data を使うことがありえますよね。これが意図された挙動に近いでしょうか? (virtual の getter がデフォルトで返すのは Qnil にしてしまいましたが・・・) Index: variable.c =================================================================== --- variable.c (revision 3) +++ variable.c (working copy) @@ -284,17 +284,17 @@ struct trace_var { int removed; - void (*func)(); + void (*func)(VALUE, VALUE); VALUE data; struct trace_var *next; }; struct global_variable { - int counter; - void *data; - VALUE (*getter)(); - void (*setter)(); - void (*marker)(); + int counter; + VALUE data; + int data_is_value; /* if true data is used as VALUE else used as (VALUE*) */ + VALUE (*getter)(ID id, VALUE *var); /* NULL if undefined variable */ + void (*setter)(VALUE val, ID id, VALUE *var); /* ditto */ int block_trace; struct trace_var *trace; }; @@ -304,18 +304,6 @@ ID id; }; -static VALUE undef_getter(ID id); -static void undef_setter(VALUE val, ID id, void *data, struct global_variable *var); -static void undef_marker(void); - -static VALUE val_getter(ID id, VALUE val); -static void val_setter(VALUE val, ID id, void *data, struct global_variable *var); -static void val_marker(VALUE data); - -static VALUE var_getter(ID id, VALUE *var); -static void var_setter(VALUE val, ID id, VALUE *var); -static void var_marker(VALUE *var); - struct global_entry* rb_global_entry(ID id) { @@ -328,11 +316,8 @@ entry->id = id; entry->var = var; var->counter = 1; - var->data = 0; - var->getter = undef_getter; - var->setter = undef_setter; - var->marker = undef_marker; - + var->getter = NULL; + var->setter = NULL; var->block_trace = 0; var->trace = 0; st_add_direct(rb_global_tbl, id, (st_data_t)entry); @@ -341,68 +326,20 @@ } static VALUE -undef_getter(ID id) +default_getter(ID id, VALUE* var) { - rb_warning("global variable `%s' not initialized", rb_id2name(id)); - - return Qnil; -} - -static void -undef_setter(VALUE val, ID id, void *data, struct global_variable *var) -{ - var->getter = val_getter; - var->setter = val_setter; - var->marker = val_marker; - - var->data = (void*)val; -} - -static void -undef_marker(void) -{ -} - -static VALUE -val_getter(ID id, VALUE val) -{ - return val; -} - -static void -val_setter(VALUE val, ID id, void *data, struct global_variable *var) -{ - var->data = (void*)val; -} - -static void -val_marker(VALUE data) -{ - if (data) rb_gc_mark_maybe(data); -} - -static VALUE -var_getter(ID id, VALUE *var) -{ - if (!var) return Qnil; return *var; } static void -var_setter(VALUE val, ID id, VALUE *var) +default_setter(VALUE val, ID id, VALUE *var) { *var = val; } static void -var_marker(VALUE *var) +readonly_setter(VALUE val, ID id, VALUE *var) { - if (var) rb_gc_mark_maybe(*var); -} - -static void -readonly_setter(VALUE val, ID id, void *var) -{ rb_name_error(id, "%s is a read-only variable", rb_id2name(id)); } @@ -412,7 +349,11 @@ struct trace_var *trace; struct global_variable *var = entry->var; - (*var->marker)(var->data); + if (var->data_is_value) + rb_gc_mark_maybe(var->data); + else if (var->data) + rb_gc_mark_maybe(*((VALUE *)var->data)); + trace = var->trace; while (trace) { if (trace->data) rb_gc_mark_maybe(trace->data); @@ -446,17 +387,17 @@ rb_define_hooked_variable( const char *name, VALUE *var, - VALUE (*getter) (/* ??? */), - void (*setter) (/* ??? */)) + VALUE (*getter) (ID,VALUE*), + void (*setter) (VALUE,ID,VALUE*)) { struct global_variable *gvar; ID id = global_id(name); gvar = rb_global_entry(id)->var; - gvar->data = (void*)var; - gvar->getter = getter?getter:var_getter; - gvar->setter = setter?setter:var_setter; - gvar->marker = var_marker; + gvar->data = (VALUE)var; + gvar->data_is_value = Qfalse; + gvar->getter = getter?getter:default_getter; + gvar->setter = setter?setter:default_setter; } void @@ -474,12 +415,17 @@ void rb_define_virtual_variable( const char *name, - VALUE (*getter) (/* ??? */), - void (*setter) (/* ??? */)) + VALUE (*getter) (ID,VALUE*), + void (*setter) (VALUE,ID,VALUE*)) { - if (!getter) getter = val_getter; - if (!setter) setter = readonly_setter; - rb_define_hooked_variable(name, 0, getter, setter); + struct global_variable *gvar; + ID id = global_id(name); + + gvar = rb_global_entry(id)->var; + gvar->data = Qnil; + gvar->data_is_value = Qtrue; + gvar->getter = getter?getter:default_getter; + gvar->setter = setter?setter:readonly_setter; } static void @@ -616,7 +562,17 @@ rb_gvar_get(struct global_entry *entry) { struct global_variable *var = entry->var; - return (*var->getter)(entry->id, var->data, var); + + if (!var->getter) { + rb_warning("global variable `%s' not initialized", rb_id2name(entry->id)); + return Qnil; + } + else if (var->data_is_value) { + return (*var->getter)(entry->id, &var->data); + } + else { + return (*var->getter)(entry->id, (VALUE *)var->data); + } } struct trace_data { @@ -652,8 +608,20 @@ if (rb_safe_level() >= 4) rb_raise(rb_eSecurityError, "Insecure: can't change global variable value"); - (*var->setter)(val, entry->id, var->data, var); + if (!var->getter) { + var->getter = default_getter; + var->setter = default_setter; + var->data = val; + var->data_is_value = Qtrue; + } + else if (var->data_is_value) { + (*var->setter)(val, entry->id, &var->data); + } + else { + (*var->setter)(val, entry->id, (VALUE *)var->data); + } + if (var->trace && !var->block_trace) { var->block_trace = 1; trace.trace = var->trace; @@ -684,7 +652,7 @@ VALUE rb_gvar_defined(struct global_entry *entry) { - if (entry->var->getter == undef_getter) return Qfalse; + if (!entry->var->getter) return Qfalse; return Qtrue; }