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的版本做了限制:

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

C++解释器包裹代码
头文件

#ifndef __RubyInterpreter_H__
#define __RubyInterpreter_H__


#include < string>

typedef unsigned  long    VALUE;
typedef std:: string        String;

typedef VALUE(*staticValueMethod)( );
typedef VALUE(*ProtectedMethod)(VALUE);

class RubyInterpreter
{
public:
    RubyInterpreter();
    virtual ~RubyInterpreter();

public:
    /// 初始化解释器
    void initializeInterpreter();

    /// 终止解释器
    void finalizeInterpreter();

    /// 设置
    void setOutputFunction(staticValueMethod func);

    /// 加入引用库的搜索路径
    void addSearchPath(const String& path);

public:
    /// 执行语句
    bool execute(const String& command);
    
    /// 执行文件
    bool executeFile(String rubyfile);

private:
    /// 记录错误日志
    void logRubyErrors(const std::string& intro, int errorcode);
    
    /// 
    void loadProtected(ProtectedMethod func, VALUE args,
        const std::string& msg, bool exitOnFail = false);

    /// 
    static VALUE loadDlls(VALUE);
}
;


#endif
源文件

#include "StdAfx.h"
#include "RubyInterpreter.h"

#include "FixRubyHeaders.h"
#include <ruby.h>
#include "FixRubyHeaders.h"


RubyInterpreter::RubyInterpreter()
{

}


RubyInterpreter::~RubyInterpreter()
{

}


void RubyInterpreter::initializeInterpreter()
{
#if defined(NT)
    static int dummyargc(0);
    static char** vec;
    NtInitialize(&dummyargc, &vec);
#endif

    // 初始化Ruby
    ruby_init();

    // 使用UTF8编码
    execute( "$KCODE = 'u'" );

    // addSearchPath();

    
// 初始化脚本加载路径
    ruby_init_loadpath();

    // 设置安全级别
    rb_set_safe_level(0);

    // 
    ruby_script("ruby");

    //loadProtected(&RubyInterpreter::loadDlls, 0, "Ruby error while loading dlls");
}


void RubyInterpreter::finalizeInterpreter()
{
    ruby_finalize();
}


void RubyInterpreter::setOutputFunction(staticValueMethod func)
{
    rb_defout = rb_str_new("", 0);

    // 定义一个虚拟类的方法
    rb_define_singleton_method(rb_defout, "write", func, 1);
}


void RubyInterpreter::addSearchPath( const String& path)
{
    ruby_incpush(path.c_str());
}


VALUE RubyInterpreter::loadDlls(VALUE val)
{
    String lib;

    // 
    return rb_require(lib.c_str());
}


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


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

    }

}


bool RubyInterpreter::execute( const String& command)
{
    int status = -1;

    rb_eval_string_protect(command.c_str(), &status);

    logRubyErrors("", status);

    if ( status )
    {
        rb_eval_string_protect("print $!", &status);
        return false;
    }


    return true;
}


bool RubyInterpreter::executeFile(String rubyfile)
{
    bool error = execute("load '" + rubyfile + "'");
    return error;
}



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

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



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

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

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


测试
在实例代码里面:Export4ScriptLib工程是动态链接库工程,testRubyInterpreter是测试用的可执行程序工程.
测试用的Ruby代码test.rb如下:
require  ' Export4ScriptLib '

print  " hello 你好!\n "


ply = Export4ScriptLib::Player.new
ply.Jump();
ply.Move(100, 2000);
测试用C++代码如下:
class testClient
{
public:
    testClient()
    {
        mRubyInterpreter = new RubyInterpreter();
        mRubyInterpreter->initializeInterpreter();
    }
    
    ~testClient()
    {
        delete mRubyInterpreter;
    }

