C++ 与 Lua 联合编程

news2025/8/8 14:48:17

在软件开发的广阔天地里,不同编程语言各有所长。C++ 以其卓越的性能、强大的功能和对硬件的直接操控能力,在系统开发、游戏引擎、服务器等底层领域占据重要地位,但c++编写的程序需要编译,这往往是一个耗时操作,特别对于大型程序而言编译可能耗费几十分钟;而Lua 则凭借其轻量级、可嵌入性和灵活的脚本特性,在游戏脚本、配置管理等方面大放异彩。当 C++ 与 Lua 携手合作,它们能够优势互补,创造出更强大、更灵活的应用程序。本文将带你逐步深入了解 C++ 和 Lua 联合编程,从基础概念到实际应用,领略这种编程方式的魅力。

一、 C++ 与 Lua 联合编程

(一)C++ 与 Lua 的特点

C++ 是一种静态类型的编程语言,它拥有丰富的特性,如面向对象编程、泛型编程等。这使得 C++ 在处理大规模、高性能的系统级任务时表现出色,像游戏引擎、操作系统组件等对性能和资源管理要求极高的场景,C++ 都是不二之选。

Lua 则是一种轻量级的脚本语言,它的语法简洁,易于学习和使用。Lua 的核心优势在于其可嵌入性,能够轻松地被集成到其他应用程序中,为程序提供灵活的脚本功能。在游戏开发中,Lua 常被用于编写游戏逻辑脚本、用户界面交互脚本,以及实现游戏的热更新功能,让开发者无需重新编译整个程序,就能修改游戏内容。

(二)联合编程

将 C++ 和 Lua 联合编程,就像是让两位高手相互配合。C++ 负责搭建坚实的底层框架,处理计算密集型任务和资源管理;Lua 则专注于实现灵活多变的业务逻辑和用户交互。这种合作方式不仅能提高开发效率,还能使应用程序更具扩展性和可维护性。比如在游戏开发中,C++ 实现游戏的核心引擎功能,包括图形渲染、物理模拟等;Lua 则用于编写游戏角色的行为逻辑、任务脚本和关卡配置,这样当需要修改游戏内容时,只需要更新 Lua 脚本,无需重新编译 C++ 代码,大大缩短了开发周期。

二、搭建联合编程环境

(一)安装 Lua

  1. Windows 系统:从 Lua 官方网站下载 Windows 安装包,安装过程中记得勾选将 Lua 添加到系统环境变量。安装完成后,打开命令提示符,输入 “lua -v”,如果显示 Lua 的版本信息,就说明安装成功了。

  2. Linux 系统:以 Ubuntu 为例,在终端中执行 “sudo apt-get install lua5.3” 命令,就能轻松完成安装。安装后,同样可以在终端输入 “lua -v” 来验证是否安装成功。或者通过官网安装,整个安装过程很简单,就算遇到问题网上也能找到大量解决方法。

关于lua的安装和基础可以参考:Lua 从基础入门到精通(非常详细),本文着重于联合编程,lua和c++基础不会展开。

(二)准备 C++ 开发环境

  1. Windows 系统:推荐使用Visual Studio,官网点开下载,配置选择c++桌面应用

  2. Linux 系统:自带

三、C++ 与 Lua 的基础交互

(一)第一个程序:hello lua

1.首先引入头文件,在代码开头,使用 extern "C" 是因为 C++ 支持函数重载,会对函数名进行修饰,而 Lua 是用 C 语言编写的,其函数名没有经过修饰。通过 extern "C" 可以确保 C++ 编译器以 C 语言的方式处理这些 Lua 头文件中的函数名,避免链接错误。lua.h 提供了 Lua 核心功能的接口,lauxlib.h 是 Lua 辅助库,提供了一些方便的函数,lualib.h 则用于打开 Lua 标准库。

extern "C"
{
    #include <lua.h>
    #include <lauxlib.h>
    #include <lualib.h>
}

