C++ 调用 Lua 函数

零、前言

Lua 作为一门脚本语言,可以作为 “配置文件”、“动态逻辑脚本” 等角色作用于宿主程序。

因为他是一门语言,所以他有以下的好处:

1. Lua 会处理语法细节,后续维护简单,并且可以有注释。
2. 可以编写逻辑,达到复杂的配置。

如果我们的程序需要进行一些 “下发配置” 时,一般会考虑选择 “json”、“文件” 等形式。但是如果 “配置” 内容较为复杂,则可以考虑 Lua 了,具体可以查看以下分享。

一、运行 Lua 文件

在之前 “C++ 与 Lua 交互异常处理” 的文章中,已分享如何在 C/C++ 中使用 Lua 文件,这里复习一下。

可以通过 lua_calllua_pcall 两个函数调用 Lua 代码。

int lua_call(lua_State *L, int nargs, int nresults);

int lua_pcall(lua_State *L, int nargs, int nresults, int errfunc);

两者均用于在 C/C++ 代码中调用 Lua 函数,不同点在于:

  • lua_call 会将代码中的异常直接抛出,导致程序中断。
  • lua_pcall 提供一个保护模式运行 Lua 代码,即使发生异常,也会被捕获,并可以通过第四个参数的错误处理函数处理错误,程序不会因此而中断。

参数:

  • 参数 L: Lua State 的指针。
  • 参数 nargs: 传递给被调用函数的参数个数。
  • 参数 nresults: 期望的返回值个数。
  • 参数 errfunc: 错误处理函数的索引,用于处理发生的错误。如果为 0,则错误信息会被压入栈顶。

返回值:

  • 函数调用成功,返回 0,并将返回值压入栈中。
  • 如果函数调用发生错误,返回一个非零值,并将错误信息压入栈中。

错误处理的细节可以翻阅之前的 “C++ 与 Lua 交互异常处理” 文章。

举个例子

我们通过 lua_pcall 加载一个 Lua 文件,然后在调用 Lua 中的一个函数计算数值,最后获取返回结果。

Lua 文件的内容

function luaFunction(x, y)
    return (x ^ 2 * math.sin(y)) / (1 - x)
end

接下来看宿主如何运行和调用,可以结合着注释理解。思路是:

  1. 使用 luaL_loadfile 加载 Lua 文件
  2. 使用 lua_pcall 运行 Lua 文件,此时 Lua 中的 luaFunction 是一个全局变量
  3. luaFunction 压入栈,同时将需要传递的参数压入栈,然后通过 lua_pcall 调用函数
  4. 最后使用出栈函数获取结果,因为这里为数值,所以使用 lua_tonumberx 出栈函数
// C++ 入口
void cppCallLuaFunction() {
    std::string fname = PROJECT_PATH + "/5、C++调用Lua代码/调用Lua函数/调用Lua函数.lua";
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    // 加载 Lua 文件、运行 Lua 文件
    if (luaL_loadfile(L, fname.c_str()) || lua_pcall(L, 0, 0, 0)) {
        printf("can't run config. file: %s\n", lua_tostring(L, -1));
        lua_close(L);
        return;
    }

    double calResult = 0;
    bool isSuccess = false;
    // 调用 Lua 文件中的函数
    isSuccess = callLuaFunction(L, 2, 34, &calResult);
    if (isSuccess) {
        printf("调用 Lua 成功 luaFunction: %f\n", calResult);
    } else {
        printf("调用 Lua 失败\n");
    }

    lua_close(L);
}

// 调用 Lua 函数
bool callLuaFunction(lua_State *L, double x, double y, double *result) {
    // 获取全局中的 luaFunction 变量,将其压入栈中
    int getResult = lua_getglobal(L, "luaFunction");
    if (getResult == LUA_TNIL) {
        printf("lua_getglobal get failure\n");
        return false;
    }
    // 将 x 和 y 入栈,会作为 luaFunction 函数的两个参数
    lua_pushnumber(L, x);
    lua_pushnumber(L, y);

    // 运行 luaFunction 函数
    if (lua_pcall(L, 2, 1, 0) != LUA_OK) {
        printf("error running function 'f': %s", lua_tostring(L, -1));
        return false;
    }

    int isNum;
    // 获取 luaFunction 的返回值
    *result = lua_tonumberx(L, -1, &isNum);
    if (!isNum) {
        printf("function 'luaFunction' should return a number.");
        return false;
    }

    // 需要将返回值弹出
    lua_pop(L, 1);
    return true;
}

