lua

17k 词

|

分类

<a href="/categories/#lua之基础" title="lua之基础">lua之基础</a> 

|

“翻译”自https://github.com/SteveKChiu/lua-intf

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">&amp;</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">&amp;</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">&amp;</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">&amp;</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">&amp;</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">-&gt;</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">&#34;simple_log&#34;</span><span class="p">)</span>
                 <span class="p">.</span><span class="n">addFunction</span><span class="p">(</span><span class="s">&#34;log&#34;</span><span class="p">,</span> <span class="o">&amp;</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">&#34;Lua Env: test.lua main() begin&#34;</span><span class="p">)</span>
<span class="kd">local</span> <span class="n">p</span> <span class="o">=</span> <span class="s2">&#34;../TestLuaIntf&#34;</span>
<span class="nb">package.path</span> <span class="o">=</span> <span class="nb">package.path</span> <span class="o">..</span> <span class="s2">&#34;;&#34;</span> <span class="o">..</span> <span class="n">p</span> <span class="o">..</span> <span class="s2">&#34;/&#34;</span> <span class="o">..</span> <span class="s2">&#34;TestLuaIntf&#34;</span> <span class="o">..</span> <span class="s2">&#34;/?.lua&#34;</span>

<span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Lua Env: test.lua main() middle&#34;</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">&#34;log&#34;</span><span class="p">):</span><span class="n">new</span><span class="p">(</span><span class="s2">&#34;svn_log&#34;</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">&#34;Lua Env: test.lua main() call info() %d...&#34;</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">&#34;Lua Env: test.lua main() end&#34;</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">&#34;./test_ref.lua&#34;</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">&#34;test_module.test_func&#34;</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">&#34;hello world&#34;</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">&amp;</span><span class="n">e</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">cerr</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;Error: &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">e</span><span class="p">.</span><span class="n">what</span><span class="p">()</span> <span class="o">&lt;&lt;</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="