C++程序中嵌入Ruby脚本系统

什么是Ruby?
    Ruby,一种为简单快捷面向对象编程(面向对象程序设计)而创的脚本语言,由日本人松本行弘(まつもとゆきひろ,英译:Yukihiro Matsumoto,外号matz)开发,遵守GPL协议和Ruby License。Ruby的作者认为Ruby > (Smalltalk + Perl) / 2,表示Ruby是一个语法像Smalltalk一样完全面向对象、脚本执行、又有Perl强大的文字处理功能的编程语言。

什么是SWIG?
    SWIG(Simplified Wrapper and Interface Generator)是个帮助使用C或者C++编写的软件能与其它各种高级编程语言进行嵌入联接的开发工具。SWIG能应用于各种不同类型的语言包括常用脚本编译语言例如Perl, PHP, Python, Tcl, Ruby and PHP。
  简单来说,主要用于导出C/C++程序库给脚本语言使用的一个自动化工具.导出的工作是非常机械化,而且繁复的.

编译环境设置
    Ruby在Windows下:
    头文件在$RUBY_HOME/lib/ruby/1.8/i386-mswin32;
    lib在$RUBY_HOME/lib,为msvcrt-ruby18.lib;
    dll在RUBY_HOME/bin,其实只有一个dll,就是:msvcrt-ruby18.dll.
    在这里需要注意到的是,$RUBY_HOME/lib/ruby/1.8/i386-mswin32/config.h这个文件对VC的版本做了限制:

None.gif #if _MSC_VER != 1200
None.gif #error MSC version unmatch
None.gif #endif
    所以,如果VC不是这个版本的话,编译是通不过的,对此问题,最简单的办法就是:将这三行代码注释掉,就可以了.

C++解释器包裹代码
头文件
None.gif
None.gif#ifndef __RubyInterpreter_H__
None.gif #define __RubyInterpreter_H__
None.gif
None.gif
None.gif#include < string>
None.gif
None.giftypedef unsigned  long    VALUE;
None.giftypedef std:: string        String;
None.gif
None.giftypedef VALUE(*staticValueMethod)( dot.gif);
None.giftypedef VALUE(*ProtectedMethod)(VALUE);
None.gif
None.gif class RubyInterpreter
ExpandedBlockStart.gif {
InBlock.gifpublic:
InBlock.gif    RubyInterpreter();
InBlock.gif    virtual ~RubyInterpreter();
InBlock.gif
InBlock.gifpublic:
ExpandedSubBlockStart.gif    /// 初始化解释器
InBlock.gif    void initializeInterpreter();
InBlock.gif
ExpandedSubBlockStart.gif    /// 终止解释器
InBlock.gif    void finalizeInterpreter();
InBlock.gif
ExpandedSubBlockStart.gif    /// 设置
InBlock.gif    void setOutputFunction(staticValueMethod func);
InBlock.gif
ExpandedSubBlockStart.gif    /// 加入引用库的搜索路径
InBlock.gif    void addSearchPath(const String& path);
InBlock.gif
InBlock.gifpublic:
ExpandedSubBlockStart.gif    /// 执行语句
InBlock.gif    bool execute(const String& command);
InBlock.gif    
ExpandedSubBlockStart.gif    /// 执行文件
InBlock.gif    bool executeFile(String rubyfile);
InBlock.gif
InBlock.gifprivate:
ExpandedSubBlockStart.gif    /// 记录错误日志
InBlock.gif    void logRubyErrors(const std::string& intro, int errorcode);
InBlock.gif    
ExpandedSubBlockStart.gif    /// 
InBlock.gif    void loadProtected(ProtectedMethod func, VALUE args,
InBlock.gif        const std::string& msg, bool exitOnFail = false);
InBlock.gif
ExpandedSubBlockStart.gif    /// 
InBlock.gif    static VALUE loadDlls(VALUE);
ExpandedBlockEnd.gif}
;
None.gif
None.gif
None.gif #endif
None.gif
源文件
None.gif
None.gif#include "StdAfx.h"
None.gif#include "RubyInterpreter.h"
None.gif
None.gif#include "FixRubyHeaders.h"
None.gif#include 
None.gif#include "FixRubyHeaders.h"
None.gif
None.gif
None.gifRubyInterpreter::RubyInterpreter()
ExpandedBlockStart.gif {
InBlock.gif
ExpandedBlockEnd.gif}

None.gif
None.gifRubyInterpreter::~RubyInterpreter()
ExpandedBlockStart.gif {
InBlock.gif
ExpandedBlockEnd.gif}

None.gif
None.gif void RubyInterpreter::initializeInterpreter()
ExpandedBlockStart.gif {
InBlock.gif#if defined(NT)
InBlock.gif    static int dummyargc(0);
InBlock.gif    static char** vec;
InBlock.gif    NtInitialize(&dummyargc, &vec);
InBlock.gif#endif
InBlock.gif
InBlock.gif    // 初始化Ruby
InBlock.gif
    ruby_init();
InBlock.gif
InBlock.gif    // 使用UTF8编码
InBlock.gif
    execute( "$KCODE = 'u'" );
InBlock.gif
InBlock.gif    // addSearchPath();
InBlock.gif
InBlock.gif    
// 初始化脚本加载路径
InBlock.gif
    ruby_init_loadpath();
InBlock.gif
InBlock.gif    // 设置安全级别
InBlock.gif
    rb_set_safe_level(0);
InBlock.gif
InBlock.gif    // 
InBlock.gif
    ruby_script("ruby");
InBlock.gif
InBlock.gif    //loadProtected(&RubyInterpreter::loadDlls, 0, "Ruby error while loading dlls");
ExpandedBlockEnd.gif
}

