本篇在讲什么 Lua的require相关的内容 本篇需要什么 对Lua语法有简单认知 对C++语法有简单认知 依赖Visual Studio工具 本篇的特色 具有全流程的图文教学 重实践,轻理论,快速上手 提供全流程的源码内容 |
★提高阅读体验★ 👉 ♠ 一级标题 👈👉 ♥ 二级标题 👈👉 ♣ 三级标题 👈👉 ♦ 四级标题 👈 |
目录
- ♠ 前言
- ♠ 前瞻
- ♠ require的定义
- ♠ Lua的加载器
- ♠ 简单的断点调试
- ♠ require自定义文件
- ♥ 修改Lua_Path
- ♠ 自定义加载器
- ♥ 自定义
- ♠ 推送
- ♠ 结语
♠ 前言
想在Lua代码中去require非.lua
后缀的文件,发现需要去自定义一下Lua的加载器,这里我们先从c源码的角度去探究一下require
的过程,再自定义一个加载器去加载指定后缀的Lua代码文件
♠ 前瞻
阅读本篇文章需要准备编译Lua源码的工程,详情可参考下面文章
Lua学习笔记:在Visual Studio中调试Lua源码和打断点
阅读本篇文章前最好提前了解C/C++和Lua的交互原理,详情可参考下面文章
Lua学习笔记:C/C++和Lua的相互调用
阅读本篇文章前最好提前了解Lua的package,详情可参考下面文章
Lua学习笔记:探究package
阅读本篇文章前最好提前了解Lua的词法分析,详情可参考下面文章
Lua学习笔记:词法分析
♠ require的定义
require的本质其实是注册在_G
内的一个全局函数,在Lua中如果把_G
去dump一下,可以看到方法名字在其中
原型函数定义在源码loadlib.c
中,名为ll_require
的函数,其在luaopen_package
函数中被注册到全局表当中
♠ Lua的加载器
在Lua初始化的时候定义了几个默认加载器去读取文件内容,在loadlib.c
文件中的luaopen_package
方法中去初始化了加载器
其中loader_lua
就是用来加载Lua文件的默认加载器,只要是require一个lua文件就默认会执行
♠ 简单的断点调试
我们预先准备了两个Lua文件,加几个断点来看一下执行流程,Lua代码如下所示
我们分别在ll_require
和loader_Lua
函数中添加了断点,在执行后,依次执行了两个函数,最终在luaL_loadfile
函数中完成对require文件的词法分析
♠ require自定义文件
默认的对Lua的读取只支持.lua
后缀的文件,我们可以通过几种不同的方式去改变这一策略
♥ 修改Lua_Path
Lua的package表中字段path
留存的就是文件的搜索路径,我们可以通过补充搜索路径来达到目的
可以直接通过修改源码中的LUA_PATH_DEFAULT
定义,去使得Lua程序可以加载.luac
结尾的文件,执行后可直接被require
源码可能并不是能随便修改的,我们也可以直接在Lua代码中为package.path
添加搜索路径,如下图所示
♠ 自定义加载器
如果require的文件后缀不是.lua
在require的时候会报错,原因是在默认加载器loader_Lua
并没有设定对其他后缀的加载方式,不过我们可以自定义加载器
♥ 自定义
如下述代码所示,在执行脚本加载之前,将loaders
的默认加载器替换成我们自定义的函数就可以了
int myLuaLoader(lua_State * L)
{std::string filename(luaL_checkstring(L, 1));return 1;
}void addLuaLoader(lua_State * _state, lua_CFunction func)
{if (!func) return;lua_getglobal(_state, "package"); /* L: package */lua_getfield(_state, -1, "loaders"); /* L: package, loaders */lua_pushcfunction(_state, func); /* L: package, loaders, func */for (int i = (int)(lua_objlen(_state, -2) + 1); i > 2; --i){lua_rawgeti(_state, -2, i - 1); /* L: package, loaders, func, function */lua_rawseti(_state, -3, i); /* L: package, loaders, func */}lua_rawseti(_state, -2, 2); /* L: package, loaders */lua_setfield(_state, -2, "loaders"); /* L: package */lua_pop(_state, 1);}int main()
{lua_State* L = luaL_newstate();luaL_openlibs(L);addLuaLoader(L, myLuaLoader);luaL_dofile(L, "lua_src/test.lua");lua_close(L);
}
我们在myLuaLoader
函数中加入断点,在执行程序后已经可以获取到对应的文件名字了
接下来,我们补充函数内容,让其可以去识别到特定后缀的文件,很简单,直接拼接.luac
结尾后缀,然后去loadfile,大家可以根据自己需求去扩展
int myLuaLoader(lua_State * L)
{const char *filePath = "D:\\work\\cToLua\\Debug_Lua\\";const char *name = luaL_checkstring(L, 1);const char *suffix = ".luac";char *filename = (char *)malloc(strlen(filePath) + strlen(name)+ strlen(suffix));sprintf(filename, "%s%s%s", filePath, name, suffix);if (luaL_loadfile(L, filename) != 0)return 0;return 1;
}
♠ 推送
- Github
https://github.com/KingSun5
♠ 结语
若是觉得博主的文章写的不错,不妨关注一下博主,点赞一下博文,另博主能力有限,若文中有出现什么错误的地方,欢迎各位评论指摘。