山本です。 >> 数が違っても安全かというのとは少し違うのですが、 >> >> #include <stdio.h> >> >> static void hoge(int d) >> { >> printf("%d\n", d); >> } >> >> int main() >> { >> void (*p)(); >ANSI C的には、ここで宣言している関数のポインタの変数の定義は正しくない >というか、void (*p)(int); といった具合いに引数を取るなら取るで、その型 >も含めて宣言すべきです。 やっぱりそうなりますよね。C++ では削除された機能ですし、 http://david.tribble.com/text/cdiffs.htm によれば C99 でも deprecated となったようですし・・・void * と同じく、保証された動作ならそれはそれで 便利なのかもしれないと思ったのですけど(ruby.h でのコールバック関数の型は プロトタイプ化しておいて、variable.c 内部では引数なし関数ポインタで持つとか) >> プロトタイプがないときの仕様を調べればいいのか・・・ >挙動不定とかかもしれません。 とりあえず variable.c から引数のない関数ポインタを追放してみたのですが、 骨がおれました。今までのように機械的に変換するわけにはいかず、関数内部の 挙動も把握する必要があったので。 http://www.ccsnet.ne.jp/~ocean/ansi/ に追加した HAVE_STRONG_PROTOTYPES を削除する パッチ 003.diff に加えて、下のパッチでとりあえず動いているように見えます。 # 結局 union を使って K&R との差を補ったのと、コールバック関数を switch # に切り分けた部分がオブジェクト指向的に後退してるようで気に入りませんが、 # 型安全性の面では向上した・・・のかな?もっといい方法があるかもしれません。 # なお、io.c などコールバック関数側はそのままなので、まだ警告は出ます。 $ あと、どこかで関数ポインタの比較は遅延ロードなどの関係でできないという話を $ 聞いたので、その点はこのパッチにメリットがあるかもしれません。 Index: ruby.h =================================================================== --- ruby.h (revision 1) +++ ruby.h (working copy) @@ -493,8 +493,8 @@ void rb_extend_object _((VALUE,VALUE)); void rb_define_variable _((const char*,VALUE*)); -void rb_define_virtual_variable _((const char*,VALUE(*)(ANYARGS),void(*)(ANYARGS))); -void rb_define_hooked_variable _((const char*,VALUE*,VALUE(*)(ANYARGS),void(*)(ANYARGS))); +void rb_define_virtual_variable (const char*,VALUE(*)(ID),void(*)(VALUE,ID)); +void rb_define_hooked_variable (const char*,VALUE*,VALUE(*)(ID,VALUE*),void(*)(VALUE,ID,VALUE*)); void rb_define_readonly_variable _((const char*,VALUE*)); void rb_define_const _((VALUE,const char*,VALUE)); void rb_define_global_const _((const char*,VALUE)); Index: variable.c =================================================================== --- variable.c (revision 3) +++ variable.c (working copy) @@ -284,17 +284,28 @@ struct trace_var { int removed; - void (*func)(); + void (*func)(VALUE, VALUE); VALUE data; struct trace_var *next; }; +enum globval_variable_state { GVAR_UNDEF, GVAR_VALUE, GVAR_VIRTUAL, GVAR_HOOK }; + struct global_variable { - int counter; - void *data; - VALUE (*getter)(); - void (*setter)(); - void (*marker)(); + int counter; + enum global_variable_state state; + union { + VALUE val; /* GVER_VALUE */ + struct { /* GVER_VIRTUAL */ + VALUE (*getter)(ID id); + void (*setter)(VALUE val, ID id); + } virt; + struct { /* GVER_HOOK */ + VALUE *var; + VALUE (*getter)(ID id, VALUE *var); + void (*setter)(VALUE val, ID id, VALUE *var); + } hook; + } u; int block_trace; struct trace_var *trace; }; @@ -304,18 +315,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 +327,7 @@ 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->state = GVAR_UNDEF; var->block_trace = 0; var->trace = 0; st_add_direct(rb_global_tbl, id, (st_data_t)entry); @@ -341,68 +336,33 @@ } static VALUE -undef_getter(ID id) +hook_default_getter(ID id, VALUE *var) { - rb_warning("global variable `%s' not initialized", rb_id2name(id)); - - return Qnil; + if (!var) return Qnil; + return *var; } static void -undef_setter(VALUE val, ID id, void *data, struct global_variable *var) +hook_default_setter(VALUE val, ID id, VALUE *var) { - var->getter = val_getter; - var->setter = val_setter; - var->marker = val_marker; - - var->data = (void*)val; + *var = val; } static void -undef_marker(void) +hook_readonly_setter(VALUE val, ID id, VALUE *var) { + rb_name_error(id, "%s is a read-only variable", rb_id2name(id)); } static VALUE -val_getter(ID id, VALUE val) +virt_default_getter(ID id) { - return val; + rb_name_error(id, "%s is a write-only variable", rb_id2name(id)); } static void -val_setter(VALUE val, ID id, void *data, struct global_variable *var) +virt_default_setter(VALUE val, ID id) { - 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) -{ - *var = val; -} - -static void -var_marker(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 +372,15 @@ struct trace_var *trace; struct global_variable *var = entry->var; - (*var->marker)(var->data); + switch (var->state) { + case GVAR_VALUE: + rb_gc_mark_maybe(var->u.val); + break; + case GVAR_HOOK: + if (var->u.hook.var) rb_gc_mark_maybe(*var->u.hook.var); + break; + } + trace = var->trace; while (trace) { if (trace->data) rb_gc_mark_maybe(trace->data); @@ -446,17 +414,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->state = GVAR_HOOK; + gvar->u.hook.var = var; + gvar->u.hook.getter = getter?getter:hook_default_getter; + gvar->u.hook.setter = setter?setter:hook_default_setter; } void @@ -468,18 +436,22 @@ void rb_define_readonly_variable(const char *name, VALUE *var) { - rb_define_hooked_variable(name, var, 0, readonly_setter); + rb_define_hooked_variable(name, var, 0, hook_readonly_setter); } void rb_define_virtual_variable( const char *name, - VALUE (*getter) (/* ??? */), - void (*setter) (/* ??? */)) + VALUE (*getter) (ID), + void (*setter) (VALUE,ID)) { - 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->state = GVAR_VIRTUAL; + gvar->u.virt.getter = getter?getter:virt_default_getter; + gvar->u.virt.setter = setter?setter:virt_default_setter; } static void @@ -616,7 +588,21 @@ rb_gvar_get(struct global_entry *entry) { struct global_variable *var = entry->var; - return (*var->getter)(entry->id, var->data, var); + + switch (var->state) { + case GVAR_UNDEF: + rb_warning("global variable `%s' not initialized", rb_id2name(entry->id)); + return Qnil; + case GVAR_VALUE: + return var->u.val; + case GVAR_VIRTUAL: + return (*var->u.virt.getter)(entry->id); + case GVAR_HOOK: + return (*var->u.hook.getter)(entry->id, var->u.hook.var); + } + + rb_bug("should not reach here"); + return Qnil; /* dummy */ } struct trace_data { @@ -652,8 +638,21 @@ if (rb_safe_level() >= 4) rb_raise(rb_eSecurityError, "Insecure: can't change global variable value"); - (*var->setter)(val, entry->id, var->data, var); + switch (var->state) { + case GVAR_UNDEF: + var->state = GVAR_VALUE; + case GVAR_VALUE: + var->u.val = val; + break; + case GVAR_VIRTUAL: + (*var->u.virt.setter)(val, entry->id); + break; + case GVAR_HOOK: + (*var->u.hook.setter)(val, entry->id, var->u.hook.var); + break; + } + if (var->trace && !var->block_trace) { var->block_trace = 1; trace.trace = var->trace; @@ -684,7 +683,7 @@ VALUE rb_gvar_defined(struct global_entry *entry) { - if (entry->var->getter == undef_getter) return Qfalse; + if (entry->var->state == GVAR_UNDEF) return Qfalse; return Qtrue; }