2012-05-06 1 views
0

c에서 rb_function (예 : rb_ivar_get)을 재정의하는 방법을 알아 내는데 문제가 있습니다.루비 C 확장의 rb_ 함수를 어떻게 덮어 씁니까?

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 

내 생각은 루비 링커 내을 베일로 덮기되어 있습니다

#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; 
} 

그리고 다음과 같은 시험 : 나는 다음과 같은 코드가 있습니다 variables.c에 정의 된 rb_ivar_get, rb_attr_get 및 rb_ivar_set의 정의. 내가 맞습니까? 그렇다면 어떻게 내 방법이 루비의 베일이 아닌 다른 방법으로 바꿀 수 있습니까?

답변

2

추가로 .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을 편집하십시오.

+0

나를 위해,이 확장 기능을 추가하는 주된 요점은'@ivar = "some value"호출에 직접 수정 사항을 추적 할 수 있기 때문입니다. 그래서 당신은 소스를 편집하고 다시 컴파일해야한다는 것을 말하고 있습니다. 다른 방법은 없습니다. – tophat

+0

내가 아는 한, 안돼. –

+0

에디션보기. –