lua
lua-intf 提供了C++11 与Lua 语言之间的一种绑定机制,它提供了三种不同的API(本文只会讲到LuaBinding 与LuaRef)
- LuaBinding:导出C++ 类、函数给Lua 脚本使用
- LuaRef:用于访问Lua Object 的高级别API
- LuaState:为Lua C API 提供的低级别包装器
除了C++11、Lua 之外,lua-intf 没有其他需要依赖的。而且它是一个只有头文件的库,也就是说没有makefile 或者其他安装操作,直接拷贝lua-intf 源码到你的项目中,在代码中#include LuaIntf.h 即可
Lua 和C++ 的错误处理
使用LuaIntf,最好使用C++ 编译器编译Lua,这样允许Lua 库在出现错误时抛出异常,而且保证栈上的C++ 对象被正确的析构
更多关于错误处理的内容,可以参见http://lua-users.org/wiki/ErrorHandlingBetweenLuaAndCplusplus
如果你坚持想在C 环境下编译Lua,而且可以忍受longjump 问题,你可以在#include LuaIntf.h 头文件之前先#define LUAINTF_LINK_LUA_COMPILED_IN_CXX 0
#define LUAINTF_LINK_LUA_COMPILED_IN_CXX 0
#include "LuaIntf/LuaIntf.h"
在C++ 环境下编译Lua
预编译的Lua 库大多数发行版都是在C 环境下编译的,如果需要在C++ 环境下编译Lua 库,你可能需要自己手动操作。在C++ 环境下编译Lua 其实也很简单,首先先把Lua 源码下载下来
curl http://www.lua.org/ftp/lua-5.3.0.tar.gz -o lua-5.3.0.tar.gz
tar xf lua-5.3.0.tar.gz
cd lua-5.3.0
在不同的环境下使用不同的命令进行编译构建
# Linux环境下编译
make linux MYCFLAGS="-x c++" CC="g++"
# Mac OSX 环境下编译
make macosx MYCFLAGS="-x c++" MYLDFLAGS="-lc++"
## 在Windows 下用MINGW 或MSYS 编译
make mingw MYCFLAGS="-x c++" CC="g++"
而且你可以选择指定路径进行安装
make install INSTALL_TOP=<path>
为Lua 脚本导出C++ 类或函数
比如下面这个C++ 类
class Web
{
public:
// base_url is optional
Web(const std::string &base_url);
~Web();
<span class="k">static</span> <span class="kt">void</span> <span class="n">go_home</span><span class="p">();</span>
<span class="k">static</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">home_url</span><span class="p">();</span>
<span class="k">static</span> <span class="kt">void</span> <span class="n">set_home_url</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="o">&</span><span class="n">url</span><span class="p">);</span>
<span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">url</span><span class="p">()</span> <span class="k">const</span><span class="p">;</span>
<span class="kt">void</span> <span class="n">set_url</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="o">&</span><span class="n">url</span><span class="p">);</span>
<span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">resolve_url</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="o">&</span><span class="n">uri</span><span class="p">);</span>
<span class="c1">// doing reload if uri is empty
std::string load(const std::string &uri);
}
使用下面的代码可以将Web 类导出
LuaBinding(L).beginClass<Web>("Web")
.addConstructor(LUA_ARGS(_opt<std::string>))
.addStaticProperty("home_url", &Web::home_url, &Web::set_home_url)
.addStaticFunction("go_home", &Web::go_home)
.addProperty("url", &Web::url, &Web:set_url)
.addFunction("resolve_url", &Web::resolve_url)
.addFunction("load", &Web::load, LUA_ARGS(_opt<std::string>))
.addStaticFunction("lambda", [] {
// you can use C++11 lambda expression here too
return "yes";
})
.endClass();
在Lua 代码中可以这样使用Web 类
local w = Web() -- auto w = Web("");
w.url = "http://www.xumenger.com" -- w.set_url("http://www.xumenger.com");
local page = w:load() -- auto page = w.load("");
page = w:load("http://www.google.com") -- page = w.load("http://www.google.com");
local url = w.url -- auto url = w.url();
Lua 调用C++ 的例子
编写C++ 代码simplelog.cpp,如下
#define LUAINTF_LINK_LUA_COMPILED_IN_CXX 0
#include "LuaIntf/LuaIntf.h"
#include <iostream>
#include <lua.hpp>
#include <string>
using namespace std;
struct lua_State;
class SimpleLog {
public:
static SimpleLog *getInstance() {
static SimpleLog instance;
return &instance;
}
<span class="o">~</span><span class="n">SimpleLog</span><span class="p">();</span>
<span class="kt">void</span> <span class="n">Log</span><span class="p">(</span><span class="k">const</span> <span class="n">string</span> <span class="o">&</span><span class="n">str</span><span class="p">);</span>
<span class="kt">void</span> <span class="n">BindLua</span><span class="p">(</span><span class="n">lua_State</span> <span class="o">*</span><span class="n">L</span><span class="p">);</span>
private:
SimpleLog();
};
SimpleLog::SimpleLog() {}
SimpleLog::~SimpleLog() {}
void SimpleLog::Log(const string &str) {
cout << "C++ Env: SimpleLog::Log() " << str << endl;
}
namespace {
using LuaRef = LuaIntf::LuaRef;
<span class="kt">void</span> <span class="nf">LuaLog</span><span class="p">(</span><span class="k">const</span> <span class="n">string</span> <span class="o">&</span><span class="n">str</span><span class="p">)</span> <span class="p">{</span>
<span class="n">SimpleLog</span><span class="o">::</span><span class="n">getInstance</span><span class="p">()</span><span class="o">-></span><span class="n">Log</span><span class="p">(</span><span class="n">str</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">namespace</span> <span class="n">LuaSimpleLog</span> <span class="p">{</span>
<span class="kt">void</span> <span class="n">Bind</span><span class="p">(</span><span class="n">lua_State</span> <span class="o">*</span><span class="n">L</span><span class="p">)</span> <span class="p">{</span>
<span class="n">assert</span><span class="p">(</span><span class="n">L</span><span class="p">);</span>
<span class="n">LuaIntf</span><span class="o">::</span><span class="n">LuaBinding</span><span class="p">(</span><span class="n">L</span><span class="p">).</span><span class="n">beginModule</span><span class="p">(</span><span class="s">"simple_log"</span><span class="p">)</span>
<span class="p">.</span><span class="n">addFunction</span><span class="p">(</span><span class="s">"log"</span><span class="p">,</span> <span class="o">&</span><span class="n">LuaLog</span><span class="p">)</span>
<span class="p">.</span><span class="n">endModule</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">};</span>
};
void SimpleLog::BindLua(lua_State *L) {
LuaSimpleLog::Bind(L);
}
int main() {
lua_State *L = luaL_newstate();
luaL_openlibs(L);
SimpleLog::getInstance()->BindLua(L);
cout << "C++ Env: C++ main() start" << endl;
luaL_dofile(L, "./test.lua");
cout << "C++ Env: C++ main() end" << endl;
return 0;
}
编写log.lua 代码如下
local Log = {}
local log = simple_log.log
function Log:new(log_name)
assert("table" type(self))
assert(not log_name or "string" type(log_name))
local log = {}
log.log_name = log_name or "log"
setmetatable(log, self)
self.__index = self
return log
end
function Log:set_log_name(log_name)
self.log_name = log_name
end
function Log:info(pattern, …)
log(string.format(pattern, …))
end
function Log:debug()
print("Lua Env: log.lua debuf()")
end
return Log
编写test.lua 内容如下
local function main()
<span class="nb">print</span><span class="p">(</span><span class="s2">"Lua Env: test.lua main() begin"</span><span class="p">)</span>
<span class="kd">local</span> <span class="n">p</span> <span class="o">=</span> <span class="s2">"../TestLuaIntf"</span>
<span class="nb">package.path</span> <span class="o">=</span> <span class="nb">package.path</span> <span class="o">..</span> <span class="s2">";"</span> <span class="o">..</span> <span class="n">p</span> <span class="o">..</span> <span class="s2">"/"</span> <span class="o">..</span> <span class="s2">"TestLuaIntf"</span> <span class="o">..</span> <span class="s2">"/?.lua"</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Lua Env: test.lua main() middle"</span><span class="p">)</span>
<span class="kd">local</span> <span class="n">log</span> <span class="o">=</span> <span class="nb">require</span><span class="p">(</span><span class="s2">"log"</span><span class="p">):</span><span class="n">new</span><span class="p">(</span><span class="s2">"svn_log"</span><span class="p">)</span>
<span class="n">log</span><span class="p">:</span><span class="n">info</span><span class="p">(</span><span class="s2">"Lua Env: test.lua main() call info() %d..."</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Lua Env: test.lua main() end"</span><span class="p">)</span>
end
xpcall(main, function(…)
local msg = {…};
for k, v in pairs(msg) do
print("Lua Env: k=" … tostring(k) … " v =" … tostring(v))
end
print("Lua Env: " … tostring() … " 123")
end)
编译C++ 程序运行效果如下
这个例子中的流程是通过C++ 调用lua 接口luaL_dofile(L, “./test.lua”) 来执行test.lua,test.lua 中require(“log”),然后lua 再调用C++ 的函数Log 完成打印!
访问Lua Object 的高级别API
LuaRef 被设计用来方便地访问Lua 对象,而且大多数情况下你不需要像Lua 原生API那样处理Lua 栈
使用Lua 原生API 实现C++ 访问Lua 的代码要这样实现
lua_State *L = ...;
lua_getglobal(L, "module");
lua_getfield(L, -1, "method");
lua_pushintteger(L, 1);
lua_pushstring(L, "yes");
lua_pushboolean(L, true);
lua_call(L, 3, 0);
但如果使用LuaRef 那么就会非常简单
LuaRef fun(L, "module.method");
func(1, "yes", true);
访问Lua 中的表也是非常方便的
LuaRef table(L, "my_table");
table["value"] = 15;
int value = table.get<int>("value");
for(auto &e : table) {
std::string key = e.key<std::string>();
LuaRef value = e.value<LuaRef>();
…
}
你也可以和Lua 原生API 混用
lua_State *L = ...;
LuaRef v = ...;
lua_getglobal(L, "my_method");
Lua::push(L, 1); // the same as lua_pushinteger
Lua::push(L, v); // push v to lua stack
Lua::push(L, true); // the same as lua_pushboolean
lua_call(L, 3, 2);
LuaRef r(L, -2); // map r to lua stack index -2
C++ 调用Lua 脚本
下面实际的实现一个例子展示如何使用C++ 调用Lua 脚本,将代码运行起来更直观!
编写call_lua.cpp 的代码如下
#define LUAINTF_LINK_LUA_COMPILED_IN_CXX 0
#include "LuaIntf/LuaIntf.h"
#include <lua.hpp>
#include <string>
#include <iostream>
using namespace std;
struct lua_State;
int main()
{
using LuaRef = LuaIntf::LuaRef;
<span class="k">try</span> <span class="p">{</span>
<span class="n">lua_State</span> <span class="o">*</span><span class="n">L</span> <span class="o">=</span> <span class="n">luaL_newstate</span><span class="p">();</span>
<span class="n">luaL_openlibs</span><span class="p">(</span><span class="n">L</span><span class="p">);</span>
<span class="n">LuaIntf</span><span class="o">::</span><span class="n">LuaContext</span> <span class="n">ctx</span><span class="p">(</span><span class="n">L</span><span class="p">);</span>
<span class="n">ctx</span><span class="p">.</span><span class="n">doFile</span><span class="p">(</span><span class="s">"./test_ref.lua"</span><span class="p">);</span>
<span class="n">LuaRef</span> <span class="n">func</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="s">"test_module.test_func"</span><span class="p">);</span>
<span class="n">func</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nb">true</span><span class="p">,</span> <span class="s">"hello world"</span><span class="p">);</span>
<span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="k">const</span> <span class="n">LuaIntf</span><span class="o">::</span><span class="n">LuaException</span> <span class="o">&</span><span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="n">cerr</span> <span class="o"><<</span> <span class="s">"Error: "</span> <span class="o"><<</span> <span class="n">e</span><span class="p">.</span><span class="n">what</span><span class="p">()</span> <span class="o"><<</span> <span class="n">endl</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
}
在编写对应的test_ref.lua 代码如下
local M = {}
local modelName = "test_module"
_G[modelName] = M
function M.test_func(int_v, bool_v, string_v)
print<span class="