2.main 函数是程序的入口点。lua_open() 函数用于创建一个新的 Lua 状态机,它就像是一个容器,管理着 Lua 解释器的所有状态信息。随后,luaopen_base(L)luaopen_string(L) 和 luaopen_table(L) 分别打开了 Lua 的基础库、字符串库和表库。这些库提供了 Lua 编程中常用的功能,比如基础的算术运算、字符串处理和表操作等。

int main(int argc, char* argv[])
{
    lua_State* L = lua_open(); 
    luaopen_base(L);
    luaopen_string(L);
    luaopen_table(L);

3.luaL_loadfile(L, "main.lua") 尝试加载名为 main.lua 的 Lua 脚本文件。如果加载过程中出现错误,该函数会返回一个非零值。一旦发生错误,程序会打印 loadfile error: 提示信息,并通过 lua_tostring(L, -1) 从 Lua 栈中获取错误信息(这里的-1值读取lua栈顶,应为出现异常lua会把错误提示信息压入栈顶),将其打印出来,最后返回 -1 表示程序异常退出。 

    if(luaL_loadfile(L,"main.lua"))
    {
        printf("loadfile error:\n");
        const char* error = lua_tostring(L,-1);
        printf("\t%s\n",error);
        return -1;
    }

4.lua_pcall(L, 0, 0, 0) 用于执行之前加载的 Lua 脚本。lua_pcall 是一个安全的调用函数,它会捕获 Lua 脚本执行过程中抛出的异常。第一个参数 L 是 Lua 状态机,第二个参数 0 表示传递给 Lua 脚本的参数数量,第三个参数 0 表示期望从 Lua 脚本获取的返回值数量,第四个参数 0 表示错误处理函数的索引。如果执行过程中出现错误,同样会把错误信息压入lua栈顶,通过 lua_tostring(L, -1) 从 Lua 栈中获取错误信息,最后返回 -1。

    if(lua_pcall(L,0,0,0))
    {
        printf("pcall error:\n");
        const char* error = lua_tostring(L,-1);
        printf("\t%s\n",error);
        return -1;
    }

5。最后,lua_close(L) 用于关闭 Lua 状态机,释放相关的资源。

    lua_close(L);
    return 0;
}

 完整cpp文件:

extern "C"
{
    #include <lua.h>
    #include <lauxlib.h>
    #include <lualib.h>
}

int main(int argc, char* argv[])
{
    lua_State* L = lua_open(); 
    luaopen_base(L);
    luaopen_string(L);
    luaopen_table(L);
    if(luaL_loadfile(L,"main.lua"))
    {
        printf("loadfile error:\n");
        const char* error = lua_tostring(L,-1);
        printf("\t%s\n",error);
        return -1;
    }
    if(lua_pcall(L,0,0,0))
    {
        printf("pcall error:\n");
        const char* error = lua_tostring(L,-1);
        printf("\t%s\n",error);
        return -1;
    }
    lua_close(L);
    return 0;
}

lua脚本(main.lua): 

print("hello lua")

(二)lua调用c++实现的函数

1.基本函数调用

先来看完整代码

extern "C"
{
    #include "lua.h"
    #include <lualib.h>
    #include <lauxlib.h>
}

#include <iostream>
#include <string.h>

int Ctest(lua_State* L)
{
    std::cout << "Cpp:Ctest" << std::endl;
    std::cout << lua_gettop(L) << std::endl;
    size_t len;
    const char* name = lua_tolstring(L,1,&len);
    int age = lua_tonumber(L,2);
    bool is = lua_toboolean(L,3);
    std::cout << lua_gettop(L) << std::endl;
    std::cout << "name: " << name << " age: " << age << " is: " << is << std::endl; 
    return 0;
}

int main(int argc, char* argv[])
{
    lua_State* L = lua_open();
    luaopen_base(L);
    lua_register(L,"ctest",Ctest);
    std::cout << "1: " <<lua_gettop(L) << std::endl;
    if(luaL_loadfile(L,"lesson1.lua"))
    {
        std::cout << "load file failed" << std::endl;
        const char* error = lua_tostring(L, -1);
        std::cout << error << std::endl;
        return -1;
    }
    std::cout << "2: " << lua_gettop(L) << std::endl;
    if(lua_pcall(L,0,0,0))
    {
        std::cout << "pcall failed" << std::endl;
        const char* error = lua_tostring(L, -1);
        std::cout << error << std::endl;
        return -1;
    }
    std::cout << lua_gettop(L) << std::endl;
    lua_close(L);
    return 0;
}

