初探mRuby,C程序嵌入以及自定义扩展

Author:zumikua Updated at:2014-03-01 16:04:24 UTC

博客荒废好长一段时间了,总感觉再不写点什么我说不定都提不起劲给VPS续费了……

mRuby已经release了1.0.0版本,虽然无论是文档还是社区建设mRuby都还有很长的一段路要走,但是对我来说尝鲜已经是足够了。

既然你会翻到这里估计我也就不用再费口舌介绍什么是mRuby了,那么下面我们就直接进入正题好了。

编译

Linux

之前在linux上编译过一次,不过说起来linux上编译起来真的有难度么?连configure都不需要直接装上bison、automake和ruby然后在代码根目录直接make就好。

Windows

曾经试过在Windows上编译一个Ruby出来,不过因为种种原因作罢了,这次编译mRuby也算是补回这个遗憾。

  1. 去GNUWIN32下载bison、make以及mingw32
  2. 安装一个版本的ruby
  3. 在环境变量中添加bison、make、gcc(mingw32提供)所在的路径,具体方法自己google
  4. 在mRuby源码的根目录下make
  5. Done

编译好了以后在build/host/bin/下提供了mruby、mirb和mrbc,分别对应ruby、irb以及rbc

mruby和mirb不用多说,mrbc好像是编译rb程序用的,我没用过就不多说了。

嵌入

正如mRuby名字所揭示的那样,她是一门专门用于嵌入(Embedded)的轻量级语言,类似于Lua。既然如此我们就首先来测试一下将Ruby嵌入到C程序中去。

#include<stdio.h>
#include<stdlib.h>
#include<mruby.h>
#include<mruby/compile.h>

int main(){
  char ruby_code[] = "puts 'Hello World'";
  mrb_state \*mrb = mrb_open();
  mrb_load_string(mrb,ruby_code);
  return 0;
}

嗯……好像还真是比Ruby简单不少……顺带一提之前我曾经试着在windows下将Ruby嵌入到C程序中去,可是使用RubyInstaller提供的名字叫1.9.1的lib总是会在运行的时候报段错误……(Ruby版本1.9.3),当然在Linux下没有任何问题就是了。

顺带一提,请自行设置include文件的搜索位置,以及需要链接的lib(libmruby.a,位于build\host\lib下),在此仅提供Code::Blocks自动生成的编译命令

mingw32-gcc.exe -Wall  -g    -IF:\mruby\mruby-1.0.0\include -c F:\mruby\main.c -o obj\Debug\main.o
mingw32-g++.exe -LF:\mruby\mruby-1.0.0\build\host\lib -o bin\Debug\mruby.exe obj\Debug\main.o   -lmingw32 F:\mruby\mruby-1.0.0\build\host\lib\libmruby.a 

好像同时指定了lib的搜索位置以及lib文件的绝对路径,嘛,不要在意

扩充

在C程序里单独嵌入一个Ruby命令没什么意思,我们来为其扩充一些我们自己定义的内容。

#include<stdio.h>
#include<stdlib.h>
#include<mruby.h>
#include<mruby/compile.h>
mrb_value foo_add(mrb_state\* mrb,mrb_value self){
  int a,b;
  mrb_get_args(mrb,"ii",&a,&b);
  return mrb_fixnum_value(a+b);
}
int main(){
  char ruby_code[] = "p Foo.new.add(1,2)";
  mrb_state \*mrb = mrb_open();
  struct RClass\* foo_class;
  foo_class = mrb_define_class(mrb,"Foo",mrb->object_class);
  mrb_define_method(mrb,foo_class,"add",foo_add,MRB_ARGS_REQ(2));
  mrb_load_string(mrb,ruby_code);
  return 0;
}

这样,我们就添加了一个名为Foo的类以及其一个实例方法add

参考代码,我们可以了解一下mrb定义方法的一般方式,foo_add是作为方法的具体实现函数,其返回类型以及参数类型都是固定的。而ruby传给该方法的参数则需要通过mrb_get_args(mrb,args...)获取。其中args表示各个参数的类型。具体参考此表:

