作者: 杨粼波
Email: tx7do#yahoo.com.cn 突发奇想的想要把Ruby嵌入到C++的程序里面去,想了于是就去做了.现在只在Windows下面测试通过,其他系统下,我还没有试过,不过基本过程大差不差的.
需要准备些什么? 1.Ruby(
http://www.ruby-lang.org/en/downloads/),去页面中下载最新版本的Ruby 的安装包;
2.Swig(
http://www.swig.org/download.html),去页面中下载最新的安装包.
什么是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;
};
源代码下载testRubyInterpreter.rar参考资料1. 什么是Ruby
http://www.kuqin.com/beginner/ruby.html
2. SWIG
http://swig.minidx.com/ 3.
http://raylinn.javaeye.com/blog/629329