// --> 调用 Lua 成功 luaFunction: -2.116331

二、lua_getglobal

在上面一小节中使用到了 lua_getglobal ,这里详细阐述下这一函数的作用

int (lua_getglobal) (lua_State *L, const char *name);

作用:

用于获取全局变量的值,并将其压入 Lua 栈中。

参数:

  • 参数 L: Lua 状态机(Lua state)指针。
  • 参数 name: 要获取的全局变量的名称,以字符串形式表示。

返回值:

如果成功获取到全局变量,则返回该变量在栈中的索引(索引是从 1 开始的整数)。

如果未找到指定的全局变量,则返回 LUA_TNIL。

三、配置文件

在前言一节中,分享用 Lua 文件作为 “配置文件”,这一小节则围绕这一用法展开分享他的好处和讲解如何在 C++ 中调用 Lua 文件。

1、根据环境执行不同逻辑

有时我们需要根据不同的环境,配置一些不同的参数。可以在 Lua 文件中通过 os.getenv 获取系统配置的环境变量,进行返回不同的配置信息。

首先,需要在运行的机器中配置环境变量,我的电脑是 Mac ,配置在 ~/.zshrc 中。

~/.zshrc 配置内容如下所示

export DISPLAY_ENV="Mac"

然后在 Lua 中进行获取使用,Lua 的内容如下

-- DISPLAY_ENV 在环境变量中配置,open ~/.zshrc 可以查看
local displayEnv = os.getenv("DISPLAY_ENV");
print("displayEnv", displayEnv)

if displayEnv == "Mac" then
    width = 3072
    height = 1920
else
    width = 1920
    height = 1080
end

最后通过 C++ 加载和运行该 Lua 文件

void loadConfigUseEnv() {
    std::string filename = PROJECT_PATH + "/5、C++调用Lua代码/Lua作为配置文件/根据环境变量获取值/config.lua";
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    if (luaL_loadfile(L, filename.c_str()) || lua_pcall(L, 0, 0, 0)) {
        printf("can't run config. file: %s\n", lua_tostring(L, -1));
        return;
    }

    int width;
    if (!getGlobInt(L, "width", &width)) {
        printf("Get width failure.");
        return;
    }
    int height;
    if (!getGlobInt(L, "height", &height)) {
        printf("Get height failure.");
        return;
    }

    printf("size: %d x %d\n", width, height);

    lua_close(L);
}

bool getGlobInt(lua_State *L, const char *var, int *result) {
    int isNum;
    // 将 var 对应的值压入栈中
    lua_getglobal(L, var);
    *result = (int) lua_tointegerx(L, -1, &isNum);
    if (!isNum) {
        printf("'%s' should be a number\n", var);
        return false;
    }
    // 将 var 对应值压入栈中的值弹出
    lua_pop(L, 1);
    return true;
}

会输出以下内容

displayEnv	Mac
size: 3072 x 1920

可以看到,Lua 脚本会获取我们电脑的环境变量,根据值执行不同的逻辑,这一点在 json、文本配置文件是无法做到的。

2、预设配置

宿主可以初始化一些配置选项给到 Lua ,Lua 根据所需进行使用,当然也可以自行生成配置项,这一过程只是业务逻辑上的设计。

下面举个例子,假设需要给一个 App 配置一个主题色,颜色用 rgb 进行表示,在 Lua 中可以用 table 进行装载颜色,让数据联系更加紧凑,可以内置一些颜色给到 Lua 进行使用。

话不多说,下面开始演示这一过程是如何使用。

第一步,我们需要先将内置的颜色值设置到 Lua 中。 需要先创建一个表 table ,然后将 key - value 放置到 table 中。

在下面的 setColor 方法中,会先创建一个 table ,然后通过封装的 setColorField 方法将 red、green、blue 放置到 table 中。

setColorField 中会将 key、value 按顺序压栈,然后通过 lua_settable 设置到对应的索引表中。

lua_newtable、lua_createtable、lua_settable、lua_setfield 这些 api 会在接下来的小节进行详细讲解

void setColor(lua_State *L, struct ColorTable *ct) {
    // 创建一个空表,并压入栈顶
    // lua_newtable 这是一个宏,真是定义是 lua_createtable(L, 0, 0)
    // lua_newtable 和 lua_createtable 会压入一个 table 到栈中
    lua_newtable(L);
//    lua_createtable(L, 0, 3);

    setColorField(L, "red", ct->red);
    setColorField(L, "green", ct->green);
    setColorField(L, "blue", ct->blue);

    // 'name' = table
    // 弹出表,并且将其设置为指定名称的全局变量的值
    lua_setglobal(L, ct->name);
}