    void  exec()
    {
        // 执行语句
        mRubyInterpreter->execute( " print \ "This  is C++ call Ruby  print funtion!\n\ "");
        
        // 执行文件
        mRubyInterpreter->executeFile( " test.rb ");
    }

private:
    RubyInterpreter* mRubyInterpreter;
};
目录
相关文章
|
4月前
|
存储 数据库 开发者
Ruby的魔法之旅:如何从命令行脚本演变为Web应用
【8月更文挑战第31天】在编程领域,Ruby凭借其灵活优雅的语法深受开发者喜爱。从命令行脚本到Web应用,Ruby均能游刃有余。它可以帮助我们简化日常任务,如批量重命名文件或自动备份数据。随着技能提升,Ruby还能用于开发复杂的数据抓取工具,并通过Sinatra框架快速搭建Web接口,实现功能共享。其强大的社区支持和简洁的代码风格,使Ruby成为探索编程世界的理想选择,无论处理文本还是构建应用,Ruby都能带来高效与便捷。
46 0
|
4月前
|
C++
C++ 根据程序运行的时间和cpu频率来计算在另外的cpu上运行所花的时间
C++ 根据程序运行的时间和cpu频率来计算在另外的cpu上运行所花的时间
53 0
|
4月前
|
算法 C语言 C++
C++语言学习指南:从新手到高手,一文带你领略系统编程的巅峰技艺!
【8月更文挑战第22天】C++由Bjarne Stroustrup于1985年创立,凭借卓越性能与灵活性,在系统编程、游戏开发等领域占据重要地位。它继承了C语言的高效性,并引入面向对象编程,使代码更模块化易管理。C++支持基本语法如变量声明与控制结构;通过`iostream`库实现输入输出;利用类与对象实现面向对象编程;提供模板增强代码复用性;具备异常处理机制确保程序健壮性;C++11引入现代化特性简化编程;标准模板库(STL)支持高效编程;多线程支持利用多核优势。虽然学习曲线陡峭,但掌握后可开启高性能编程大门。随着新标准如C++20的发展,C++持续演进,提供更多开发可能性。
92 0
|
2月前
|
存储 程序员 编译器
简述 C、C++程序编译的内存分配情况
在C和C++程序编译过程中,内存被划分为几个区域进行分配:代码区存储常量和执行指令;全局/静态变量区存放全局变量及静态变量;栈区管理函数参数、局部变量等;堆区则用于动态分配内存,由程序员控制释放,共同支撑着程序运行时的数据存储与处理需求。
167 21
|
2月前
|
XML 前端开发 数据格式
Ruby脚本:自动化网页图像下载的实践案例
Ruby脚本:自动化网页图像下载的实践案例
|
3月前
|
C++
【C++案例】一个项目掌握C++基础-通讯录管理系统
这篇文章通过一个通讯录管理系统的C++项目案例,详细介绍了如何使用C++实现添加、显示、删除、查找、修改和清空联系人等功能。
57 3
|
3月前
|
C++
【C++基础】程序流程结构详解
这篇文章详细介绍了C++中程序流程的三种基本结构:顺序结构、选择结构和循环结构,包括if语句、三目运算符、switch语句、while循环、do…while循环、for循环以及跳转语句break、continue和goto的使用和示例。
70 2
|
4月前
|
Rust 安全 C++
系统编程的未来之战:Rust能否撼动C++的王座?
【8月更文挑战第31天】Rust与C++:现代系统编程的新选择。C++长期主导系统编程,但内存安全问题频发。Rust以安全性为核心,通过所有权和生命周期概念避免内存泄漏和野指针等问题。Rust在编译时确保内存安全,简化并发编程,其生态系统虽不及C++成熟,但发展迅速,为现代系统编程提供了新选择。未来有望看到更多Rust驱动的系统级应用。
72 1
|
4月前
|
存储 算法 数据可视化
【C++】C++旅游管理系统(源码+论文)【独一无二】
【C++】C++旅游管理系统(源码+论文)【独一无二】
|
4月前
|
搜索推荐 数据处理 文件存储
【C++】C++ 培训报名系统 (源码+论文)【独一无二】
【C++】C++ 培训报名系统 (源码+论文)【独一无二】