我目前正在尝试学习 Nim(它进展缓慢 - 不能投入太多时间)。另一方面,为了获得一些工作代码,我想对我正在使用 ruby 开发的 Nim 应用程序的部分进行原型制作。
由于 mruby 允许在 C 应用程序中嵌入 ruby 子集,并且由于 nim 允许将任意 C 代码编译为函数,所以感觉这应该相对简单。有人做过吗?
我特别在寻找使用 Nim 的时髦宏功能分解成内联 ruby 代码的方法。我要自己尝试,但我认为肯定有人已经尝试过和/或提出比我目前的学习状态更优雅的解决方案:)
https://github.com/micklat/NimBorg
这是一个目标有点相似的项目。目前它针对的是 python 和 lua,但使用相同的技术与 Ruby 交互应该不会太难。
Nim 中有几个功能可以帮助以流利的方式与外语进行交互:
1) 使用 Nim 的点运算符从 Nim 调用 Ruby
这些有点像method_missing
Ruby 中的。你可以定义一个像RubyValue
Nim 这样的类型,它有点运算符,可以将任何表达式转换foo.bar
为foo.bar(baz)
适当的 Ruby 方法调用。参数可以传递给一个泛型函数toRubyValue
,可以为各种 Nim 和 C 类型重载,以自动将它们转换为正确的 Ruby 类型。
2) 从 Ruby 调用 Nim
在大多数脚本语言中,有一种注册外来类型的方法,通常在特定数据结构中描述,每个导出的类型必须填充一次。您可以使用一些通用编程和 Nim 的.global.
vars 来自动创建和缓存通过点运算符传递给 Ruby 的每种类型所需的数据结构。将有一个getRubyTypeDesc(T: typedesc)
可能依赖的通用过程typeinfo
,typetraits
或者用户提供的一些重载过程,定义必须为该类型导出的内容。
现在,如果你真的想依赖 mruby(例如因为你有使用它的经验),你可以考虑使用.emit。pragma直接输出 mruby 代码。然后,您可以要求 Nim 编译器仅生成源代码,您将在第二步中对其进行编译,或者您可以更改编译器可执行文件,Nim 在编译项目时将调用它(这在上面链接的同一部分中进行了解释)。
这是我迄今为止发现的。
从 mruby 执行中获取返回值并不像我想象的那么容易。也就是说,经过多次试验和错误,这是我发现执行一些 mruby 代码的最简单方法:
const mrb_cc_flags = "-v -I/mruby_1.2.0_path/include/ -L/mruby_1.2.0_path/build/host/lib/"
const mrb_linker_flags = "-v"
const mrb_obj = "/mruby_1.2.0_path/build/host/lib/libmruby.a"
{. passC: mrb_cc_flags, passL: mrb_linker_flags, link: mrb_obj .}
{.emit: """
#include <mruby.h>
#include <mruby/string.h>
""".}
proc ruby_raw(str:cstring):cstring =
{.emit: """
mrb_state *mrb = mrb_open();
if (!mrb) { printf("ERROR: couldn't init mruby\n"); exit(0); }
mrb_load_string(mrb, `str`);
`result` = mrb_str_to_cstr(mrb, mrb_funcall(mrb, mrb_top_self(mrb), "test_func", 0));
mrb_close(mrb);
""".}
proc ruby*(str:string):string =
echo ruby_raw("def test_func\n" & str & "\nend")
"done"
let resp = ruby """
puts 'this was a puts from within ruby'
"this is the response"
"""
echo(resp)
我很确定您应该能够在配置良好的环境中省略文件开头的一些编译器标志,例如通过正确设置 LD_LIBRARY_PATH (尤其是因为这会使代码更具可移植性)
到目前为止我遇到的一些问题:
我被迫使用 mrb_funcall ,因为出于某种原因,clang 似乎认为 mrb_load_string 函数返回一个int
,尽管我可以找到所有 c 代码和文档以及网上的几个人另有说法:
error: initializing 'mrb_value' (aka 'struct mrb_value') with an expression of incompatible type 'int'
mrb_value mrb_out = mrb_load_string(mrb, str);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~
需要 mruby/string.h 标头mrb_str_to_cstr
,否则会出现段错误。RSTRING_PTR 似乎也可以正常工作(至少在没有 string.h 的情况下会给出一个明显的错误),但如果你将它写成上面的单行代码,它将执行该函数两次。
我将继续前进,写一些更惯用的 nim,但这已经完成了我现在所需要的。