void setColorField(lua_State *L, const char *index, int value) {
    // 第一种
    // 键
    lua_pushstring(L, index);
    // 值
    lua_pushnumber(L, (double) value / MAX_COLOR);
    // 将键和值弹出,然后设置到 table (索引为 -3) 中,table[stack[-2]] = stack[-1]
    lua_settable(L, -3);

    // 第二种
//    lua_pushnumber(L, (double) value / MAX_COLOR);
//    lua_setfield(L, -2, index);
}

经过这一步的设置,在 Lua 中就可以查看到对应的值了。

第二步,加载和运行 Lua 文件,在 Lua 文件中使用第一步设置的值,将其返回给宿主

Lua 文件内容如下

print(PINK, PINK.red, PINK.green, PINK.blue)
-- c++ 获取到的为 table
background = PINK
-- c++ 获取到的为 string
--background = "PINK"

加载和运行的代码如下

#define MAX_COLOR 255

struct ColorTable {
    char *name;
    unsigned char red, green, blue;
} colorTable[] = {
        {"WHITE", MAX_COLOR, MAX_COLOR, MAX_COLOR},
        {"RED",   MAX_COLOR, 0,         0},
        {"GREEN", 0,         MAX_COLOR, 0},
        {"BLUE",  0,         0,         MAX_COLOR},
        {"PINK",  255,       192,       203},
        {nullptr, 0,         0,         0}
};

void load(lua_State *L, const char *fname) {
    int i = 0;
    // 设置颜色值
    while (colorTable[i].name != nullptr) {
        setColor(L, &colorTable[i++]);
    }

    if (luaL_loadfile(L, fname) || lua_pcall(L, 0, 0, 0)) {
        printf("can't run config. file: %s\n", lua_tostring(L, -1));
        return;
    }

    // ... 获取值的操作,下面分享
}

void loadConfigUseTable() {
    std::string filename = PROJECT_PATH + "/5、C++调用Lua代码/Lua作为配置文件/配置中使用表/config.lua";
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);
    load(L, filename.c_str());
    lua_close(L);
}

第三步,获取 Lua 返回的值

将 Lua 中对应的 “background” 变量压入栈,然后将 key 压入栈,通过 lua_gettable 获取对应的值。

bool getColorField(lua_State *L, const char *key, int *result) {
    int isNum;

    // 第一种做法
    // 将 key 压入栈
    lua_pushstring(L, key);
    // 将栈顶(key)弹出,然后将读取的值压入
    lua_gettable(L, -2);

    // 第二种做法(lua_getfield 和 lua_gettable 都会返回类型)
//    if (lua_getfield(L, -1, key) == LUA_TNUMBER) {
//        printf("invalid component in background color");
//        return false;
//    }

    *result = (int) (lua_tonumberx(L, -1, &isNum) * MAX_COLOR);
    if (!isNum) {
        printf("invalid component '%s' in color", key);
        return false;
    }

    lua_pop(L, 1);
    return true;
}

void load(lua_State *L, const char *fname) {
    // ... 加载和运行 Lua 文件

    // 将 background 压入栈
    lua_getglobal(L, "background");
    if (lua_istable(L, -1)) {
        int red;
        if (!getColorField(L, "red", &red)) {
            printf("Get red failure.\n");
            return;
        }
        int green;
        if (!getColorField(L, "green", &green)) {
            printf("Get green failure.\n");
            return;
        }
        int blue;
        if (!getColorField(L, "blue", &blue)) {
            printf("Get blue failure.\n");
            return;
        }
        printf("table color: (%d, %d, %d)\n", red, green, blue);
    } else if (lua_isstring(L, -1)) {
        const char *name = lua_tostring(L, -1);
        int i;
        for (i = 0; colorTable[i].name != nullptr; i++) {
            if (strcmp(name, colorTable[i].name) == 0) {
                break;
            }
        }
        if (colorTable[i].name == nullptr) {
            printf("invalid color name (%s)", name);
            return;
        }
        int red = colorTable[i].red;
        int green = colorTable[i].green;
        int blue = colorTable[i].blue;
        printf("string color: (%d, %d, %d)\n", red, green, blue);
    } else {
        printf("'background' is not a table.");
    }
}

最后会输出

table: 0x600001afc880	1.0	0.75294117647059	0.79607843137255
table color: (255, 192, 203)

