• Lua学习笔记:require非.lua拓展名的文件


    前言
    本篇在讲什么

    Lua的require相关的内容
    本篇需要什么

    Lua语法有简单认知
    C++语法有简单认知
    依赖Visual Studio工具

    本篇的特色

    具有全流程的图文教学
    重实践,轻理论,快速上手
    提供全流程的源码内容


    ★提高阅读体验★

    👉 ♠ 一级标题 👈

    👉 ♥ 二级标题 👈

    👉 ♣ 三级标题 👈

    👉 ♦ 四级标题 👈


    ♠ 前言

    想在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_requireloader_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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    我们在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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    ♠ 推送

    • Github
    https://github.com/KingSun5
    
    • 1

    ♠ 结语

    若是觉得博主的文章写的不错,不妨关注一下博主,点赞一下博文,另博主能力有限,若文中有出现什么错误的地方,欢迎各位评论指摘。

    👉 本文属于原创文章,转载请评论留言,并在转载文章头部著名作者出处👈
  • 相关阅读:
    SpringBoot项目的创建
    基于MATLAB开发AUTOSAR软件应用层模块-part11.AUTOSAR Dictionary-2 mapping
    Docker面试整理-Docker Swarm与Kubernetes有什么区别?
    2023年windows DockerDeskTop最新款4.18.0 全程保姆级安装
    第三部分—数据结构与算法基础_3. 受限线性表
    VS配置opencv
    SpringBoot+Vue 的网上图书商城管理系统
    【微信小程序】项目初始化
    基于java+springboot+vue实现的旅游管理系统(文末源码+lw+ppt)23-402
    shiro之AccessControlFilter()
  • 原文地址:https://blog.csdn.net/Mr_Sun88/article/details/133277157