c - 如何从 C 结构创建 Ruby 对象
问题描述
我正在尝试使用 C API 制作一个 Ruby 模块。我必须承认,我无法完全理解它的在线文档,但我正在尝试使用来自另一个类方法的 C 结构的数据返回一个 Ruby 对象(如果这没有意义,抱歉)。这是我的问题的一个例子:
例子.c
#include "ruby.h"
#include "extconf.h"
typedef struct example1_t
{
int x;
} example1_t;
typedef struct example2_t
{
char *name;
} example2_t;
void example1_free(example1_t *e1);
void example2_free(example2_t *e2);
static VALUE rb_example1_alloc(VALUE klass)
{
return Data_Wrap_Struct(klass, NULL, example1_free, ruby_xmalloc(sizeof(example1_t)));
}
static VALUE rb_example1_init(VALUE self, VALUE x)
{
example1_t *e1;
Check_Type(x, T_FIXNUM);
Data_Get_Struct(self, example1_t, e1);
e1->x = NUM2INT(x);
return self;
}
static VALUE rb_example1_x(VALUE self)
{
example1_t *e1;
Data_Get_Struct(self, example1_t, e1);
return INT2NUM(e1->x);
}
static VALUE rb_example2_alloc(VALUE klass)
{
return Data_Wrap_Struct(klass, NULL, example2_free, ruby_xmalloc(sizeof(example2_t)));
}
static VALUE rb_example2_init(VALUE self, VALUE s)
{
example2_t *e2;
Check_Type(s, T_STRING);
Data_Get_Struct(self, example2_t, e2);
e2->name = (char*)malloc(RSTRING_LEN(s) + 1);
memcpy(e2->name, StringValuePtr(s), RSTRING_LEN(s) + 1);
return self;
}
static VALUE rb_example2_name(VALUE self)
{
example2_t *e2;
Data_Get_Struct(self, example2_t, e2);
return rb_str_new_cstr(e2->name);
}
static VALUE rb_example2_name_len(VALUE self)
{
example1_t *len;
example2_t *e2;
Data_Get_Struct(self, example2_t, e2);
len->x = strlen(e2->name);
/*
How do I make a new Example1 Ruby Class from the "len"
structure and return it with the length of e2->name
assigned to len->x?
*/
return it?
}
void Init_example()
{
VALUE mod = rb_define_module("Example");
VALUE example1_class = rb_define_class_under(mod, "Example1", rb_cObject);
VALUE example2_class = rb_define_class_under(mod, "Example2", rb_cObject);
rb_define_alloc_func(example1_class, rb_example1_alloc);
rb_define_alloc_func(example2_class, rb_example2_alloc);
rb_define_method(example1_class, "initialize", rb_example1_init, 1);
rb_define_method(example1_class, "x", rb_example1_x, 0);
rb_define_method(example2_class, "initialize", rb_example2_init, 1);
rb_define_method(example2_class, "name", rb_example2_name, 0);
rb_define_method(example2_class, "name_len", rb_example2_name_len, 0);
}
void example1_free(example1_t *e1)
{
memset(e1, 0, sizeof(example1_t));
}
void example2_free(example2_t *e2)
{
memset(e2, 0, sizeof(example2_t));
}
正如您在 中看到的rb_example2_name_len
,我想创建一个Example1
类并从Example2
方法中返回它。我怎么能做到这一点?
任何帮助深表感谢。
解决方案
您可以使用它rb_class_new_instance
来创建新对象。
您将需要VALUE
表示类对象。获得它的一种方法是将其存储在全局或静态变量中,然后在 init 函数中对其进行初始化,而不仅仅是使用局部变量:
static VALUE example1_class;
//...
void Init_example()
{
//...
example1_class = rb_define_class_under(mod, "Example1", rb_cObject);
//...
}
您还需要将参数转换为 Ruby 形式。rb_class_new_instance
需要一个数组VALUES
。
static VALUE rb_example2_name_len(VALUE self)
{
example2_t *e2;
Data_Get_Struct(self, example2_t, e2);
// Format arguments as array of VALUES
VALUE args[1];
args[0] = INT2NUM((int)strlen(e2->name));
// args are length of array, pointer to array and class you are
// creating an instance of. example1_class is available here because
// we made it a static variable.
VALUE e1 = rb_class_new_instance(1, args, example1_class);
return e1;
}
制作静态的另一种方法example1_class
是使用rb_const_get
. 在这种情况下,您还必须获取包含模块:
VALUE mExample = rb_const_get(rb_cObject, rb_intern("Example"));
VALUE cExample1 = rb_const_get(mExample, rb_intern("Example1"));
VALUE e1 = rb_class_new_instance(1, args, cExample1);
rb_class_new_instance
基本上只是调用你的分配函数,然后是你的初始化程序,所以你可以自己在里面重现该代码rb_example2_name_len
:
example1_t *e1_struct = ruby_xmalloc(sizeof(example1_t));
e1_struct->x = (int)strlen(e2->name);
VALUE e1 = Data_Wrap_Struct(example1_class, NULL, example1_free, e1_struct);
return e1;
这将避免需要将数据转换为 ruby 格式,以便立即将其转换回来,但我认为您不会获得太多收益,并且使用rb_class_new_instance
可能更清晰。
您还应该知道Data_Wrap_Struct
struct 宏已被弃用:
旧的(非类型化的)Data_XXX 宏系列已被弃用。在 Ruby 的未来版本中,旧的宏可能无法工作。
为了简单起见,我在这里使用了它们,但您可能想尝试使用较新的TypedData_
宏。
推荐阅读
- python - 删除空白行后如何获取更新的csv文件?
- python - 我想通过 Boost.python 用 OpenCV 编译一个 C++ 程序以在 python 脚本中使用
- snowflake-cloud-data-platform - 雪花。大规模更新行/微分区处理
- amazon-web-services - AWS Lambda 函数:无法获取秘密 arn:aws:secretsmanager
- c# - 无法在 C# .Net Framework 中调用带有参数的 HttpGet 方法
- android - 无法到达 EditText 中的 EditorAction
- python - 使用 plotly scatter matrix API 添加图例?
- javascript - 在Javascript中将数组推入多维数组
- raspberry-pi - 如何在 Motion 中的 on_event_end 后暂停
- django - Celery-Django 博客 Grok 模式