lua脚本(lesson1.lua): 

ctest("xiaoming" , 13, nil)

我们定义的Ctest函数接收3个参数,字符串、数字、bool类型,代码运行时程序会把参数依次压入栈,压入完毕后从栈低开始从下往上算,第一个是字符串(索引1),第二个是数字(索引2),第三个是bool数据(索引3),我们分别用lua_tolstring,lua_tonumber,lua_toboolean从栈中取出这些数据并转换数据类型为CPP支持类型,这里的return 0指的是本函数的返回值个数是0个,也就是没有返回值。

在完成函数定义后,我们需要将定义的函数注册给lua脚本,使用lua_register(L,"ctest",Ctest);其中第一个参数是lua状态机,第二个参数是lua脚本中函数名,这个函数名可以和cpp中定义的函数名不同,第三个是cpp中实现函数的函数指针。

完成上述操作后,就可以在lua脚本中调用ctest函数了。

2.array类型数据作为参数

还是先来看完整代码

extern "C"
{
    #include "lua.h"
    #include <lualib.h>
    #include <lauxlib.h>
}

#include <iostream>
#include <string.h>

int Ctestarr(lua_State* L)
{
    std::cout << "ctestarr" << std::endl;
    int len =luaL_getn(L,-1);
    std::cout << "len: " << len << std::endl;
    for(int i = 0; i < len ; i++)
    {
        lua_pushnumber(L,i+1);
        lua_gettable(L,1);//pop index push table[index]
        size_t size;
        std::cout << lua_tolstring(L,-1,&size) << std::endl;
        lua_pop(L,1);
    }
    return 0;
}

int main(int argc, char* argv[])
{
    lua_State* L = lua_open();
    luaopen_base(L);
    lua_register(L,"ctestarr",Ctestarr);
    std::cout << "1: " <<lua_gettop(L) << std::endl;
    if(luaL_loadfile(L,"lesson2.lua"))
    {
        std::cout << "load file failed" << std::endl;
        const char* error = lua_tostring(L, -1);
        std::cout << error << std::endl;
        return -1;
    }
    std::cout << "2: " << lua_gettop(L) << std::endl;
    if(lua_pcall(L,0,0,0))
    {
        std::cout << "pcall failed" << std::endl;
        const char* error = lua_tostring(L, -1);
        std::cout << error << std::endl;
        return -1;
    }
    std::cout << lua_gettop(L) << std::endl;
    lua_close(L);
    return 0;
}

脚本(lesson2.lua):

arr = {"xiaoming", "xiaohong", "xiaogang"}
ctestarr(arr)

我们定义的Ctestarr函数接收1个array数据,代码运行时程序会把参数压入栈,通过luaL_getn(L,-1)来计算array的长度,他的第一个参数是lua状态机,第二个是array在栈中位置,-1代表栈顶。获取长度后用佛如循环遍历读取array元素,每次读取先使用lua_pushnumber(L,i+1);向栈中压入一个索引值(lua索引从1开始,而不是从0开始,所以这里是i+1),然后执行lua_gettable(L,1);这个函数第二个参数是array在栈中位置,因为代码运行时程序会把参数压入栈,所以array一直在栈底(前面读取长度时用-1是因为栈内只有array,它即在栈底也在栈顶,而现在由于压入了索引,array已经不是栈顶了),lua_gettable(L,1);执行时会先对将lua栈中索引出栈,然后压入索引对于元素值,所以在tostring读取后记得执行lua_pop(L,1)恢复栈空间(lua_pop()函数第二个参数是出栈个数,出栈只从栈顶出,不是位置)。

3.带有键值对的table类型数据作为参数
extern "C"
{
    #include "lua.h"
    #include <lualib.h>
    #include <lauxlib.h>
}