None.gif
None.gif void RubyInterpreter::finalizeInterpreter()
ExpandedBlockStart.gif {
InBlock.gif    ruby_finalize();
ExpandedBlockEnd.gif}

None.gif
None.gif void RubyInterpreter::setOutputFunction(staticValueMethod func)
ExpandedBlockStart.gif {
InBlock.gif    rb_defout = rb_str_new("", 0);
InBlock.gif
InBlock.gif    // 定义一个虚拟类的方法
InBlock.gif
    rb_define_singleton_method(rb_defout, "write", func, 1);
ExpandedBlockEnd.gif}

None.gif
None.gif void RubyInterpreter::addSearchPath( const String& path)
ExpandedBlockStart.gif {
InBlock.gif    ruby_incpush(path.c_str());
ExpandedBlockEnd.gif}

None.gif
None.gifVALUE RubyInterpreter::loadDlls(VALUE val)
ExpandedBlockStart.gif {
InBlock.gif    String lib;
InBlock.gif
InBlock.gif    // 
InBlock.gif
    return rb_require(lib.c_str());
ExpandedBlockEnd.gif}

None.gif
None.gif void RubyInterpreter::loadProtected(ProtectedMethod func,
None.gif                                    VALUE val, 
None.gif                                     const std:: string& msg, 
None.gif                                     bool exitOnFail)
ExpandedBlockStart.gif {
InBlock.gif    int error = 0;
InBlock.gif    rb_protect(func, val, &error);
InBlock.gif    logRubyErrors("Ruby error while initializing", error);
ExpandedBlockEnd.gif}

None.gif
None.gif void RubyInterpreter::logRubyErrors( const std:: string& intro,  int errorcode)
ExpandedBlockStart.gif {
InBlock.gif    if (errorcode != 0)
ExpandedSubBlockStart.gif    {
InBlock.gif        VALUE info = rb_inspect(ruby_errinfo);
InBlock.gif        rb_backtrace();
InBlock.gif        if (intro.length() > 0)
ExpandedSubBlockStart.gif        {
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif
None.gif bool RubyInterpreter::execute( const String& command)
ExpandedBlockStart.gif {
InBlock.gif    int status = -1;
InBlock.gif
InBlock.gif    rb_eval_string_protect(command.c_str(), &status);
InBlock.gif
InBlock.gif    logRubyErrors("", status);
InBlock.gif
InBlock.gif    if ( status )
ExpandedSubBlockStart.gif    {
InBlock.gif        rb_eval_string_protect("print $!", &status);
InBlock.gif        return false;
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    return true;
ExpandedBlockEnd.gif}

None.gif
None.gif bool RubyInterpreter::executeFile(String rubyfile)
ExpandedBlockStart.gif {
InBlock.gif    bool error = execute("load '" + rubyfile + "'");
InBlock.gif    return error;
ExpandedBlockEnd.gif}

None.gif


SWIG的使用
步骤大致为:
1. 编写后缀为.i的脚本;
2. 使用swig生成导出代码,假如脚本名为:sample.i,那么生成的源码文件名规则就为:sample_wrap.cpp/.c.
3. 将生成的cpp加入动态链接库,然后编译.

最简单的.i脚本为:
None.gif % module Export4ScriptLib
ExpandedBlockStart.gif
% {
InBlock.gif#include 
"Player.h"
ExpandedBlockEnd.gif
%}

None.gif
None.gif
None.gif
% include  " stl.i "
None.gif
% include  " Player.h "
Edit:如果想要使用STL的导出类,那就需要添加%include "stl.i"
假如说,头文件里面定义的所有的类,类所有的方法,你都要将之导出,那么以上就足够了.但是,假如你只需要导出部分的类,部分的类的方法.那么你就需要自己手动写入到.i脚本里面去了.

生成代码的命令为:
None.gifswig.exe -c++ -ruby Exports.i
这样写的前提是你已经吧swig的路径加入到环境变量里面去了,其中第一个参数表示的是导出的代码为c++,第二个参数表示的目标脚本语言是谁,第三个参数是.i脚本的路径名.我写了一个批处理:invoke_swig.bat,做这件事情.不过更完美的做法是在VC项目里面的"预生成事件"加入此语句.

剩下的事情就是把生成的代码和要导出的代码编译一边,就可以开始使用导出的C++库了.


测试
在实例代码里面:Export4ScriptLib工程是动态链接库工程,testRubyInterpreter是测试用的可执行程序工程.
测试用的Ruby代码test.rb如下:
None.gifrequire  ' Export4ScriptLib '
None.gif
None.gif print  " hello 你好!\n "
None.gif
None.gif
None.gifply = Export4ScriptLib::Player.new
None.gifply.Jump();
None.gifply.Move(100, 2000);
测试用C++代码如下:
None.gif class testClient
None.gif{
None.gifpublic:
None.gif    testClient()
None.gif    {
None.gif        mRubyInterpreter = new RubyInterpreter();
None.gif        mRubyInterpreter->initializeInterpreter();
None.gif    }
None.gif    
None.gif    ~testClient()
None.gif    {
None.gif        delete mRubyInterpreter;
None.gif    }
None.gif
None.gif    void  exec()
None.gif    {
None.gif        // 执行语句
None.gif        mRubyInterpreter->execute( " print \ "This  is C++ call Ruby  print funtion!\n\ "");
None.gif        
None.gif        // 执行文件
None.gif        mRubyInterpreter->executeFile( " test.rb ");
None.gif    }
None.gif
None.gifprivate:
None.gif    RubyInterpreter* mRubyInterpreter;
None.gif};

你可能感兴趣的:(C++程序中嵌入Ruby脚本系统)