LuaDotNet:.NET 下的 Lua 和 Luabind 的一个轻量级封装






4.89/5 (8投票s)
2003年8月8日
4分钟阅读

157702

1625
将 Lua 脚本引擎添加到您的 .NET 应用程序中。
引言
本文介绍了一个 Lua(参见 [1])和 Luabind(参见 [2])的 .NET 轻量级封装:使用它,您可以将一个具有 C、C++ 后端的脚本引擎嵌入到您的 .NET 应用程序中。
如果您不熟悉 Lua 和 Luabind,下面也提供了一个简单的入门示例。
Lua,一个可嵌入的 C 脚本语言
以下是一些摘自 Lua “关于”页面的引述
“Lua 是一种功能强大的轻量级编程语言,旨在扩展 C、C++ 应用程序。”
“Lua 是一个您可以嵌入到应用程序中的语言引擎。这意味着,除了语法和语义之外,Lua 还提供了一个 API,允许应用程序与 Lua 程序交换数据,并允许使用 C 函数扩展 Lua。从这个意义上说,Lua 可以被视为一个用于构建领域特定语言的语言框架。”
通常,在应用程序中使用 Lua 需要遵循以下步骤:
- 创建 Lua 状态
- 绑定方法到 Lua
- 执行 Lua 脚本
- 清理并释放状态
创建 Lua 状态
“Lua 库是完全可重入的:它没有全局变量。Lua 解释器的整个状态(全局变量、堆栈等)存储在 `lua_State` 类型的动态分配结构中。必须将指向该状态的指针作为第一个参数传递给库中的每个函数,除了 `lua_open` 之外,它从头开始创建一个 Lua 状态。”
如上所述,`lua_open` 用于分配 Lua 状态。
lua_State* L = lua_open();
绑定方法到 Lua
假设需要绑定以下方法:
void my_print( const char* str ) { printf( str ); }
首先,我们需要创建一个 `my_print` 函数的包装器,该包装器接收一个 Lua 状态并返回一个整数,表示它希望返回给 Lua 的值的数量。
int my_print_lua(lua_State *L) { /* get the number of arguments (n) and check that the stack contains at least 1*/ int n = lua_gettop(L); if (n<1) { lua_pushstring(L, "not enough arguments"); lua_error(L); } /* try to cast argument to string and call my_print, - remember that Lua is dynamically typed - we take the first element in the stack */ my_print( lua_checkedstring( L, 1) ); /* my_print does not return values */ return 0; };
最后,使用以下方法在 Lua 中注册该方法:
lua_register(L, "pr", my_print_lua);
注意: 处理 Lua 堆栈可能会变得繁琐且容易出错。希望 Luabind 的出现能(极大地)简化包装任务。
执行 Lua 脚本
考虑以下脚本:
s = 'this is a test';
pr( s );
正如所见,Lua 语法非常直接。在脚本中,我们看到我们绑定的方法(`pr`)被调用了。要在 C 中执行此脚本,请使用 `lua_dostring`:
const char* str = "s = 'this is a test';pr( s );";
lua_dostring(L, str);
程序将输出:
this is a test
清理并释放状态
完成时,请不要忘记调用 `lua_close` 来释放 Lua 状态。
lua_close(L);
Luabind,简化操作
如前所述,处理 Lua 状态是一项繁琐的工作,我们希望避免。Luabind 使用模板元编程来为我们简化操作。
使用 Luabind,之前的步骤变为:
- 创建状态
using namespace luabind; // luabind namespace lua_State* L =lua_open(L); luabind::open(L); // init luabind
- 绑定方法
module(L) [ def("pr", &my_print); ];
备注
- 我们不需要编写 `my_print_lua` 包装器,Luabind 为我们完成了这项工作。
- Luabind 还可以绑定类。
- 请查看 Luabind 页面以获取更多信息,因为它们的文档做得非常好。
LuaNET,封装器
那么,在 .NET 应用程序中执行 Lua 脚本怎么样?这应该不是一个大问题,只需要编写一个托管的 C++ 封装器。
备注
- 可以通过在 LuaNET 项目上运行文档工具来构建详细的类文档。
- 所有类都位于 `Lua` 命名空间下。
State,封装 lua_State
托管类 `State` 封装了 `lua_State` 结构。它处理对 `lua_open` 和 `lua_close` 的调用。
这是一个创建状态、设置一些变量并执行脚本的小示例:
- C#
Lua.State L = new Lua.State(); // creating a lua state L.set_Global("a",1); // equivalent to a=1 in Lua L.DoString("b=a * 2;") // executing a Lua script Double b = L.get_Global("b").ToDouble(); // retreive the value of b
请注意,`get_Global` 返回一个 `LuaObject`,然后通过 `ToDouble` 强制转换为 `double`。
LuaObject,封装 luabind::object
`LuaObject` 使您能够设置和检索 Lua 中的值。支持的值包括:
字符串
double
,int
bool
table
由于 Lua 是动态类型的,因此在转换之前需要检查 `LuaObject` 的类型。否则,如果转换失败,将会引发异常。
L.DoString("s='string';");
L.DoString("print('s: ' .. tostring(s));");
LuaObject s=state.get_Global("s");
s.ToString(); // ok
s.ToDouble(); // fails, s is a string
如果对象是 `table`,`LuaObject` 会在 `table` 上实现 `IDictionnary` 接口,而 `TableEnumerator` 会实现 `IDictionnaryEnumerator` 接口。
L.DoString("t={1,'string'};");
LuaObject t=L.get_Global("t");
System.Collections.IDictionaryEnumerator te = t.GetEnumerator();
while( te.MoveNext() )
{
System.Console.WriteLine("t...(" + te.Key + "," + te.Value +")");
};
加载库
Lua 提供了一组默认 API 来处理字符串、表、I/O、文件等。要加载这些 API,请使用 `DefaultLibrary.Load`。
Lua.Libraries.DefaultLibrary.Load( L );
使用 `LuabindLibrary.Load` 加载 Luabind。
Lua.Libraries.LuabindLibrary.Load( L );
您可以使用与 `DefaultLibrary` 或 `LuabindLibrary` 相同的方法来封装您的 API 并使用 .NET 加载它们。
使用演示
演示项目支持 Lua 5.0 和 Luabind。您需要重新编译所有项目并启动 _LuaNetTest_。
历史
- 2004/8/8: 初始发布