聚星亭

吾笨笨且懒散兮 急须改之而奋进
posts - 74, comments - 166, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

ChaiScript 体验手册

Posted on 2009-08-03 21:55 besterChen 阅读(6221) 评论(0)  编辑 收藏 引用

好长时间以来,一直都想在网上找一个C风格并且可以与C/C++配合良好的脚本语言。可是无奈,用过很多的脚本,luaPython ruby等等,但总是感觉让我不是很满意(自己水平差,没办法),除了LUA一直在使用以外,其它脚本都是一看就放弃了。      

越来越感觉用lua不顺手,它的书写风格让我感觉不舒服,可是,功夫不负有心人,7月底的时候,让我找到了ChaiScript,简单的看了介绍就赶紧的用了下试试…… 

由于本人菜鸟,有点发现总是不敢独享,于是鉴于chaiScript没有现成的使用文档,所以就写了这个破烂文章,仅为帮助像我一样菜的朋友,故高手略过…… 

本人也只是简单的测试和使用了一下,因此很多的东西摸得不是很透,所以,倘若我写的内容有错误或者理解不多的地方,还请各位朋友帮忙指正,以免误人子弟。

 

 

        besterChen                     
2009729                


 

 

ChaiScript 简介

首先,ChaiScript是一个开源项目,使用的是GoogleSVN,如果大家有兴趣参与或者获取源代码可以浏览网址:http://code.google.com/p/chaiscript/ 

直接引用下官网中对ChaiScript的评价和简介:

ChaiScript是第一个(也许是唯一)直接针对C + +设计的嵌入式脚本语言。作为一个本地C + +应用程序,,在现有的嵌入式脚本语言中,它也有自己的优势:

1.             它使用一个头,唯一办法,这使得它很容易与现有的项目。
2.             保持类型安全性之间的C + +应用程序和用户脚本。
3.             它支持多种C + +的技术,包括回调,重载函数,类方法,和STL容器。  

它是09713才公布了第一个发行版本,7月底公布的1.1的发布版本,比较幸运的是我们可以很早的体验到这个脚本的最新特性,关于更多的介绍,请大家到下述网址自己的查找,这里就不多浪费大家时间了。



 

它的官方网站是:       http://www.chaiscript.com/
最新的下载地址在:    http://code.google.com/p/chaiscript/downloads/list 

下面让我们开始吧。


 

配置ChaiScript脚本的运行环境

ChaiScript的正常运行需要有Boost的支持,所以,我们需要先在配置ChaiScript环境之前,安装和配置好boost库,下面我将分开介绍boostChaiScript的配置。 

1Boost库的安装和使用

大家可以官网(http://www.boost.org/)来获取最新的boost库代码及Jam编译器。

bjam.exe复制到boost根目录下:
   将命令行也定位boost根目录下。

编译时通过设置参数可以指定编译器和编译版本以及存放路径这里没哟编译python.

 

bjam --toolset=msvc-8.0 --prefix=x:\boost stage
这个命令将会生成release版的libdll文件

bjam --toolset=msvc-8.0 --prefix=x:\boost debug stage
这个命令将会生成带gd字符的debug版的libdll文件

bjam --tooset=msvc-8.0 --prefix=x:\boost debug runtime-link=static stage
这个命令将会生成带sgd字符的静态debug版的libdll文件

 

如果不是用--prefix参数将默认生成在c:\boost目录下 

编译完成后可以建立一个bat文件自动将生成的libdll文件拷贝到lib目录下面

cplib.bat

dir /W/S/B *vc80*.lib,*vc80*.dll >liblist
if exist lib (echo exist lib fold) else (mkdir lib)
for /f %%x in (liblist) do @copy "%%x" lib\

 

拷贝之后可以做一下清理工作,同个建立一个bat文件来自动清理编译过程产生的.obj文件

delobj.bat

del /s/q *.obj

 将这两个文件放在x:\boost目录下执行就可以了。 

配置环境变量 

Tools -> Options -> Projects and Solutions -> VC++ Directories Library files加上x:\boost\lib 

Include files加上x:\boos 

测试程序:

#include <iostream>
#include <string>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string_regex.hpp> 

using namespace std;
using namespace boost;
int main()
{   
  string s = "    Hello boost!! ";    
 
trim(s);    
  cout << s << endl
  getchar();  

  std::string regstr = "a+"
  boost::regex expression(regstr);  
  std::string testString = "aaa";

  // 匹配至少一个a    
  if( boost::regex_match(testString, expression) )   
 
       std::cout<< "Match" << std::endl;   
  }
  else
 
       std::cout<< "Not Match" << std::endl; 
  }
}

 
说明:本库的配置方法引用与http://paul216.blog.hexun.com/29507251_d.html


 

