当脚本只是一系列简单命令序列的时候,一个Goto语句用起来会非常方便,比如可以这样写
--开始
Print("这是1");
Print("这是2");
Goto("开始");
这是个无限循环,可以随时跳转,很方便,看起来也很清晰。
我基于LuaPlus实现了一个,代码如下:
/*
* /file CmdScript.h
* /brief 基于命令式的Lua脚本对象
* /author cloud
* /date 2010-3-15
*/
#ifndef _CLOUD_CMD_SCRIPT_H_
#define _CLOUD_CMD_SCRIPT_H_
#include <LuaPlus.h>
#include <iostream>
#include <string>
#include <map>
#include <windows.h>
using namespace LuaPlus;
class CCmdScript
{
public:
CCmdScript();
~CCmdScript();
/*
* /brief 在此成员内向Lua注册函数,重载此方法,注册自己的函数
* /return 出错返回false
*/
virtual bool Register();
/*
* /brief 解析指定的脚本文件,读取每一行,并做映射
* /return 返回此脚本文件的行数,小于0,解析失败
*/
int ParseFile(std::string strFile);
/*
* /brief 执行一个指定的字符串
* /return 返回错误消息,未出错,返回空字符串
*/
std::string DoLineByIndex(int index);
/*
* /brief 将导出的脚本函数,跳转到指定行
*/
int Lua_Goto(LuaState* state);
int GetNextLineIndex();
/*
* /brief 获取当前脚本文件总共的行数
*/
int GetSumLine();
bool RunFile(std::string strFile);
/*LuaPlus State对象*/
LuaStateOwner m_state;
private:
/*行号和脚本命令的映射*/
std::map<int,std::string> m_mapLine;
/*当前脚本行的指针*/
int m_iCurrLine;
std::string m_strFile;
HANDLE m_hThread;
};
#endif
/*
* /file CmdScript.cpp
* /author cloud
* /date 2010-3-15
*/
#include "CmdScript.h"
#include <fstream>
UINT WINAPI ScriptThread(void* p);
int Lua_Print(LuaState* state)
{
LuaStack args(state);
const char* szmsg = args[1].GetString();
printf("%s/n",szmsg);
::Sleep(1000);
return 0;
}
CCmdScript::CCmdScript():m_iCurrLine(1)
{
Register();
}
CCmdScript::~CCmdScript()
{
::TerminateProcess(m_hThread,-1);
::CloseHandle(m_hThread);
LuaState::Destroy(m_state);
}
bool CCmdScript::Register()
{
m_state->GetGlobals().Register("Goto",*this,&CCmdScript::Lua_Goto);
m_state->GetGlobals().Register("Print",Lua_Print);
return true;
}
int CCmdScript::ParseFile(std::string strFile)
{
m_strFile = strFile;
std::ifstream file;
file.open(m_strFile.c_str(),std::ios_base::in);
if(!file) return false;
int indexLine = 1;
while(file){
std::string strLine;
std::getline(file,strLine);
m_mapLine[indexLine] = strLine;
indexLine ++;
}
file.close();
return m_mapLine.size();
}
std::string CCmdScript::DoLineByIndex(int index)
{
std::string strRet;
std::map<int,std::string>::iterator it = m_mapLine.find(index);
//未找到指定的行
if(it == m_mapLine.end()) return strRet;
std::string strLine = it->second;
int iRet = m_state->DoString(strLine.c_str());
m_iCurrLine ++;
return strRet;
}
int CCmdScript::Lua_Goto(LuaState* state)
{
//printf("This is CCmdScript::Lua_Goto! this=0x%X/n",this);
LuaStack args(state);
std::string strMark = args[1].GetString();
strMark = "--"+strMark;
bool bFound = false;
//遍历map,找到匹配的行
for(std::map<int,std::string>::iterator it = m_mapLine.begin();it != m_mapLine.end();it++){
if(it->second == strMark){
//修改对象当前行号
m_iCurrLine = it->first;
bFound = true;
break;
}
}
//找到,就返回1,否则,返回0
state->PushInteger((int)bFound);
return 1;
}
int CCmdScript::GetNextLineIndex()
{
return m_iCurrLine;
}
int CCmdScript::GetSumLine()
{
return m_mapLine.size();
}
bool CCmdScript::RunFile(std::string strFile)
{
ParseFile(strFile);
m_hThread = ::CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ScriptThread,(void*)this,0,0);
if(m_hThread == NULL) return false;
return true;
}
/*
* /brief 脚本线程
*/
UINT WINAPI ScriptThread(void* p)
{
CCmdScript* pScript = (CCmdScript*)p;
while(1){
//先检查脚本是否结束了
int iNext = pScript->GetNextLineIndex();
int iMax = pScript->GetSumLine();
if(iNext>iMax) return 0;
pScript->DoLineByIndex(iNext);
}
return 0;
}
写个测试程序,
#include "CmdScript.h"
#pragma comment(lib,"LuaPlusLib_1100.lib")
class MyScript : public CCmdScript
{
public:
MyScript(){Register();}
~MyScript(){}
virtual bool Register();
};
int MyAdd(int a,int b)
{
printf("This is MyAdd/n");
return 0;
}
bool MyScript::Register()
{
m_state->GetGlobals().RegisterDirect("MyAdd",MyAdd);
return true;
}
int main(int argc,char** argv)
{
MyScript script;
script.RunFile("test.lua");
system("pause");
return 0;
}
现在执行上面写的脚本
--开始
Print("这是1");
Print("这是2");
Goto("开始");
这是个无限循环,VS2005 XP SP3 下测试通过。