四、lua_newtable

void lua_newtable(lua_State *L);

作用:

用于创建一个新的空表,并将其压入 Lua 栈中。

参数:

  • 参数 L: Lua 状态机(Lua state)指针。

返回值:

没有返回值,但会压入一个 table 在栈顶。

五、lua_createtable

lua_newtable 其实是一个宏定义,真正实现是 lua_createtable

#define lua_newtable(L)		lua_createtable(L, 0, 0)
void  (lua_createtable) (lua_State *L, int narr, int nrec);

作用:

用于创建一个新的表,并将其压入 Lua 栈中。

参数:

  • 参数 L: Lua 状态机(Lua state)指针。
  • 参数 narr: 表的数组部分初始容量。数组部分是用于存储连续整数键(索引)的区域。当按顺序插入整数键时,它们会被存储在数组部分中,以实现高效的数组访问。narr 的值可以是正整数,用于指定表初始化时预分配的数组部分的大小。如果 narr 为 0,则表示不分配数组部分。
  • 参数 nrec: 表的哈希部分初始容量。哈希部分是用于存储非整数键(如字符串键)的区域。当我们插入非整数键时,它们会被存储在哈希部分中,并使用哈希表来实现高效的键值对查找。nrec 的值可以是正整数,用于指定表初始化时预分配的哈希部分的大小。如果 nrec 为 0,则表示不分配哈希部分。

一般情况下,如果事先知道表的大致大小,设置适当的 narr 和 nrec 值可以提高表操作的效率。但如果不确定表的大小,或者表的大小会动态变化,设置为 0 会让 Lua 在需要时自动调整表的大小。Lua 在内部会根据需要动态调整表的大小,以适应实际的键值对数量。

返回值:

没有返回值,但会压入一个 table 在栈顶。

六、lua_gettable

int (lua_gettable) (lua_State *L, int idx);

作用:

用于从 Lua 栈中获取表中指定键的值,并将该值压入栈顶

参数:

  • 参数 L: Lua 状态机(Lua state)指针。
  • 参数 index: 表示要获取表的索引位置。如果索引为正数,则表示从栈底向上数的位置获取表;如果索引为负数,则表示从栈顶向下数的位置获取表。

返回值:

返回数据类型,并且会将获取到的值压入栈顶,如果没有找到对应的值,则会将 nil 压入(因为在 table 中查询一个不存在的键时,则会返回 nil )。

返回的数据类型有以下类型,可以根据类型判断是否符合期望。

#define LUA_TNIL		0
#define LUA_TBOOLEAN		1
#define LUA_TLIGHTUSERDATA	2
#define LUA_TNUMBER		3
#define LUA_TSTRING		4
#define LUA_TTABLE		5
#define LUA_TFUNCTION		6
#define LUA_TUSERDATA		7
#define LUA_TTHREAD		8

一图胜千言:

七、lua_getfield

和 lua_gettable 的作用一样,只是更加便捷,不用额外的将 key 压入栈顶。

int (lua_getfield) (lua_State *L, int idx, const char *k);

作用:

用于获取指定表(索引为 idx )中字段名为 k 的值。

参数:

  • 参数 L: Lua 状态机的指针。
  • 参数 index: 表在堆栈中的索引。如果索引为正数,则表示从栈底向上数的位置获取表;如果索引为负数,则表示从栈顶向下数的位置获取表。
  • 参数 k: 字段的名称,以 C 字符串的形式传递。

返回值:

返回数据类型,并且会将获取到的值压入栈顶,如果没有找到对应的值,则会将 nil 压入(因为在 table 中查询一个不存在的键时,则会返回 nil )。

返回的数据类型有以下类型,可以根据类型判断是否符合期望。

#define LUA_TNIL		0
#define LUA_TBOOLEAN		1
#define LUA_TLIGHTUSERDATA	2
#define LUA_TNUMBER		3
#define LUA_TSTRING		4
#define LUA_TTABLE		5
#define LUA_TFUNCTION		6
#define LUA_TUSERDATA		7
#define LUA_TTHREAD		8

八、lua_settable

void lua_settable(lua_State *L, int index);

作用:

用于将键(索引为 -2 )、值(索引为 -1 )对存储到表(索引为 index )中。

可以理解为 table[stack[-2]] = stack[-1] ,table 为 stack[index]

参数:

  • 参数 L: Lua 状态机(Lua state)指针。
  • 参数 index: 表示要获取表的索引位置。如果索引为正数,则表示从栈底向上数的位置获取表;如果索引为负数,则表示从栈顶向下数的位置获取表。

