• Lua学习笔记:探究package


    前言
    本篇在讲什么

    理解Lua的package
    本篇需要什么

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

    本篇的特色

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


    ★提高阅读体验★

    👉 ♠ 一级标题 👈

    👉 ♥ 二级标题 👈

    👉 ♣ 三级标题 👈

    👉 ♦ 四级标题 👈


    ♠ 前言

    本篇文章简单了解一下Lua的全局表package,及其表内字段功能


    ♠ 前瞻

    阅读本篇文章需要准备编译Lua源码的工程,详情可参考下面文章

    Lua学习笔记:在Visual Studio中调试Lua源码和打断点

    阅读本篇文章前最好提前了解C/C++和Lua的交互原理,详情可参考下面文章

    Lua学习笔记:C/C++和Lua的相互调用


    ♠ 注册标准库

    首先我们要知道,在创建一个新的Lua虚拟机后,其环境内是没有定义任何函数的,我们需要注册一下标准库以供使用,代码如下

    lua_State* L = luaL_newstate();
    luaL_openlibs(L);
    
    • 1
    • 2

    我们通过函数luaL_openlibs向Lua环境注册一些标准函数


    ♥ luaL_openlibs

    函数原型在在脚本linit.c当中,代码如下所示

    static const luaL_Reg lualibs[] = {
      {"", luaopen_base},
      {LUA_LOADLIBNAME, luaopen_package},
      {LUA_TABLIBNAME, luaopen_table},
      {LUA_IOLIBNAME, luaopen_io},
      {LUA_OSLIBNAME, luaopen_os},
      {LUA_STRLIBNAME, luaopen_string},
      {LUA_MATHLIBNAME, luaopen_math},
      {LUA_DBLIBNAME, luaopen_debug},
      {NULL, NULL}
    };
    
    
    LUALIB_API void luaL_openlibs (lua_State *L) {
      const luaL_Reg *lib = lualibs;
      for (; lib->func; lib++) {
        lua_pushcfunction(L, lib->func);
        lua_pushstring(L, lib->name);
        lua_call(L, 1, 0);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    可以看到在for循环当中以此把lualibs数组内的函数和函数名字注册到了lua_State


    ♥ luaopen_package

    函数luaopen_package是注册package表的核心函数,其源码定义在脚本loadlib.c当中,如下所示

    LUALIB_API int luaopen_package (lua_State *L) {
      int i;
      /* create new type _LOADLIB */
      luaL_newmetatable(L, "_LOADLIB");
      lua_pushcfunction(L, gctm);
      lua_setfield(L, -2, "__gc");
      /* create `package' table */
      luaL_register(L, LUA_LOADLIBNAME, pk_funcs);
    #if defined(LUA_COMPAT_LOADLIB) 
      lua_getfield(L, -1, "loadlib");
      lua_setfield(L, LUA_GLOBALSINDEX, "loadlib");
    #endif
      lua_pushvalue(L, -1);
      lua_replace(L, LUA_ENVIRONINDEX);
      /* create `loaders' table */
      lua_createtable(L, 0, sizeof(loaders)/sizeof(loaders[0]) - 1);
      /* fill it with pre-defined loaders */
      for (i=0; loaders[i] != NULL; i++) {
        lua_pushcfunction(L, loaders[i]);
        lua_rawseti(L, -2, i+1);
      }
      lua_setfield(L, -2, "loaders");  /* put it in field `loaders' */
      setpath(L, "path", LUA_PATH, LUA_PATH_DEFAULT);  /* set field `path' */
      setpath(L, "cpath", LUA_CPATH, LUA_CPATH_DEFAULT); /* set field `cpath' */
      /* store config information */
      lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATHSEP "\n" LUA_PATH_MARK "\n"
                         LUA_EXECDIR "\n" LUA_IGMARK);
      lua_setfield(L, -2, "config");
      /* set field `loaded' */
      luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 2);
      lua_setfield(L, -2, "loaded");
      /* set field `preload' */
      lua_newtable(L);
      lua_setfield(L, -2, "preload");
      lua_pushvalue(L, LUA_GLOBALSINDEX);
      luaL_register(L, NULL, ll_funcs);  /* open lib into global table */
      lua_pop(L, 1);
      return 1;  /* return 'package' table */
    }
    
    • 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
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    我们挑重点来看,在开始向环境当中注册了一个名为package的表,并且在表中注册了两个名为loadlibseeall的函数

    static const luaL_Reg pk_funcs[] = {
      {"loadlib", ll_loadlib},
      {"seeall", ll_seeall},
      {NULL, NULL}
    };
    
    
    /* create `package' table */
    luaL_register(L, LUA_LOADLIBNAME, pk_funcs);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    随后依次为表package设置了loaders、path、cpath、config、loaded、preload参数

    lua_setfield(L, -2, "loaders");  /* put it in field `loaders' */
    setpath(L, "path", LUA_PATH, LUA_PATH_DEFAULT);  /* set field `path' */
    setpath(L, "cpath", LUA_CPATH, LUA_CPATH_DEFAULT); /* set field `cpath' */
    lua_setfield(L, -2, "config");
    lua_setfield(L, -2, "loaded");
    lua_setfield(L, -2, "preload");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    最后又向loaded中的全局表_G中注册了两个名为requiremodule的函数

    static const luaL_Reg ll_funcs[] = {
      {"module", ll_module},
      {"require", ll_require},
      {NULL, NULL}
    };
    
    
    lua_pushvalue(L, LUA_GLOBALSINDEX);
    luaL_register(L, NULL, ll_funcs);  /* open lib into global table */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    所以经过了上述一通操作,我们的全局表package最终变成了下面这个样子

    {
    	["config"]   = "...",
    	["cpath"]    = "...",
    	["loaded"]   = {
    		["_G"]   = {
    			["require"] = function,
    			["module"]  = function,
    		}
    	},
    	["loaders"]  = {},
    	["loadlib"]  = function,
    	["path"]     = "...",
    	["preload"]  = {},
    	["seeall"]   = function,
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    ♠ package的参数

    从上边创建package的时候我们其实已经知道,其中所包含的字段,这里我们简单了解一下这些个字段都是干嘛用的

    通过pairs遍历package的key值,也可以很直观的看到package中所有的参数

    在这里插入图片描述


    ♥ loaders

    存储加载器的表,打印出来如下图所示

    在这里插入图片描述

    其对应的四个加载器,定义在loadlib.c中,代码如下图所示

    static const lua_CFunction loaders[] =
      {loader_preload, loader_Lua, loader_C, loader_Croot, NULL};
    
    • 1
    • 2

    这个加载器可以理解为解析文件的方式,比如我们require了一个lua文件,那么会通过loader_Lua方法去解析文件,如果require了一个c文件,那么会通过loader_C方法去解析文件

    后续得空专门为require的流程再写一遍笔记


    ♥ cpath

    c加载器的搜索路径,loader_C方法会从package.cpath路径下搜索对应的文件

    在这里插入图片描述


    ♥ path

    Lua文件加载器的搜索路径,Luader_Lua方法会从package.path下搜索对应的Lua文件

    在这里插入图片描述

    在这里插入图片描述

    我们在D盘某个路径下有个lua文件test3.lua,我们从c盘的一个lua文件正常是require不到他的,现在只要补充搜索路径即可,如下所示

    package.path = package.path .. ";D:\\lua_src\\?.lua"
    require "test3"
    
    • 1
    • 2

    ♥ loaded

    管理全局函数和已经加载的标准库,在loaded内存在表__G,全局函数和全局表都会存其中管理

    在这里插入图片描述

    管理一些已经require的模块,require的时候首先会判断package.loaded内是否已经加载过了,如果加载过了直接返回

    在这里插入图片描述


    ♥ loadlib

    加载c库中的方法,返回的是一个lua_CFunction,只加载不执行


    ♥ seeall

    为模块设置一个元表,其__index字段引用_G,以便该模块继承全局环境的值。作为功能模块的选项,功能等同于下面代码

    setmetatable(M, {__index = _G});
    
    • 1

    ♠ 推送

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

    ♠ 结语

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

    👉 本文属于原创文章,转载请评论留言,并在转载文章头部著名作者出处👈
  • 相关阅读:
    阿里云大数据工程师(ACP)认证考试大纲
    行业选择有多重要?选Java我没后悔
    【python环境搭建】一台电脑下安装不同python版本时如何安装模块
    Linux常用命令总结
    SQLite 3.44.0 发布!
    关于 Redis 中集群
    搜索引擎站群霸屏排名源码系统+关键词排名 前后端完整的搭建教程
    Vue.extend()实现每个页面弹框
    【无标题】
    分布式与微服务的区别
  • 原文地址:https://blog.csdn.net/Mr_Sun88/article/details/133042226