C++程序中嵌入Ruby脚本系统-阿里云开发者社区

开发者社区> 杨粼波> 正文

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

简介: Ruby,一种为简单快捷面向对象编程(面向对象程序设计)而创的脚本语言,由日本人松本行弘(まつもとゆきひろ,英译:Yukihiro Matsumoto,外号matz)开发,遵守GPL协议和Ruby License。
+关注继续查看
什么是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.gifclass 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 <ruby.h>
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.gifvoid 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.gifvoid RubyInterpreter::finalizeInterpreter()
ExpandedBlockStart.gif{
InBlock.gif    ruby_finalize();
ExpandedBlockEnd.gif}

None.gif
None.gifvoid 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.gifvoid 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.gifvoid 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.gifvoid 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.gifbool 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.gifbool 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.gifprint "hello 你好!\n"
None.gif
None.gif
None.gifply = Export4ScriptLib::Player.new
None.gifply.Jump();
None.gifply.Move(100, 2000);
测试用C++代码如下:
None.gifclass 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};

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
怎么设置阿里云服务器安全组?阿里云安全组规则详细解说
阿里云服务器安全组设置规则分享,阿里云服务器安全组如何放行端口设置教程
8383 0
嵌入式系统之Modbus TCP to Modbus Rtu协议转换器开发
在做隧道广告项目的时候,用户提出一个新的需求:要求在指定的时段播放指定的节目。在利用现有条件下,经过充分分析,决定在隧道里安装一个西门子200 PLC,这样除建国门段的隧道系统外,其它现场直接用摩莎公司的NProt模块(TCP 转 485)就可以使隧道外的PC机直接访问到PLC,这个没有什么问题。
1030 0
嵌入式系统的省电模式
电源管理一直是一个热门话题,是近年来的嵌入式开发人员遇到的“一个很酷的话题”。其重要性在很大程度上取决于延长手持设备电池寿命的要求,但环境危害,散热等引起了人们的关注。嵌入式软件可以实现三种节电程序:使用动态电压和频率调整(DVFS)来根据当前需要调整CPU性能。
924 0
嵌入式系统 Boot Loader 技术内幕
1. 引言 在专用的嵌入式板子运行 GNU/Linux 系统已经变得越来越流行。一个嵌入式 Linux 系统从软件的角度看通常可以分为四个层次: 1. 引导加载程序。
700 0
EpicEditor – 可嵌入的 JavaScript Markdown 编辑器
  EpicEditor 是一款可以嵌入到网页中的 JavaScript Markdown 编辑器,支持全屏编辑、在线预览、自动保存草稿,离线支持等等。对于开发人员,它提供了一个健壮的API,可以很容易定制主题并允许你灵活使用绑定的 Markdown 解析器。
842 0
嵌入式系统的复杂性推动了对接口标准的发展
二十年前,用于系统建模、仿真、图像和信号处理的嵌入式实时处理通常使用按比例缩小的超级计算机体系结构,以并行对称拓扑互连的相同处理器的同质阵列。这些架构的编程解决方案最初是分散的,通常使用硬件供应商或微处理器专用软件层来处理处理元件之间的通信。
791 0
linux系统初始化脚本
给大家分享一个工作中很实用的系统初始化脚本,其实就是各种命令的集合!当然了,如果有cobber就更嗨了~~ 点击(此处)折叠或打开 #!/bin/bash ###此脚本用于初始化系统,也就是刚刚配置完网卡的服务器用于初始化.
837 0
+关注
杨粼波
网游的老兵
1135
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载