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不是这个版本的话,编译是通不过的,对此问题,最简单的办法就是:将这三行代码注释掉,就可以了.#error MSC version unmatch
#endif
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"%{
#include "Player.h"
%}
%include "stl.i"
%include "Player.h"
假如说,头文件里面定义的所有的类,类所有的方法,你都要将之导出,那么以上就足够了.但是,假如你只需要导出部分的类,部分的类的方法.那么你就需要自己手动写入到.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++代码如下:print "hello 你好!\n"
ply = Export4ScriptLib::Player.new
ply.Jump();
ply.Move(100, 2000);
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;
};
{
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;
};