#include <iostream>
#include <string.h>

int CtestTable1(lua_State* L)
{
    std::cout << "ctesttable1" << std::endl;
    lua_pushnil(L);
    while(lua_next(L,1) != 0)//每次调用从先栈顶弹出一个值,然后push key, push value
    {
        std::cout << lua_tostring(L,-2) << ": " << lua_tostring(L,-1) << std::endl;
        lua_pop(L,1);
    }

    return 0;
}

int CtestTable2(lua_State* L)
{
    std::cout << "ctesttable2" << std::endl;
    lua_getfield(L,1,"name");//会把value压入栈顶
    std::cout << lua_tostring(L,-1) << std::endl;
    lua_pop(L,1);
    lua_getfield(L,1,"age");//会把value压入栈顶
    std::cout << lua_tostring(L,-1) << std::endl;
    lua_pop(L,1);
    return 0;
}

int main(int argc, char* argv[])
{
    lua_State* L = lua_open();
    luaopen_base(L);
    lua_register(L,"ctesttable1",CtestTable1);
    lua_register(L,"ctesttable2",CtestTable2);
    std::cout << "1: " <<lua_gettop(L) << std::endl;
    if(luaL_loadfile(L,"lesson3.lua"))
    {
        std::cout << "load file failed" << std::endl;
        const char* error = lua_tostring(L, -1);
        std::cout << error << std::endl;
        return -1;
    }
    std::cout << "2: " << lua_gettop(L) << std::endl;
    if(lua_pcall(L,0,0,0))
    {
        std::cout << "pcall failed" << std::endl;
        const char* error = lua_tostring(L, -1);
        std::cout << error << std::endl;
        return -1;
    }
    std::cout << lua_gettop(L) << std::endl;
    return 0;
}
arr = {name="xiaoming",age = "18", id = "22300"}
ctesttable1(arr)
ctesttable2(arr)

两种方法可以读取,lua_next(L,1)函数第二个参数是table在栈中位置,它在执行时会先出战一个数据,然后入栈key,最后入栈value,也就是在第一次读取时我们要先手动压栈一个nil值。读取结束后再手动出栈一个值。

第二种方法是lua_getfield(L,1,"name");第二个参数是table在栈中位置,第三个参数是要读取的key值,执行结束会将输入key对于的value值压栈,读取结束后需要手动出栈以复原占空间。

4.带返回值的情况
extern "C"
{
    #include "lua.h"
    #include <lualib.h>
    #include <lauxlib.h>
}

#include <iostream>
#include <string.h>

int CtestRe(lua_State* L)
{
    lua_pushstring(L,"return value");
    lua_pushnumber(L,100);
    lua_newtable(L);
    lua_pushstring(L,"key");
    lua_pushstring(L,"value");
    lua_settable(L,-3);
    lua_pushstring(L,"key2");
    lua_pushnumber(L,123);
    lua_settable(L,-3);
    return 3;
}


int main(int argc, char* argv[])
{
    lua_State* L = lua_open();
    luaopen_base(L);
    lua_register(L, "ctestre", CtestRe);
    std::cout << "1: " <<lua_gettop(L) << std::endl;
    if(luaL_loadfile(L,"lesson5.lua"))
    {
        std::cout << "load file failed" << std::endl;
        const char* error = lua_tostring(L, -1);
        std::cout << error << std::endl;
        return -1;
    }
    std::cout << "2: " << lua_gettop(L) << std::endl;
    if(lua_pcall(L,0,0,0))
    {
        std::cout << "pcall failed" << std::endl;
        const char* error = lua_tostring(L, -1);
        std::cout << error << std::endl;
        return -1;
    }
    std::cout << lua_gettop(L) << std::endl;
    return 0;
}
a,b,c = ctestre() 
print(a)
print(b)
for k,v in pairs(c) do
	print(k,v)
end

