lua-intf使用手册

| 分类 lua之基础  | 标签 lua  luaintf  lua热更新  C++  使用手册  API  C++11  LuaRef  LuaState  LuaBinding  热更新 

“翻译”自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();

    static void go_home();

    static std::string home_url();
    static void set_home_url(const std::string &url);

    std::string url() const;
    void set_url(const std::string &url);
    std::string resolve_url(const std::string &uri);

    // 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;
    }

    ~SimpleLog();
    void Log(const string &str);
    void BindLua(lua_State *L);

private:
    SimpleLog();
};

SimpleLog::SimpleLog() {}
SimpleLog::~SimpleLog() {}

void SimpleLog::Log(const string &str) {
    cout << "C++ Env: SimpleLog::Log() " << str << endl;
}

namespace {
    using LuaRef = LuaIntf::LuaRef;

    void LuaLog(const string &str) {
        SimpleLog::getInstance()->Log(str);
    }

    namespace LuaSimpleLog {
        void Bind(lua_State *L) {
            assert(L);
            LuaIntf::LuaBinding(L).beginModule("simple_log")
                     .addFunction("log", &LuaLog)
                     .endModule();
        }
    };
};

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()

    print("Lua Env: test.lua main() begin")
    local p = "../TestLuaIntf"
    package.path = package.path .. ";" .. p .. "/" .. "TestLuaIntf" .. "/?.lua"

    print("Lua Env: test.lua main() middle")

    local log = require("log"):new("svn_log")
    log:info("Lua Env: test.lua main() call info() %d...", 1)

    print("Lua Env: test.lua main() end")
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;

    try {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);

        LuaIntf::LuaContext ctx(L);
        ctx.doFile("./test_ref.lua");

        LuaRef func(L, "test_module.test_func");
        func(1, true, "hello world");
    } catch (const LuaIntf::LuaException &e) {
        cerr << "Error: " << e.what() << endl;
    }

    return 0;
}

在编写对应的test_ref.lua 代码如下

local M = {}
local modelName = "test_module"
_G[modelName] = M

function M.test_func(int_v, bool_v, string_v)
    print(int_v)
    print(bool_v)
    print(string_v)
end

-- M.test_func(1, true, "123")

return M

OK,现在看一下运行效果!

更多资料




如果本篇文章对您有所帮助,您可以通过微信(左)或支付宝(右)对作者进行打赏!


上一篇     下一篇