2ChaiScript运行环境的配置。

       ChaiScript的使用方法比较简单,只要在IDE中配置一下包含目录就可以了。

       首先,我们从我们下载回来的文件中把Include拷贝到指定的一个库文件夹中,比如我的目录是:D:\OtherLib\ChaiScript,如下图:

      

然后在VC环境中点击:

Tools -> Options -> Projects and Solutions -> VC++ Directories Include files加上D:\OtherLib\ChaiScript\Include 

       这样我们就可以开始写可以嵌ChaiScript的程序了。


 

ChaiScript的基础语法概要

       关于ChaiScript的基础语法讲解并不像如何使用ChaiScript那样模糊,在官网上已经讲的很详尽清楚了,只不过是英文版本,我就简单的翻译一下,我尽量建议大家看英文版本,因为我的英文水平我自己都不放心…… 

1、            条件模块(if

       ChaiScript支持If语句,但是需要说明:"else if"是一个单独的关键字而不是两个分开的关键字。大概的用法格式如下:

var i = 2

if (i < 2)
{
     print("less than 2")
}
else if (i == 2)
{
     print("equal to 2")
}
else
{
     print("more than 2")

}

 

2、            循环语句

       ChaiScriptC + +的循环风格差不多有两种:for循环和while循环。大概的用法如下:

1)        For 循环:

for (var i = 0; i < 10; ++i)
{
     print("i: " + i.to_string())
} 

2)        While 循环:

var i = 0
while (i < 10)
{
     print("i: " + i.to_string())
     ++i
}

       说明:跟C++一样,在ChaiScript中也可以使用break关键字来中断循环。

3、            容器(VectorsMaps

ChaiScript支持少数容器类型,也允许用简捷的写法创建VectorsMaps。比如:

1)        Vectors:

var x = [1, 2, 3]
print(x[1])

2)        Maps:

var x = ["bob":1, "fred":2]
print(x["fred"])

       同样,我们也可以通过一个值的范围来快速的创建Vector容器,例如:

var x = [1..10]

print(x)

4、            函数

ChaiScript中定义函数有两种书写方法。

1)        使用“def”关键字定义静态函数,在全局范围内都可见。比如:

def add_elems(x, y)
{
     x + y
}

print(add_elems(5, 10))

2)        创建一个匿名函数,通常赋值给一个变量来使用。

var add_elems = fun(x, y) { x + y }
print(add_elems(5, 10))

5、            返回值

函数的返回值也可以有两种写法。

一种是跟C++一样,明确的使用关键字”return”返回,用法跟C++一样,例如:

def five()
{
  return 5
}

print(five())

另一种是隐式的返回函数结果,就是当一个函数没有明确的使用return语句返回时,最后的这个数值就作为函数的返回值,比如:

def five()
{
  5
}

print(five())

       这样定义函数的返回值与上一种写法是一样的。