我们在函数中执行lua_pushstring(L,"return value");  lua_pushnumber(L,100);向栈中压入一个string数据,一个number数据,执行lua_newtable(L);向栈中压入一个空table,然后一次压栈key值和value值,再执行lua_settable(L,-3);将压入的key和value加入table,这里的-3是之前压入的空table的栈中位置,因为压入了key和value,所以从占地开始从上往下第三个空间才是table,执行lua_settable后会把栈顶的key和value两个空间出栈,此时table又回到栈顶。

5.c++向lua中设置全局变量和读取lua中定义的全局变量
extern "C"
{
    #include "lua.h"
    #include <lualib.h>
    #include <lauxlib.h>
}

#include <iostream>
#include <string.h>


int main(int argc, char* argv[])
{
    lua_State* L = lua_open();
    luaopen_base(L);
    luaopen_string(L);
    luaopen_table(L);
    
    lua_pushstring(L,"value");//由c++设置全局变量
    lua_setglobal(L,"key");
    lua_pop(L,1);
    lua_pushnumber(L,18);//由c++设置全局变量
    lua_setglobal(L,"age");
    lua_pop(L,1);
    lua_newtable(L);
    lua_pushstring(L,"name");
    lua_pushstring(L,"xiaoming");
    lua_settable(L, -3);// 会把key,value出栈 
    lua_pushstring(L,"age");
    lua_pushnumber(L,13);
    lua_settable(L, -3);//还是-3
    lua_setglobal(L,"person");
    lua_pop(L,1);
    if(luaL_loadfile(L,"lesson6.lua"))
    {
        std::cout << "load file failed" << std::endl;
        const char* error = lua_tostring(L, -1);
        std::cout << error << std::endl;
        return -1;
    }

    if(lua_pcall(L,0,0,0))
    {
        std::cout << "pcall failed" << std::endl;
        const char* error = lua_tostring(L, -1);
        std::cout << error << std::endl;
        return -1;
    }
    std::cout << "1: " <<lua_gettop(L) << std::endl;
    lua_getglobal(L,"width");
    int width = lua_tonumber(L,-1);
    std::cout << "1: " <<lua_gettop(L) << std::endl;
    lua_pop(L,1);
    std::cout << "1: " <<lua_gettop(L) << std::endl;
    std::cout << "width = " << width << std::endl;

    lua_getglobal(L,"table");
    std::cout << "2: " <<lua_gettop(L) << std::endl;
    lua_getfield(L,-1,"age");
    std::cout << "2: " <<lua_gettop(L) << std::endl;
    std::cout << "age = " << lua_tonumber(L,-1) << std::endl;
    lua_pop(L,2);
    std::cout << "2: " <<lua_gettop(L) << std::endl;
    std::cout << lua_gettop(L) << std::endl;
    return 0;
}
print(key)
print(age)
for k,v in pairs(person) do
	print(k,v)
end
table = {name = "xiaohong" , age = "16"}
width = 100

这部分很简单,要注意的就是栈空间的管理,并且设置全部变量的位置应该再pcall之前,读取应该在pcall之后,要不然读不到。

(三)c++调用lua实现的函数

1.调用函数
extern "C"
{
    #include "lua.h"
    #include <lualib.h>
    #include <lauxlib.h>
}

#include <iostream>
#include <string.h>


int main(int argc, char* argv[])
{
    lua_State* L = lua_open();
    luaopen_base(L);
    luaopen_string(L);
    luaopen_table(L);
    
    if(luaL_loadfile(L,"lesson7.lua"))
    {
        std::cout << "load file failed" << std::endl;
        const char* error = lua_tostring(L, -1);
        std::cout << error << std::endl;
        return -1;
    }
    
    if(lua_pcall(L,0,0,0))
    {
        std::cout << "pcall failed" << std::endl;
        const char* error = lua_tostring(L, -1);
        std::cout << error << std::endl;
        return -1;
    }
    
    std::cout << lua_gettop(L) << std::endl;
    lua_getglobal(L,"event");
    lua_pushstring(L,"xiaoming ");
    std::cout << lua_gettop(L) << std::endl;
    if(lua_pcall(L,1,1,0) != 0)
    {
        std::cout << "pcall failed" << std::endl;
        const char* error = lua_tostring(L, -1);
        std::cout << error << std::endl;
        lua_pop(L,1);
    }
    else
    {
        std::cout << "pcall success: " << lua_tostring(L,-1) << std::endl;
        std::cout << lua_gettop(L) << std::endl;
        lua_pop(L,1);
    }
    std::cout << lua_gettop(L) << std::endl;
    return 0;
}
function event(a)
	print("event")
	print(a)
	return "lua_event"