返回值:

没有返回值,会将键和值出栈。

九、lua_setfield

和 lua_settable 的作用一样,只是更加便捷,不用将 key 值压入到栈中,但还是需要将 value 压至栈顶。

void lua_setfield(lua_State *L, int index, const char *key);

作用:

用于将值(索引为 -1 )存储到指定表(索引为 index )中的指定键名(key)。

可以理解为 table[index] = stack[-1] ,table 为 stack[index]

参数:

  • 参数 L: Lua 状态机(Lua state)指针。
  • 参数 index: 表在栈中的索引位置。如果索引为正数,则表示从栈底向上数的位置获取表;如果索引为负数,则表示从栈顶向下数的位置获取表。
  • 参数 key: 作为键的字符串。是一个 C 字符串,表示要在表中使用的键名。

返回值:

没有返回值,会将栈顶的 value 出栈。

十、lua_setglobal

void lua_setglobal(lua_State *L, const char *name);

作用:

用于将值(索引为 -1 )以 name 作为属性名存储到全局变量中。

可以理解为 'name' = value

参数:

  • 参数 L: Lua 状态机(Lua state)指针。
  • 参数 name: 是一个 C 字符串,表示要存储值的全局变量名。

返回值:

没有返回值,会将栈顶值弹出。

十一、写在最后

Lua 项目地址:Github传送门 (如果对你有所帮助或喜欢的话,赏个star吧,码字不易,请多多支持)

如果觉得本篇博文对你有所启发或是解决了困惑,点个赞或关注我呀

公众号搜索 “江澎涌”,更多优质文章会第一时间分享与你。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/169669.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python之代理ip的配置与调试

目录 前言 一、代理IP的配置 二、代理IP的调试 2.1 使用curl命令测试代理IP 2.2 使用requests库调试代理IP 三、代理IP的获取 3.1 使用代理IP池 3.2 使用付费代理IP服务 总结 前言 代理IP是网络爬虫中常用的技术手段。通过使用代理服务器,可以实现对特定网…

内网穿透的应用-如何在Docker中部署MinIO服务并结合内网穿透实现公网访问本地管理界面

文章目录 前言1. Docker 部署MinIO2. 本地访问MinIO3. Linux安装Cpolar4. 配置MinIO公网地址5. 远程访问MinIO管理界面6. 固定MinIO公网地址 前言 MinIO是一个开源的对象存储服务器,可以在各种环境中运行,例如本地、Docker容器、Kubernetes集群等。它兼…

【Linux】22、CPU 评价指标、性能工具、定位瓶颈、优化方法论:应用程序和系统

文章目录 一、评价 CPU 的指标1.1 CPU 使用率1.2 平均负载(Load Average)1.3 上下文切换1.4 CPU 缓存命中率 二、性能工具2.1 维度:从 CPU 性能指标出发,即当你查看某性能指标时,要清除知道哪些工具可以做到2.2 维度&a…

OpenCvSharp从入门到实践-(01)认识OpenCvSharp开发环境搭建

目录 一、OpenCV 二、OpenCvSharp 三、OpenCvSharp开发环境搭建 四、下载 五、其他 一、OpenCV OpenCV是基于Apache2.0许可(开源)发行的跨平台计算机视觉和机器学习函数库,支持Windows、Linux、Android和Mac OS操作系统。OpenCV由一系…

【活动通知】2023 Elastic Meetup 北京站将于12月2日下午1点30在北京召开

《2023 Elastic Meetup 北京站》活动将于 12 月 2 日下午 1 点 30 在北京市海淀区西北旺东路10号腾讯北京总部大楼213会议室举办,届时将有行业专家及知名企业分享他们在 Elasticsearch 应用中的经验与观点,带来最前沿的技术分享与思想碰撞。 请使用电脑浏…

vulnhub靶机Presidential

靶机地址:https://download.vulnhub.com/presidential/Presidential.ova 主机发现 arp-scan -l 端口扫描 nmap --min-rate 10000 192.168.21.150 端口服务扫描 nmap -sV -sT -O -p80 192.168.21.150 漏洞扫描 nmap --scriptvuln -p80 192.168.21.150 只有一个端…

车辆限迁查询API——查询您的车辆是否限制迁入迁出

随着城市的快速发展和人们生活水平的提高,车辆的使用量也不断增加。而随之而来的问题也愈发突出,其中之一就是车辆的限迁问题。 比如,在一些大城市,为了减少交通拥堵和空气污染,政府采取了限制车辆迁入迁出的措施&…