6、            方法(Methods

其实ChaiScript并不支持什么方法,只是利用了函数的一个被叫作“syntactic sugar”的技术代替的方法。例如:

5.to_string()
to_string(5)

       这就是:值在“.”的左边作为第一个参数命名的函数而已,没有别的区别。

7、            派遣函数(Dispatch Functions

ChaiScript允许函数重载,使用派遣函数可以自动的使用相应的函数已达到相应的功能。比如说,你想从一堆数中打印出正确的复数,可以这样写代码:

def print_num(x) : x == 0 || x > 1
{
  print(x.to_string() + " units")
} 

def print_num(x) : x == 1
{
  print(x.to_string() + " unit")
}
print_num(2)
print_num(1)
print_num(0)


 

VC工程中使用ChaiScript

1、一个chaiscript的测试程序

       在我们下载回来的源代码中有一个TestExample工程的例子,我不知道它是在VS的多少版本下创建的,改了一下它的工程文件,在2005下编译过了,但是它目录设置的很杂,所以,我们在这里自己新建一个控制台工程,把它的源代码copy过来,我们研究下,源代码如下:

// TestExample.cpp : 定义控制台应用程序的入口点。
//

#include 
"stdafx.h"
#include 
<iostream>

#include 
<chaiscript/chaiscript.hpp>
#include 
<chaiscript/dispatchkit/function_call.hpp>
#include 
<boost/function.hpp>
#include 
<boost/date_time/posix_time/posix_time.hpp>
#include 
<boost/regex.hpp>

void log(const std::string &msg)
{
    std::cout 
<< "[" << boost::posix_time::microsec_clock::local_time() << "" << msg << std::endl;
}

void log(const std::string &module, const std::string &msg)
{
    std::cout 
<< "[" << boost::posix_time::microsec_clock::local_time() << "] <" << module << "" << msg << std::endl;
}

struct System
{
    std::map
<std::string, boost::function<std::string (const std::string &> > m_callbacks;

    
void add_callback(const std::string &t_name, 
        
const chaiscript::Proxy_Function &t_func)
    {
        m_callbacks[t_name] 
= chaiscript::functor<std::string (const std::string &)>(t_func);
    }

    
void do_callbacks(const std::string &inp)
    {
        log(
"Running Callbacks: " + inp);
        
for (std::map<std::string, boost::function<std::string (const std::string &)> >::iterator itr = m_callbacks.begin();
            itr 
!= m_callbacks.end();
            
++itr)
        {
            log(
"Callback: " + itr->first, itr->second(inp));
        }
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    
using namespace chaiscript;

    ChaiScript chai;

    
//创建一个新的system 对象
    System system;
    
//把它添加到chaiscript引擎中
    chai.add(var(&system), "system");

    
//注册System结构体的两个方法.
    chai.add(fun(&System::add_callback), "add_callback");
    chai.add(fun(
&System::do_callbacks), "do_callbacks");

    
// 让我们用chaiscript给我们的system结构添加一个新的回调函数。
    
// 在Chaiscript中创建函数"{ 'Callback1' + x }" 并通过我们C++程序System结构中的add_callback函数
    
// 将这个chaiscript脚本函数转换到boost::function,
    
// so it can be handled and called easily and type-safely
    chai.eval("system.add_callback(\"#1\", fun(x) { \"Callback1 \" + x });");

    
// 由于我们在chaiscript引擎中共享了“system”对象
    
// 所以我们可以同时在C++代码中和Chaiscript脚本中使用这个对像
    system.do_callbacks("TestString");
    chai.eval(
"system.do_callbacks(\"TestString\");");

    
// 日志函数被(log)重载过了, 因此我们必须得同时C++编译器,我们要注册那个版本的日志函数。
    
// 这样,唯一的一个方法就是用typedef创建一个函数指针,然后注册这个函数指针就可以了
    typedef void (*PlainLog)(const std::string &);
    typedef 
void (*ModuleLog)(const std::string &const std::string &);
    chai.add(fun(PlainLog(
&log)), "log");
    chai.add(fun(ModuleLog(
&log)), "log");

    chai.eval(
"log(\"Test Message\")");

    
// 由于eval函数重载了操作符(),因此我们可以直接使用chai关键字而省去eval方法。
    chai("log(\"Test Module\", \"Test Message\");");

    
//最后, 我们可以注册任何一个boost::function 作为sys对象的函数, 按照这样来说, 
    
//我们可以给system对象添加一个受限的成员函数,例如:
    chai.add(fun(boost::function<void ()>(boost::bind(&System::do_callbacks, boost::ref(system), "Bound Test"))), "do_callbacks");

    
//调用限制版本的do_callbacks函数
    chai("do_callbacks()");

    boost::function
<void ()> caller = chai.functor<void ()>("fun() { system.do_callbacks(\"From Functor\"); }");
    caller();


    
//如果我们想从所有的调用中得到一个类型安全的返回值,那我们可以使用eval的模板,如下:
    int i = chai.eval<int>("5+5");
    std::cout 
<< "5+5: " << i << std::endl;

    
//添加一个新的变量
    chai("var scripti = 15");

    
//我们甚至可以操作system的变量
    int &scripti = chai.eval<int &>("scripti");

    std::cout 
<< "scripti: " << scripti << std::endl;
    scripti 
*= 2;
    std::cout 
<< "scripti (updated): " << scripti << std::endl;
    chai(
"print(\"Scripti from chai: \" + to_string(scripti))");

    
//要做到: 在需要时添加一个直接处理Boxed_Values 的实例。

    
//在堆栈上创建和使用函数
    int x = chai.functor<int (intint)>("fun (x, y) { return x + y; }")(56);

    log(
"Functor test output", boost::lexical_cast<std::string>(x));


    
//在需要的时候,可以创建自己的容器. 目前可以支持大多数的std::vector 和std::map 
    chai.add(bootstrap::vector_type<std::vector<int> >("IntVector"));

    
return 0;
}

       我这破英文底子,哎~~我硬着头皮大概翻译了一下,方便比我还差的朋友阅读,翻译的肯定有不对的,大家可以参考源程序的英文注释,这里还望大家多多指正。

        根据这些注释,我想看懂这个程序应该不难的。这样,我们也就知道ChaiScript的基础用法了。

        下面我将更详细的讲述着程序中的知识点。

 

2、将ChaiScript添加到现有的VC工程中。

              ChaiScript文件夹的Readme.txt文件告诉我们:

²        ChaiScriptinclude目录添加到工程的包含目录中。

²        在我们的工程原文件中包含 "#include <chaiscript/chaiscript.hpp>

²        在我们的工程中创建ChaiScript引擎的实例. 

                            例如, 创建一个新的名为“chai”的引擎可以这样编写代码:

using namespace chaiscript;

ChaiScript chai;

或者写成:

chaiscript::ChaiScript chai;


 

3ChaiScript脚本与C++相互调用

1)          C++中调用/执行ChaiScript代码

现在,我们有两种方法来调用/执行ChaiScript脚本代码:

²        第一种方法:直接在C++代码中编写并执行ChaiScript代码,例如:

     //添加一个新的变量
     chai("var scripti = 15");

甚至我们都可以直接获取脚本的表达式的值,例如:

int i = chai.eval<int>("5+5");

²        第二种方式是直接调用ChaiScript脚本文件来执行。例如:

我们可以编写脚本如下:

// HelloWorld.chai
print("hello chaiscript world...")

                     编写控制台程序如下:

#include <iostream>
#include <chaiscript/chaiscript.hpp>
#include <chaiscript/dispatchkit/function_call.hpp>
#include <boost/function.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/regex.hpp> 

int _tmain(int argc, _TCHAR* argv[])
{
  using namespace chaiscript;
  ChaiScript chai;
  chai.eval_file(".\\HelloWorld.chai\0");   // 调用我们刚写的脚本

  return 0;
}

                     程序执行结果如下:

2)          C++中使用脚本的变量(对象)

     //添加一个新的变量
     chai("var scripti = 15"); 

     //我们甚至可以操作system的变量
     int &scripti = chai.eval<int &>("scripti"); 

     std::cout << "scripti: " << scripti << std::endl;
     scripti *= 2;
     std::cout << "scripti (updated): " << scripti << std::endl;
     chai("print(\"Scripti from chai: \" + to_string(scripti))");

       上面这段代码,相信大家应该都有印象吧,这段代码很简明的描述了如何在C++中操作ChaiScript中的变量的方法:使用引用。

3)          C++中调用ChaiScript中定义的函数。

总是感觉ChaiScript::functor这个模板的功能很神奇哈,在我们一开始的测试程序中,看到了关于它的很多神奇的用法,由于我的个人能力有限,值说明一下它的基本用法,至于其它的,大家可以自己去摸索。

先看下functor的声明吧:

template<typename FunctionType>

boost::function<FunctionType> functor(const std::string &script)
{
     return chaiscript::functor<FunctionType>(eval(script));
}

很明白了吧,我们这个字符串就相当于直接在ChaiScript写脚本了,没有什么别的估计的,直接调用我们编写的函数就好,OK,不多废话,在脚本中创建函数如下:

// TestFor.chai
def TestFor()
{
    for (var i = 0; i < 10; ++i
    {
       print("i: " + i.to_string())
    }
}

                     编写我们的C程序如下:

#include <iostream>
#include <chaiscript/chaiscript.hpp>
#include <chaiscript/dispatchkit/function_call.hpp>
#include <boost/function.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/regex.hpp> 

int _tmain(int argc, _TCHAR* argv[])
{
  using namespace chaiscript;
  ChaiScript chai;
  chai.eval_file(".\\TestFor.chai\0");
  boost::function<void ()> TestFor = chai.functor<void ()>("TestFor");   // 关联我们的脚本函数
  TestFor();         // 调用脚本函数 

  return 0;
}

                     程序执行结果如下:

4)          在脚本中调用C++函数/ 对象

到现在,我们应该对ChaiScript有一定得了解了,应该知道,如果要在脚本中使用C++对象或者使用C++的函数应该先把函数注册到ChaiScript引擎中。

 ChaiScript1.1中注册函数或者对象的方法已经与1.0版本发生的一点变化,1.1版本注册函数/对象的语法格式如下:

注册对象的语法格式:

/创建一个新的system 对象
System system;
//把它添加到chaiscript引擎中
chai.add(var(&system), "system");

注册函数的语法格式:

chai.add(fun(&MyClass::function), "method");

上述格式注册的是我们的类成员函数,如果要注册一个独立的函数,那可以先声明一个这个函数的指针然后在注册,如下例子:

我们可以编写脚本如下:

// HelloWorld.chai
log("hello chaiscript world...")

                     编写控制台程序如下:

#include <iostream>
#include <chaiscript/chaiscript.hpp>
#include <chaiscript/dispatchkit/function_call.hpp>
#include <boost/function.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/regex.hpp> 

void log(const std::string &msg)
{
  std::cout << "[" << boost::posix_time::microsec_clock::local_time() << "] " << msg << std::endl;
} 

int _tmain(int argc, _TCHAR* argv[])
{
  using namespace chaiscript;
  typedef void (*PlainLog)(const std::string &);
  ChaiScript chai;
  chai.add(fun(PlainLog(&log)), "log");     // 注册C++函数,以便在脚本中调用它
  chai.eval_file(".\\HelloWorld.chai\0");   // 调用我们刚写的脚本 

  return 0;
}

                     程序执行结果如下:

       好,到这里,在VC中使用ChaiScript脚本的内容就讲完了,其实重点就是几个ChaiScript中的几个API的使用,如:ChaiScript::addChaiScript::evalChaiScript::eval_file ChaiScript::functor等等。

 这些ChaiScript API更多更详细的介绍,请大家关注:http://www.chaiscript.com/node/28


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理