• Lua笔记——8.Lua & C


    Lua是一门嵌入式语言,提供了完备的 C API 使Lua 代码可以很方便的和其他宿主程序相互调用来扩展程序功能。

    Lua can be embedded and extended with code or applications written in other languages. Code and values in another language can be exposed to Lua and vice-versa.

    Lua与C语言交互

    • 在C中嵌入Lua脚本可以让用户在不重新编译代码的情况下只修改Lua代码来更新程序。
    • 在Lua中调用C函数则可以提高程序的运行效率。

    一、搭建环境

    根据自己使用的操作系统来搭建lua环境(这里使用 Win + VS2017 ,Lua 5.3)

    二、理解C API

    Lua和C交互的部分称为C API。C API是一个C代码与Lua进行交互的函数集

    重要的头文件:

    1. lua.h: (the basic API)provides the primitive functions for all interactions between C and Lua,主要包含C和Lua之间交互的基础函数,以 lua_ 开头。
    2. lauxlib.h:(lua auxiliary library —— lauxlib)provides higher-level functions for some common tasks,主要为一些命令或任务提供更高层次的辅助函数,以 luaL_ 开头。
    3. lualib.h:主要包含Lua打开内置库的函数。

    三、理解Lua堆栈

    Lua和C之间通过一个虚拟的Lua栈来进行数据交换操作,实现Lua和C之间的相互调用

    1. Lua代码中,严格遵守Lua栈的LIFO原则,只能操作栈顶元素
    2. C代码中,可以操作Lua栈中任意元素,即可以在栈的任意位置删除和插入元素
    3. Lua栈中可以存放各种类型的变量,例如number、string、函数、线程等

    C调用Lua

    C调用Lua函数

    (1) 引入Lua头文件

    C:

    #include <lua.h>
    #include <lauxlib.h>
    #include <lualib.h>
    

    C++:

    #include <lua.hpp>
    

    (2) 创建lua_State

    lua_State: An opaque structure that points to a thread and indirectly (through the thread) to the whole state of a Lua interpreter. The Lua library is fully reentrant: it has no global variables. All information about a state is accessible through this structure.
    A pointer to this structure must be passed as the first argument to every function in the library, except to lua_newstate, which creates a Lua state from scratch.

    lua_State:

    • 一个指向线程并间接通过该线程指向Lua解释器整个状态的不透明的结构。
    • 此时,该状态下的Lua解释器库是空的,没有任何的全局变量。
    • 并且所有关于状态的信息都可以通过这个不透明的结构来得到。
    • 库中的所有函数的第一个参数都必须为这个结构的指针,除了lua_newstate函数,lua_newstate函数从头创建lua_State。
        lua_State *L = luaL_newstate();
    

    (3) 打开所需的Lua库

        luaL_openlibs(L);
    

    (4) 加载、运行Lua代码

        if (luaL_dofile(L, fileName))
            cout << "Failed to load the lua script ." << endl;
    

    将Lua代码文件作为Lua代码块加载并运行,如果没有错误则返回false,等同于代码:

        if(luaL_loadfile(L,fileName) || lua_pcall(L,0,0,0))
            cout << "Failed to load the lua script ." << endl;
    

    其中,luaL_loadfile、luaL_load_filex、luaL_loadstring都和lua_load函数一样,都是只加载代码块而不运行它,如果没有错误,lua_load函数会将编译后的代码块作为一个Lua函数压入Lua虚拟栈的顶部,否则,则会向Lua栈内压入一条错误信息,该错误信息可以使用lua_tostring(L,-1)取出。

    lua_load: Loads a Lua chunk without running it. If there are no errors, lua_load pushes the compiled chunk as a Lua function on top of the stack. Otherwise, it pushes an error message.

    随即用lua_pcall(L,0,0,0)在保护模式下调用刚才压入栈的Lua函数(即给定的Lua代码文件),和lua_call一样,lua_pcall也会将函数以及函数的参数从Lua栈上移除。

    lua_pcall: Calls a function in protected mode.Like lua_call, lua_pcall always removes the function and its arguments from the stack.

    (5) 进行虚拟栈操作

        lua_getglobal(L, "add");
        lua_pushnumber(L, 10);
        lua_pushnumber(L, 20);
    
        if (lua_pcall(L, 2, 1, 0))
            cout << "Failed to call the lua func ." << endl;
    

    (6) 获取操作执行结果

        res = lua_tonumber(L, -1);
        lua_pop(L,1);
    

    (7) 关闭Lua栈

        lua_close(L);
    

    完整代码( Win + VS2017,Lua 5.3):

    //file: luaTest.cpp
    #include "pch.h"
    #include <lua.hpp>
    #include <direct.h>
    #include <iostream>
    using namespace std;
    
    void echoCwd()
    {
        char buffer[_MAX_PATH];
        _getcwd(buffer, _MAX_PATH);
        cout << "Current Work Directory : " << buffer << endl;
    }
    
    int luaAdd(lua_State *L ,const char *fileName , int x, int y)
    {
        int res = 0;
        if (luaL_dofile(L, fileName))
            cout << "Failed to load this lua script ." << endl;
    
        lua_getglobal(L, "add");
        lua_pushnumber(L, 10);
        lua_pushnumber(L, 20);
    
        if (lua_pcall(L, 2, 1, 0))
            cout << "Failed to call this lua func ." << endl;
    
        res = lua_tonumber(L, -1);
        lua_pop(L,1);
        return res;
    }
    
    int main()
    {
        //Make sure the file add.lua is in the right place or you can use orther filePath .
        const char *fileName = "add.lua";
        echoCwd();
        int res = 0;
    
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);
    
        res = luaAdd(L,fileName,10,20);
        cout << "res = " << res << endl;
    
        lua_close(L);
    
        char c;
        cin >> c;
        return 0;
    }
    

    Lua代码:

    --file: add.lua
    
    add = function (x, y)
        return x + y
    end
    

    Build:

    运行并输出:

    Current Work Directory : *****//你项目的当前路径
    res = 30
    

    C调用Lua表table

    (1)-(4)步类比C调用Lua函数的步骤

    (5) 进行虚拟栈操作

    查询表的值:

        lua_getglobal(L, "tipswin");
        lua_getfield(L, -1, "content");
    

    等同于代码:

        lua_getglobal(L,"tipswin");
        lua_pushstring(L,"name");
        lua_gettable(L,-2);
    

    lua_getfield或者lua_gettable之后,压入栈的表以及key值参数都将被出栈,之后将查询到的返回值入栈

    修改表的值:

        lua_getglobal(L, "tipswin");
        lua_pushstring(L, "Time Waits For No One .");
        lua_setfield(L,-2,"content");
        lua_pop(L, -1);
    

    完整代码:

    //file: luaTest.cpp
    #include "pch.h"
    #include <lua.hpp>
    #include <Cstring>
    #include <iostream>
    using namespace std;
    
    int main()
    {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);
    
        //Make sure the file luaCode.lua is in the right place or you can use orther file path .
        const char *fileName = "luaCode.lua";
    
        if(luaL_dofile(L, fileName))
            cout << "Failed to load this lua script ." << endl;
    
        //方式一
        lua_getglobal(L,"tipswin");
        lua_pushstring(L,"name");
        lua_gettable(L,-2);
        string name = lua_tostring(L,-1);
        cout << "TipsWin's Name is : " << name.c_str() << endl;
        lua_pop(L,-1);
        //方式二
        lua_getglobal(L, "tipswin");
        lua_getfield(L, -1, "content");
        string content = lua_tostring(L, -1);
        cout << "TipsWin's Content is : " << content.c_str() << endl;
        lua_pop(L, -1);
    
        //修改Lua表中的值
        lua_getglobal(L, "tipswin");
        lua_pushstring(L, "Time Waits For No One .");
        lua_setfield(L,-2,"content");
        lua_pop(L, -1);
    
        //修改之后的Content
        lua_getglobal(L, "tipswin");
        lua_getfield(L, -1, "content");
        content = lua_tostring(L, -1);
        cout << "After Modify The TipsWin's Content is : " << content.c_str() << endl;
        lua_pop(L, -1);
    
        lua_close(L);
    
        char c;
        cin >> c;
        return 0;
    }
    

    当前工作路径下Lua代码:

    --file: luaCode.lua
    
    tipswin = {name = "Tips",content= "Please Notice this Window ."}
    
    

    Lua调用C

    通过在C中注册函数给lua调用

    封装成c动态链接库,在lua中require

    在LuaJIT里面可以使用ffi高性能的调用C

    REF

    文档:

    http://www.lua.org/manual/5.3/manual.html

    http://lua-users.org/wiki/BindingCodeToLua

    博客:

    https://www.jb51.net/article/132851.htm

    https://blog.csdn.net/alexwoo0501/article/details/50916037

  • 相关阅读:
    Groovy Simple file download from URL
    Connect to URL and dump webpage in Groovy Stack Overflow
    bash脚本测网络流量
    HTTPBuilder Overview
    nginx设置代理账号密码
    引爆流行
    RRDtool About RRDtool
    老王 python ,
    Ubuntu下安装GeoIP
    实时查看网络的流量
  • 原文地址:https://www.cnblogs.com/sylvan/p/12251786.html
Copyright © 2020-2023  润新知