end

lua_getglobal(L,"event");先将函数压栈,由于函数需要一个参数,lua_pushstring(L,"xiaoming ");把参数压栈,之后调用lua_pcall(L,1,1,0)执行,这个函数的第二个参数是"event"函数参数个数(1个),第二个是"event"函数返回值个数(1个)函数执行会把event和参数出栈,把"event"函数返回值入栈。

2.错误处理函数(lua_pcall第四个参数设置)
extern "C"
{
    #include "lua.h"
    #include <lualib.h>
    #include <lauxlib.h>
}

#include <iostream>
#include <string.h>


int main(int argc, char* argv[])
{
    lua_State* L = lua_open();
    luaopen_base(L);
    luaopen_string(L);
    luaopen_table(L);
    
    if(luaL_loadfile(L,"lesson8.lua"))
    {
        std::cout << "load file failed" << std::endl;
        const char* error = lua_tostring(L, -1);
        std::cout << error << std::endl;
        return -1;
    }
    
    if(lua_pcall(L,0,0,0))
    {
        std::cout << "pcall failed" << std::endl;
        const char* error = lua_tostring(L, -1);
        std::cout << error << std::endl;
        return -1;
    }
    
    std::cout << lua_gettop(L) << std::endl;
    lua_getglobal(L,"err");
    int err = lua_gettop(L);
    lua_getglobal(L,"even");//故意少写一个t,触发错误
    lua_pushstring(L,"xiaoming ");
    if(lua_pcall(L,1,1,err) != 0)
    {
        std::cout << "pcall failed" << std::endl;
        const char* error = lua_tostring(L, -1);
        std::cout <<"CPP ERR: "<< error << std::endl;
        lua_pop(L,1);
    }
    else
    {
        std::cout << "pcall success: " << lua_tostring(L,-1) << std::endl;
        std::cout << lua_gettop(L) << std::endl;
        lua_pop(L,1);
    }
    std::cout << lua_gettop(L) << std::endl;
    return 0;
}
function err(e)
	print("LUA_ERR: "..e)
	return "lua_error"
end

function event(a)
	print("event")
	print(a)
	return "lua_event"
end

lua_pacll第四个参数是错误处理函数在栈中位置,正常当不设置错误处理时当发生错误,撸啊会把错误提示入栈,当设置之后,lua会把错误提示作为参数出啊如错误处理函数,错误处理函数执行后再把其返回值(这里是“lua_err”)压入栈。

四、联合编程的挑战与应对策略

(一)内存管理

在 C++ 和 Lua 联合编程中,内存管理是一个重要问题。由于 C++ 需要手动管理内存,而 Lua 有自己的垃圾回收机制,在传递数据时需要特别小心。例如,当 C++ 创建一个对象并传递给 Lua 时,需要确保在 Lua 使用完该对象后,C++ 能够正确地释放内存。可以通过智能指针等方式来辅助内存管理,确保对象在不再使用时被正确释放。

(二)异常处理

C++ 和 Lua 的异常处理机制不同,在联合编程时需要统一处理异常,以确保程序的稳定性。可以在 C++ 中捕获 Lua 脚本执行过程中抛出的异常,并进行适当的处理。例如,使用 “lua_pcall” 函数代替 “lua_call” 函数,“lua_pcall” 函数可以捕获 Lua 函数执行过程中的异常,并将异常信息压入 Lua 栈,C++ 代码可以从栈中获取异常信息并进行处理。

(三)性能优化

虽然 C++ 性能较高,但在与 Lua 交互时,频繁的栈操作和数据传递可能会带来性能开销。为了优化性能,可以尽量减少不必要的栈操作,批量传递数据,而不是逐个传递。同时,对性能敏感的代码部分,可以使用 C++ 实现,而将逻辑相对简单、变化频繁的部分交给 Lua 处理。

