0

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 にあるその定義で覆い隠しているようです。私は正しいですか?もしそうなら、どうすれば私のメソッドがルビーのものであり、その逆ではないことを変更できますか。

4

1 に答える 1

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

于 2012-05-06T18:11:46.763 に答える