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进行交互的函数集
重要的头文件:
- lua.h: (the basic API)provides the primitive functions for all interactions between C and Lua,主要包含C和Lua之间交互的基础函数,以 lua_ 开头。
- lauxlib.h:(lua auxiliary library —— lauxlib)provides higher-level functions for some common tasks,主要为一些命令或任务提供更高层次的辅助函数,以 luaL_ 开头。
- lualib.h:主要包含Lua打开内置库的函数。
三、理解Lua堆栈
Lua和C之间通过一个虚拟的Lua栈来进行数据交换操作,实现Lua和C之间的相互调用
- Lua代码中,严格遵守Lua栈的LIFO原则,只能操作栈顶元素
- C代码中,可以操作Lua栈中任意元素,即可以在栈的任意位置删除和插入元素
- 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.ogeek.net/article/132851.htm
https://blog.csdn.net/alexwoo0501/article/details/50916037
|
请发表评论