C++ 与 Lua 联合编程为开发者提供了强大的工具,让我们能够充分发挥两种语言的优势。通过深入学习和实践,你可以利用这种编程方式开发出更具扩展性、灵活性和高性能的应用程序。无论是游戏开发、脚本化工具还是其他领域,C++ 与 Lua 的联合都能为你的项目带来新的活力。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux系统(OpenEuler22.03-LTS)部署FastGPT

在 openEuler 22.03 LTS 系统上通过 Docker Compose 安装 FastGPT 的步骤如下&#xff1a; 官方参考文档&#xff1a;https://doc.fastgpt.cn/docs/development/docker/ 1. 安装 Docker 和 Docker Compose 可以参考我之前离线安装Docker的文章&#xff1a;openEuler 22.03 LT…

Kubernetes控制平面组件:Controller Manager 之 内置Controller详解

云原生学习路线导航页&#xff08;持续更新中&#xff09; kubernetes学习系列快捷链接 Kubernetes架构原则和对象设计&#xff08;一&#xff09;Kubernetes架构原则和对象设计&#xff08;二&#xff09;Kubernetes架构原则和对象设计&#xff08;三&#xff09;Kubernetes控…

结合Splash与Scrapy:高效爬取动态JavaScript网站

在当今的Web开发中&#xff0c;JavaScript的广泛应用使得许多网站的内容无法通过传统的请求-响应模式直接获取。为了解决这个问题&#xff0c;Scrapy开发者经常需要集成像Splash这样的JavaScript渲染引擎。本文将详细介绍Splash JS引擎的工作原理&#xff0c;并探讨如何将其与S…

用于构建安全AI代理的开源防护系统

大家读完觉得有帮助记得及时关注&#xff01;&#xff01;&#xff01; 大型语言模型&#xff08;LLMs&#xff09;已经从简单的聊天机器人演变为能够执行复杂任务的自主代理&#xff0c;例如编辑生产代码、编排工作流程以及基于不受信任的输入&#xff08;如网页和电子邮件&am…

克里金模型+多目标优化+多属性决策!Kriging+NSGAII+熵权TOPSIS!

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 克里金模型多目标优化多属性决策&#xff01;KrigingNSGAII熵权TOPSIS&#xff01;&#xff01;matlab2023b语言运行&#xff01; 1.克里金模型&#xff08;Kriging Model&#xff09;是一种基于空间统计学的插值方法…

LLM 论文精读(三)Demystifying Long Chain-of-Thought Reasoning in LLMs

这是一篇2025年发表在arxiv中的LLM领域论文&#xff0c;主要描述了长思维链 Long Chain-of-Thought 对LLM的影响&#xff0c;以及其可能的生成机制。通过大量的消融实验证明了以下几点&#xff1a; 与shot CoT 相比&#xff0c;long CoT 的 SFT 可以扩展到更高的性能上限&…

【Prompt工程—文生图】案例大全

目录 一、人物绘图 二、卡通头像 三、风景图 四、logo设计图 五、动物形象图 六、室内设计图 七、动漫风格 八、二次元图 九、日常场景图 十、古风神化图 十一、游戏场景图 十二、电影大片质感 本文主要介绍了12种不同类型的文生图技巧&#xff0c;通过加入不同的图像…

rust程序静态编译的两种方法总结

1. 概述 经过我的探索&#xff0c;总结了两种rust程序静态编译的方法,理论上两种方法都适用于windows、mac os和linux(mac os未验证)&#xff0c;实测方法一性能比方法二好&#xff0c;现总结如下&#xff0c;希望能够帮到你. 2.方法一 2.1 添加配置文件 在项目的同级文件夹下新…

【Linux系列】目录大小查看

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

2048游戏(含Python源码)

前言 相关参考游戏&#xff1a; 像素飞机大战&#xff08;含Python源码&#xff09;-CSDN博客https://blog.csdn.net/weixin_64066303/article/details/147693018?spm1001.2014.3001.5501使用DeepSeek定制Python小游戏——以“俄罗斯方块”为例-CSDN博客https://blog.csdn.n…

