![](/img/trans.png)
[英]In a Ruby C Extension, is the “rb_…” C functions the monkey patched version, or the original?
[英]How does one overwrite a rb_ function in a ruby c extension?
我無法弄清楚如何在c中覆蓋rb_函數(如rb_ivar_get)。 我有以下代碼:
#include "ruby.h"
void Init_metaobject();
VALUE meta_cObject = Qnil;
VALUE meta_ivar_get(VALUE obj, VALUE mId, VALUE mWarn);
VALUE meta_ivar_set(VALUE obj, VALUE mId, VALUE val);
void Init_metaobject() {
meta_cObject = rb_define_class("MetaObject", rb_cObject);
rb_define_method(meta_cObject, "meta_ivar_get", meta_ivar_get, 2);
rb_define_method(meta_cObject, "meta_ivar_set", meta_ivar_set, 2);
}
VALUE
rb_ivar_get(obj, id)
VALUE obj;
ID id;
{
return meta_ivar_get(obj, ID2SYM(id), Qtrue);
}
VALUE
rb_attr_get(obj, id)
VALUE obj;
ID id;
{
return meta_ivar_get(obj, ID2SYM(id), Qfalse);
}
VALUE
rb_ivar_set(obj, id, val)
VALUE obj;
ID id;
VALUE val;
{
return meta_ivar_set(obj, ID2SYM(id), val);
}
VALUE
meta_ivar_get(obj, mId, mWarn)
VALUE obj;
VALUE mId;
VALUE mWarn;
{
VALUE val;
ID id = rb_to_id(id);
int warn = RTEST(warn);
switch (TYPE(obj)) {
case T_OBJECT:
case T_CLASS:
case T_MODULE:
if (ROBJECT(obj)->iv_tbl && st_lookup(ROBJECT(obj)->iv_tbl, id, &val))
return val;
break;
default:
if (FL_TEST(obj, FL_EXIVAR) || rb_special_const_p(obj))
return generic_ivar_get(obj, id, warn);
break;
}
if (warn) {
rb_warning("instance variable %s not initialized", rb_id2name(id));
}
return Qnil;
}
VALUE
meta_ivar_set(obj, mId, val)
VALUE obj;
VALUE mId;
VALUE val;
{
ID id = rb_to_id(mId);
if (!OBJ_TAINTED(obj) && rb_safe_level() >= 4)
rb_raise(rb_eSecurityError, "Insecure: can't modify instance variable");
if (OBJ_FROZEN(obj)) rb_error_frozen("object");
switch (TYPE(obj)) {
case T_OBJECT:
case T_CLASS:
case T_MODULE:
if (!ROBJECT(obj)->iv_tbl) ROBJECT(obj)->iv_tbl = st_init_numtable();
st_insert(ROBJECT(obj)->iv_tbl, id, val);
break;
default:
generic_ivar_set(obj, id, val);
break;
}
return val;
}
並進行以下測試:
require 'metaobject'
class Tracker < MetaObject
attr_accessor :ivar
def initialize
@ivar = nil
end
def meta_ivar_get(symbol, warn)
puts "Instance variable, #{symbol}, retrieved"
super(symbol, warn)
end
def meta_ivar_set(symbol, obj)
puts "Instance variable, #{symbol}, changed to #{obj.inspect}"
super(symbol, obj)
end
end
obj = Tracker.new
obj.ivar = "Modified"
puts obj.ivar
其輸出僅為:
Modified
我的想法是ruby鏈接器隱藏了我對rb_ivar_get,rb_attr_get和rb_ivar_set的定義,其定義在variables.c中找到。 我對嗎? 如果是這樣,我怎么能改變一些我的方法面紗紅寶石而不是相反的方式。
你不能用額外的.so文件來做。 編輯內部Ruby函數的唯一方法是直接更改它。 轉到variable.c
,編輯它並重新編譯整個解釋器。 相反,您可以覆蓋attr_accessor
。
編輯
set_trace_func
另一個解決方案。 這是非常慢的,我不厚,這是正確的方法。 無論如何,這是它:
$instance_variables_table = {}
$instance_variable_created_proc = proc do |var, value|
puts "Instance variable #{var} created with #{value.inspect}."
end
$instance_variable_changed_proc = proc do |var, new, old|
puts "Instance variable #{var} changed from #{old.inspect} to #{new.inspect}."
end
set_trace_func(proc {|type, file, line, func, binding, mod|
unless type == "call"
eval("instance_variables", binding).each do |iv|
value = eval("instance_variable_get(:#{iv})", binding)
if $instance_variables_table.has_key? iv
if $instance_variables_table[iv] != value
new = value
old = $instance_variables_table[iv]
$instance_variable_changed_proc[iv, new, old]
end
else
$instance_variable_created_proc[iv, value]
end
end
end
$instance_variables_table = {}
eval("instance_variables", binding).each do |iv|
$instance_variables_table[iv] = eval("instance_variable_get(:#{iv})", binding)
end
})
測試代碼:
class A
def initialize
@test = 1
@test = 2
end
def a
@test = 3
end
end
A.new.a
輸出:
Instance variable @test created with 1.
Instance variable @test changed from 1 to 2.
Instance variable @test changed from 2 to 3.
我不確定它是否適用於所有情況或是否可以簡化。 如果要在實際應用程序中執行此操作,請編輯variable.c
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.