string  mruby type     C type                 note

    o:      Object         [mrb_value]
    C:      class/module   [mrb_value]
    S:      String         [mrb_value]
    A:      Array          [mrb_value]
    H:      Hash           [mrb_value]
    s:      String         [char\*,int]            Receive two arguments.
    z:      String         [char\*]                NUL terminated string.
    a:      Array          [mrb_value\*,mrb_int]   Receive two arguments.
    f:      Float          [mrb_float]
    i:      Integer        [mrb_int]
    b:      Boolean        [mrb_bool]
    n:      Symbol         [mrb_sym]
    &:      Block          [mrb_value]
    \*:      rest argument  [mrb_value\*,int]       Receive the rest of the arguments as an array.
    |:      optional                              Next argument of '|' and later are optional.

\*来源于src/class.c内部的注释

需要指出的是,所有的ruby变量都是mrb_value类型, foo_add 函数的self参数指代instance自己。而对于mrb_define_class_method定义的类方法等self指代的是该类(Class类的instance)本身。

至于mrb_define_class以及mrb_define_method及其参数意义就不用我说了吧?稍微懂点英语就可以看懂了。

还有ruby中每个方法必须要有一个返回值,所以即使你没有返回值也要return mrb_nil_value();,而且函数必须返回mrb_value。(我一开始就直接想都没想用了void作为返回类型……)

很明显,这种方法只适合少量的零散的定义,而对于大量的,更加结构化的工程这种方法也有其局限性,而我们又常常会想要在Ruby类中储存一些持久化数据,但这又会牵扯到Ruby的垃圾回收(GC)工作机理,这一切我们会在后面讲解。

下一讲:

《mRuby:mrbgems以及在Ruby对象中储存struct》

关于重装ArchLinux及其他一些事情的吐槽

Author:zumikua Updated at:2014-02-25 09:24:25 UTC

最近投奔了ArchLinux的怀抱,wiki这种东西真是美好啊,尤其是对于我这种一点也不相信社区的人来说(等下wiki不也是社区写的么)。 说实在的我个人认为与其多少大牛蹲在一个论坛里每天帮新手回答问题不如写一篇像archlinux的那个beginners' Guide来的更方便。毕竟大牛是有限的,而问题是无穷的,把有限的大牛投入到无限的问题中去,大牛们都不是雷锋,或者说他们不能永远是雷锋。而且还有像我这样的急性子完全没耐心去社区等大牛回复的人存在。 能搞定ArchLinux的安装的确让人受益匪浅,比如我为了安archlinux就把zdcclient断线问题解决了……同时还附带学会了怎么用fdisk分区以及linux的“一切皆文件”的哲学。我个人觉得比那些安装比windows还方便的某些发行版好太多。 说起来zdcclient的修复也很有意思,本来因为游戏玩腻了想去Ubuntu研究一下到底是哪里出了问题,结果手贱去更新了那五百多个包,再然后就没有然后了,unity崩溃,重启后登陆只能对着一片白屏幕,正好我本来就因为ubuntu的软件源各种坑爹而蛋痛,这次索性就换了archlinux。 然后在windows下用wireshark抓包,大概搞清楚包结构之后checkout下来zdcclient的源码然后开始在windows下修改。修改完了再放到archlinux里编译。(这时候我还没装图形界面)某种意义上来说还真是大胆,在archlinux里没法抓包(tcpdump很难用啊),一旦逻辑上出错很难改,那样的话还得回到windows下面……不过我运气很好,一次就通过了(除去一些语法错误以及低级逻辑错误以外)。说起来编译完以后一下就成功运行这种感觉好像从我开始学OI到最近学ACM(虽然半途而废了)还是第一次。C语言我还是能写一点的嘛。 说起来在抓包的过程中发现ruby干这种事情不是一般的方便啊,标准库自带md5,数据可以用数组pack,下次有心情写个ruby版的zdcclient好了。 说起来改写zdcclient才用了不到半天的时间,但是自从zdcclient停止更新到现在快4年的时间没有一个人修复这个问题(或者说修复了没外传?),虽然也有我们应该算是第一届住进桂园(武大只有桂园是神州数码验证)的计院学生的原因,但是四年没有一个人遇到这个问题还真是让人觉得不可思议啊,这里可是大学,而且还是所“985大学”欸?连上网客户端都得让一个外校的大牛(Pentie大牛,他的文章在改写zdcclient方面给了我非常大的帮助)写?真的没问题么? ---- Update 其实是有修复的,在fork到了github上的那个版本已经修复了这个问题……当初的我怎么就那么傻逼没想到去试试这个比较新的版本呢……
Main