CleanMyMac X4.16免费版mac电脑一键清理电脑垃圾工具

但是,我最近发现随着使用时间的增加,一些奇奇怪怪的文件开始占据有限的磁盘空间,存储空间变得越来越小,系统占用空间越来越大,越来越多的无效文件开始影响我电脑的运行速度。 Mac的文件管理方式和Windows不太一样&…

初级程序员如何进阶

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO 联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬 疑问的无限递归 我刚入…

比赛调研资料

视觉文旅 现有的模型 数据 功能 精准营销 基于地理推荐能力 乡村圈分析能力 都市圈分析能力 产品体系 三大数据平台 携程问道 旅游服务框架:前置(推荐种草),途中(客服),售后&#xff0…

多目标应用:基于多目标灰狼优化算法MOGWO求解微电网多目标优化调度(MATLAB代码)

一、微网系统运行优化模型 微电网优化模型介绍: 微电网多目标优化调度模型简介_IT猿手的博客-CSDN博客 二、多目标灰狼优化算法MOGWO 多目标灰狼优化算法MOGWO简介: 三、多目标灰狼优化算法MOGWO求解微电网多目标优化调度 (1&#xff09…

英飞凌TC3xx的LMU SRAM保护机制(二)

目录 1.SRAM保护配置使能 2.测试结果分析 3.小结 我们接着上文继续来梳理如何配置LMU的保护机制 英飞凌TC3xx的LMU SRAM保护机制(一)-CSDN博客 1.SRAM保护配置使能 简单举两个例子,地址下限寄存器如下: 可以看到使用SRI地址的bit31-5用于作为边界&am…

Linux下安装两个版本python

1 python下载: 官网地址:Download Python | Python.org 第一:点击下载如下图: 第二:找到对应的python版本源码包: 点击右键复制下载地址,如下图 例如我的是:https://www.python.org/…

网络运维与网络安全 学习笔记2023.11.20

网络运维与网络安全 学习笔记 第二十一天 今日目标 交换网路径选择、Eth-Trunk原理、动态Eth-Trunk配置 Eth-Trunk案例实践、MUX VLAN原理、MUX VLAN配置 交换网路径选择 STP的作用 在交换网络中提供冗余/备份路径 提供冗余路径的同时,防止环路的产生 影响同网…

越南MIC新规针对ICT和ITE产品电气授权标准变更

从2024年1月1日起,所有ICT和ITE产品(如台式电脑、笔记本电脑、平板电脑、DVB-T2电视/机顶盒、DECT电话等)都需要越南MIC授权的电气安全标准——QCVN132:2022。 目前MIC仍未最终确定要求,因为这与另一个监管机构存在冲突。所以目前他们可以接受ISO 17025的…

电磁场与电磁波part6、7--均匀平面波的反射与透射、导行电磁波

1、分界面上的反射系数 反射波电场振幅 与入射波电场振幅 的比值,即: 2、驻波系数(驻波比) 合成波的电场强度的最大值与最小值之比,即: 3、导波系统中电磁波的传输问题属于电磁场边值问题,即…

可以自己实现的Python小游戏,共十四个可收藏可直接拿走~

文章目录 1、吃金币2、打乒乓3、滑雪4、并夕夕版飞机大战5、打地鼠6、小恐龙7、消消乐8、俄罗斯方块9、贪吃蛇10、24点小游戏11、平衡木12、外星人入侵13、贪心鸟14、井字棋888‘关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四…

轻量封装WebGPU渲染系统示例<35>- HDR环境数据应用到PBR渲染材质

当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/feature/rendering/src/voxgpu/sample/BasePbrMaterialTest.ts 当前示例运行效果: 微调参数之后的效果: 此示例基于此渲染系统实现,当前示例TypeScript源码如下: export class BasePbrMateri…

writev函数的使用测试

writev函数的原型是ssize_t writev(int filedes, const struct iovec *iov, int iovcnt),第一个参数是需要写入的文件描述符,第二个参数是指向某个iovec结构数组的一个指针,其中 iovec 结构定义如下: struct iovec { void *iov_b…

Python loglog()函数

常用坐标下的图像显示 import matplotlib.pyplot as plt import numpy as np import mathplt.figure() x_input np.linspace(1, 10, 50) y_input x_input**2plt.plot(x_input, y_input,r-,linewidth2) plt.show()在loglog函数尺度下的曲线 plt.loglog(x_input, y_input,r-,…