中间件-RocketMQ

RocketMQ 基本架构消息模型消费者消费消息模式顺序消息机制延迟消息批量消息事务消息消息重试最佳实践 基本架构 nameServer: 维护broker列表信息&#xff0c;客户端连接时只需要连接nameServer。可配置成集群。 broker&#xff1a;broker分为master和slave&#xff0c;master负…

Python就业方向有哪些?

Python 作为一门通用、易学且功能强大的编程语言&#xff0c;在多个领域都有广泛的应用&#xff0c;因此就业方向也非常多样化。以下是 Python 主要的就业方向及相关技能要求。 1. Web 开发 岗位&#xff1a;Python Web 开发工程师、后端工程师、全栈工程师技术栈&#xff1a…

iptables 访问控制列表使用记录

iptables 是linux操作系统上自带的防火墙程序&#xff0c;功能强大&#xff0c;能够依据策略过滤掉一些恶意访问流量&#xff0c;本次记录一下iptables的常见使用方法&#xff0c;未尽之处&#xff0c;欢迎补充。 一、iptables 下载 我这里使用的是华为openEuler 22.03版本&am…

16. Qt系统相关:事件、定时器

1. Qt事件 1.1 简介 事件是应用程序内部或者外部产生的事情或者动作的统称。在Qt中使用一个对象来表示一个事件。所有的Qt事件均继承于抽象类QEvent。事件是由系统或者Qt平台本身在不同的时刻发出的。当用户按下鼠标、敲下键盘&#xff0c;或者是窗口需要重新绘制的时候&#…

云平台搭建

物联网云平台的基本概述 基本概念 随着物联网技术的快速发展&#xff0c;越来越多的设备需要接入网络以实现智能化功能&#xff0c;物联网平台应运而生。 物联网云平台&#xff08;IoT Cloud Platform&#xff09;是物联网生态系统中的核心组件&#xff0c;它通过提供一系列…

数学实验(Matlab语言环境和线性代数实验)

一、Matlab语言环境和线性代数实验 1.Matlab语言环境 Matlab简介 Matlab&#xff1a;Matrix Laboratry 矩阵实验室 Matlab 提供了强大的科学计算、灵活的程序设计流程、高质量的图形可视化与界面设计等功能&#xff0c;被广泛应用于科学计算、控制系统、信息处理等领域的分…

Elasticsearch 中的索引模板:如何使用可组合模板

作者&#xff1a;来自 Elastic Kofi Bartlett 探索可组合模板以及如何创建它们。 更多阅读&#xff1a; Elasticsearch&#xff1a;可组合的 Index templates - 7.8 版本之后 想获得 Elastic 认证吗&#xff1f;查看下一期 Elasticsearch Engineer 培训的时间&#xff01; El…

【LeetCode 42】接雨水(单调栈、DP、双指针)

题面&#xff1a; 思路&#xff1a; 能接雨水的点&#xff0c;必然是比两边都低&#xff08;小&#xff09;的点。有两种思路&#xff0c;一种是直接计算每个点的最大贡献&#xff08;也就是每个点在纵向上最多能接多少水&#xff09;&#xff0c;另一种就是计算每个点在横向上…

【JS逆向基础】前端基础-HTML与CSS

1&#xff0c;flask框架 以下是一个使用flask框架写成的serve程序 # noinspection PyUnresolvedReferences #Flash框架的基本内容from flask import Flask app Flask(__name__)app.route(/index) def index():return "hello index"app.route(/login) def login():re…

手机网页提示ip被拉黑名单什么意思?怎么办

‌当您使用手机浏览网页时&#xff0c;突然看到“您的IP地址已被列入黑名单”的提示&#xff0c;是否感到困惑和不安&#xff1f;这种情况在现代网络生活中并不罕见&#xff0c;但确实会给用户带来诸多不便。本文将详细解释IP被拉黑的含义、常见原因&#xff0c;并提供一系列实…