如何从C扩展中打印值?

时间:2015-02-19 13:50:37

标签: c ruby debugging ruby-c-extension

每个Ruby对象在C中都是VALUE类型。如何以可读的方式打印它?

欢迎任何有关调试Ruby C扩展的其他提示。

3 个答案:

答案 0 :(得分:5)

您可以使用C函数p在Ruby对象上调用rb_p。例如:

VALUE empty_array = rb_ary_new();
rb_p(empty_array); // prints out "[]"

答案 1 :(得分:2)

这是我想出的:

static void d(VALUE v) {
    ID sym_puts = rb_intern("puts");
    ID sym_inspect = rb_intern("inspect");
    rb_funcall(rb_mKernel, sym_puts, 1,
        rb_funcall(v, sym_inspect, 0));
}

将其放在C文件中,您可以像这样输出VALUE

VALUE v;
d(v);

我从this article借用了这个想法。

答案 2 :(得分:0)

我在Visual Studio中找到了一种使用Natvis文件的有趣方法。

我已经在Ruby C API上创建了C ++包装器对象 - 这给了我更多的类型安全性,语法变得更像编写实际的Ruby。

我不会发布整个代码 - 太久了,我计划最终开源。

但它的要点是:

class Object
{
public:
  Object(VALUE value) : value_(value)
  {
    assert(NIL_P(value_) || kind_of(rb_cObject));
  }

  operator VALUE() const
  {
    return value_;
  }
  // [More code] ...
}

然后让我们以String类为例:

class String : public Object
{
public:
  String() : Object(GetVALUE("")) {}
  String(VALUE value) : Object(value)
  {
    CheckTypeOfOrNil(value_, String::klass());
  }
  String(std::string value) : Object( GetVALUE(value.c_str()) ) {}
  String(const char* value) : Object( GetVALUE(value) ) {}


  operator std::string()
  {
    return StringValueCStr(value_);
  }

  operator std::string() const
  {
    return operator std::string();
  }

  static VALUE klass()
  {
    return rb_cString;
  }

  // String.empty?
  bool empty()
  {
    return length() == 0;
  }

  size_t length() const
  {
    return static_cast<size_t>(RSTRING_LEN(value_));
  }

  size_t size() const
  {
    return length();
  };

};

所以 - 我的包装器确保检查它们包装的VALUE是预期类型还是Nil

然后我为Visual Studio编写了一些natvis文件,当我逐步完成代码时,它将为我的包装器对象提供一些实时调试信息:

<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">

<Type Name="SUbD::ruby::String">
  <DisplayString Condition="value_ == RUBY_Qnil">Ruby String: Nil</DisplayString>
  <DisplayString Condition="value_ != RUBY_Qnil">Ruby String: {((struct RString*)value_)->as.heap.ptr,s}</DisplayString>
  <StringView Condition="value_ != RUBY_Qnil">((struct RString*)value_)->as.heap.ptr,s</StringView>
  <Expand>
    <Item Name="[VALUE]">value_</Item>
    <Item Name="[size]" Condition="value_ != RUBY_Qnil">((struct RString*)value_)->as.heap.len</Item>
    <Item Name="[string]" Condition="value_ != RUBY_Qnil">((struct RString*)value_)->as.heap.ptr</Item>
    <Item Name="[capacity]" Condition="value_ != RUBY_Qnil">((struct RString*)value_)->as.heap.aux.capa</Item>
  </Expand>
</Type>

</AutoVisualizer>

请注意,这些都是Ruby 2.0的确切内部结构的硬编码。这在Ruby 1.8或1.9中不起作用 - 尚未尝试使用2.1或2.2。此外,可能存在String如何存储的突变,我还没有添加。 (短字符串可以存储为立即值。) (实际上 - 上面发布的natvis仅适用于32位 - 目前不是64位。)

但是一旦设置完毕,我就可以逐步执行代码并检查Ruby字符串,就像它们std::string一样:

Visual Studio displaying information about Ruby String class

全力以赴并非易事。如果您在我的natvis中注意到我的RUBY_Qnil引用 - 除非我将这段调试代码添加到我的项目中,否则它们将无效:

// Required in order to make them available to natvis files in Visual Studio.
#ifdef _DEBUG
const auto DEBUG_RUBY_Qnil              = RUBY_Qnil;
const auto DEBUG_RUBY_FIXNUM_FLAG       = RUBY_FIXNUM_FLAG;
const auto DEBUG_RUBY_T_MASK            = RUBY_T_MASK;
const auto DEBUG_RUBY_T_FLOAT           = RUBY_T_FLOAT;
const auto DEBUG_RARRAY_EMBED_FLAG      = RARRAY_EMBED_FLAG;
const auto DEBUG_RARRAY_EMBED_LEN_SHIFT = RARRAY_EMBED_LEN_SHIFT;
const auto DEBUG_RARRAY_EMBED_LEN_MASK  = RARRAY_EMBED_LEN_MASK;
#endif

不幸的是,你不能在natvis定义中使用宏,所以我必须通过检查Ruby源本身手动将其中的许多扩展到natvis文件中。 (Ruby Cross Reference在这里有很大的帮助:http://rxr.whitequark.org/mri/ident?v=2.0.0-p247

它仍然是WIP,但它已经为我节省了大量的麻烦。最后我想在GitHub上提取调试设置:https://github.com/thomthom(如果你有兴趣,请关注该帐户。)