• lua require


    环境: Lua5.1 

    工具:LuaForWindows(http://files.luaforge.net/releases/luaforwindows/luaforwindows

    require作用类似于C/C++中的#include,特性:

    1. 根据搜索目录加载指定模块

    2. 判定模块是否已加载,避免重复加载

    require加载的模块数据存储在 package.loaded 表中,其存储方式以模块名为key,以返回值(若无返回值,默认true)为value进行存储的。比如:

    -- 导入任意lua文件
    require("Demo")                              
    require("Demo")                               
    require ("Desktop.Demo2")                -- 会将“.”替换为“/”
    require ("Desktop/File/Demo1")
    
    -- 遍历加载的lua文件表
    for key, value in pairs(package.loaded) do
        print(key,value)
    end
    
    --[[
    输出:
    string    table: 002DDE40
    debug    table: 002DDF08
    package    table: 002DDAF8
    _G    table: 008C1C88
    io    table: 002DDC88
    os    table: 002DDDC8
    table    table: 002DDB98
    math    table: 002DDEB8
    coroutine    table: 002DDA58
    
    Demo    true                    -- 新加,导入的重复文件不会在加载
    Desktop.Demo2    true           -- 新加    
    Desktop/File/Demo3    true      -- 新加                       
    ]]      

    如果我们试图加载一个不存在的lua文件:

    -- 加载不存在的模块
    require("ErrorModel")
    
    --[[
    错误堆栈信息:
    lua: require.lua:21: module 'ErrorModel' not found:
        no field package.preload['ErrorModel']
        no file '.ErrorModel.lua'
        no file 'E:Program Files (x86)Lua5.1luaErrorModel.lua'
        no file 'E:Program Files (x86)Lua5.1luaErrorModelinit.lua'
        no file 'E:Program Files (x86)Lua5.1ErrorModel.lua'
        no file 'E:Program Files (x86)Lua5.1ErrorModelinit.lua'
        no file 'E:Program Files (x86)Lua5.1luaErrorModel.luac'
        no file '.ErrorModel.dll'
        no file '.ErrorModel51.dll'
        no file 'E:Program Files (x86)Lua5.1ErrorModel.dll'
        no file 'E:Program Files (x86)Lua5.1ErrorModel51.dll'
        no file 'E:Program Files (x86)Lua5.1clibsErrorModel.dll'
        no file 'E:Program Files (x86)Lua5.1clibsErrorModel51.dll'
        no file 'E:Program Files (x86)Lua5.1loadall.dll'
        no file 'E:Program Files (x86)Lua5.1clibsloadall.dll'
    stack traceback:
        [C]: in function 'require'
        require.lua:21: in main chunk
        [C]: ?
    ]]

    我们看下require的实现:

    -- 参考:Lua程序设计(第2版)
    function require(name)
        -- 判定模块是否已加载,若已加载将直接返回,避免重复加载
        if not package.loaded[name] then 
            -- 搜索模块
            local loader = findloader(name)
            if loader == nil then 
                -- 模块不存在,报错
                error("unable to load module " .. name)
            end
    
            -- 将模块先默认设置为true
            package.loaded[name] = true
            -- 初始化模块,若模块存在返回值,将true替换为返回值数据
            local res = loader(name)
            if res ~= nil then 
                package.loaded[name] = res
            end 
        end 
        -- 返回模块数据
        return package.loaded[name]
    end         

    如上代码,我们可以理解require实现原理。因此重载模块数据的话,我们可以这样:

    package.loaded["*"] = nil      -- 置空已加载的模块数据
    require("*")              -- 再次加载

    对比着错误的堆栈信息我们可以简单的了解到findloader的搜索路径主要有:

    1.  Lua文件路径相关

    2. C库文件路径相关

    我们来说明下findloader搜索路径相关。lua通过LUA_PATH进行初始化,C通过LUA_CPATH进行初始化:

    // luaconf.h
    // Environment variable names for path overrides and initialization code
    #define LUA_PATH    "LUA_PATH"
    #define LUA_CPATH    "LUA_CPATH"

    LUA_PATH,LUA_CPATH没有相关的变量,会通过LUA_PATH_DEFAULT,LUA_CPATH_DEFAULT来进行默认初始化,代码如下:

    // luaconf.h
    /*
    ** In Windows, any exclamation mark ('!') in the path is replaced by the
    ** path of the directory of the executable file of the current process.
    */
    #define LUA_LDIR    "!\lua\"
    #define LUA_CDIR    "!\"
    #define LUA_PATH_DEFAULT 
      ".\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\init.lua;"
    #define LUA_CPATH_DEFAULT 
      ".\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll"

    初始化后,lua相关放置到package.path中,C库相关放置到package.cpath中。示例:

    print("package.path路径相关:")
    print(package.path)
    --[[
    -- 为了便于查看,进行了分行
    ;
    .?.lua;
    E:Program Files (x86)Lua5.1lua?.lua;
    E:Program Files (x86)Lua5.1lua?init.lua;
    E:Program Files (x86)Lua5.1?.lua;
    E:Program Files (x86)Lua5.1?init.lua;
    E:Program Files (x86)Lua5.1lua?.luac
    ]]
    
    print("package.cpath路径相关:")
    print(package.cpath)
    --[[
    -- 为了便于查看,进行了分行
    .?.dll;
    .?51.dll;
    E:Program Files (x86)Lua5.1?.dll;
    E:Program Files (x86)Lua5.1?51.dll;
    E:Program Files (x86)Lua5.1clibs?.dll;
    E:Program Files (x86)Lua5.1clibs?51.dll;
    E:Program Files (x86)Lua5.1loadall.dll;
    E:Program Files (x86)Lua5.1clibsloadall.dll
    ]]

    如上其模块名的显示"?",在lua中,其搜索的路径实质上属于模板路径。即在搜索模块时,首先会把模块名替换模块路径下的"?",再进行通过package.searchpath搜索。比如:

    ?; ?.lua; c:windows?;  /usr/local/lua/lua/?/?.lua

    关于package.searchpath的类似实现:

    function search(modname, path)
        -- 将“.”替换为“/”
        modname = string.gsub(modname, "%.", "/")
    
        local msg = {}
        for c in string.gmatch(path, "[^;]+") do 
            -- 将“?”替换为模块名
            local fname = string.gsub(c, "?", modname)
            -- 检测文件是否存在
            local f = io.open(fname)
            if f then 
                f:close()
                return fname
            else 
                msg[#msg + 1] = string.format("
    	 no file '%s'", fname)
            end 
        end 
        return nil, table.concat(msg)       -- 没有找到
    end 

    而对于搜索所有文件主要通过package.searchers会列出require使用的所有搜索器,主要有三部分:

    1. 预加载搜索,通过package.preload来进行

    2. Lua中搜索,通过package.path获取搜索路径,成功后会调用loadFile加载

    3. C库中搜索,通过package.cpath获取搜索路径,成功后,会调用loadlib来加载

    加载成功后,会添加到package.loaded中。

  • 相关阅读:
    在实践中培养学生学习软件工程的兴趣
    软件工程课程设计指导随笔
    软件工程——个人总结
    软件工程第二次作业——结对编程
    个人博客作业三:微软小娜APP的案例分析
    嵌入式软件设计第12次实验报告
    嵌入式软件设计第11次实验报告
    嵌入式软件设计第09实验报告
    嵌入式软件设计第10次实验报告
    嵌入式软件设计第7次实验报告8
  • 原文地址:https://www.cnblogs.com/SkyflyBird/p/7851752.html
Copyright © 2020-2023  润新知