C++和Lua交互总结
C++和Lua交互总结
- Chapter1. C++和Lua交互总结
- 一、Lua与C++的交互机制——Lua堆栈
- 二、堆栈的操作
- 三、C++ 调用 Lua
- 1)C++获取Lua值
- 2)C++调用Lua函数
- 示例:
- 四、Lua 调用 C++
- 包装C++函数
- 最后总结一下
Chapter1. C++和Lua交互总结
原文链接:https://blog.csdn.net/qq826364410/article/details/88624824/
一、Lua与C++的交互机制——Lua堆栈
Lua和C++ 的交互机制的基础在于Lua提供了一个虚拟栈,C++ 和Lua之间的所有类型的数据交换都通过这个栈完成。无论何时C++想从Lua中调用一个值,被请求的值将会被压入栈,无论何时C++想要传递一个值给Lua,首先将整个值压栈,然后就可以在Lua中调用。
Lua中,对虚拟栈提供正向索引和反向索引两种索引方式,区别是:正数索引1永远表示栈底,负数索引-1永远表示栈顶。重要!后面所有的交互,都是基于Lua的虚拟栈来通信。假设当前Lua的栈中有5个元素,如下图所示:
二、堆栈的操作
因为lua与c/c++是通过栈来通信,lua提供了C API对栈进行操作。
我们先来看一个最简单的例子:
#include <iostream>
#include <string.h>
using namespace std;extern "C"
{
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}int main()
{//1.创建一个state // luaL_newstate返回一个指向堆栈的指针lua_State *L = luaL_newstate();//2.入栈操作 lua_pushstring(L, "I am so cool~");lua_pushnumber(L, 20);//3.取值操作 if (lua_isstring(L, 1)) { //判断是否可以转为string cout << lua_tostring(L, 1) << endl; //转为string并返回 }if (lua_isnumber(L, 2)) {cout << lua_tonumber(L, 2) << endl;}//4.关闭state lua_close(L);getchar();return 0;
}
在执行这个例子之前,我们需要引入Lua.lib静态库,也就是上文中extern "C"中执行的include。
extern “C”
主要作用就是为了能够正确实现C++ 代码调用其他C语言代码。加上extern “C”后,会指示编译器这部分代码按c语言的进行编译,而不是C++ 的。由于C++ 支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名,比如_Log_int_int;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名,比如_Log。
void Log(int a, int b){}
Lua源代码的下载地址: http://www.lua.org/ftp/,但没有提供相应的静态库,只有.c和.h文件,需要我们自己生成静态库。
生成Lua静态库方法: https://blog.csdn.net/qq826364410/article/details/88563408
也可以直接下载我生成好的静态库:https://download.csdn.net/download/qq826364410/11029611
好了,我们有了Lua静态库,就可以开心的玩耍了。
引入Lua静态库
- 首先,新建一个Visual C++的空项目,右键点击工程属性,选择VC++目录,
- 把lua工程中的.h头文件所在的目录加到包含目录中
- 把Lua静态库文件所在的目录加到库目录中,
- 最后,点击链接器->输入->附加依赖项->加上生成的Lua静态库,比如Lua.lib,记得用分号";"与其他库隔开。
OK,大功告成!
Lua虚拟栈在源码中是如何实现的:
Lua栈是在创建lua_State时建立的,TValue stack[max_stack_len] ,欲知内情可以查 Lua源码lstate.c的stack_init函数
Lua栈可以存储数字,字符串,表,闭包等,它们最终都用TValue这种数据结构来保存 。
TValue结构对应于lua中的所有数据类型, 是一个{值, 类型} 结构, 这就lua中动态类型的实现, 它把值和类型绑在一起, 用tt记录value的类型, value是一个联合结构, 由Value定义, 可以看到这个联合有四个域, 先说明简单的
p – 可以存一个指针, 实际上是lua中的light userdata结构
n – 所有的数值存在这里, 不管是int , 还是float
b – Boolean值存在这里, 注意, lua_pushinteger不是存在这里, 而是存在n中, b只存布尔
gc – 其他诸如table, thread, closure, string需要内存管理垃圾回收的类型都存在这里
gc是一个指针, 它可以指向的类型由联合体GCObject定义, 从图中可以看出, 有string, userdata, closure, table, proto, upvalue, thread
可以的得出如下结论:
1. lua中, number, boolean, nil, light userdata四种类型的值是直接存在栈上元素里的, 和垃圾回收无关。
2. lua中, string, table, closure, userdata, thread存在栈上元素里的只是指针, 他们都会在生命周期结束后被垃圾回收。
三、C++ 调用 Lua
C++ 可以获取Lua中的值,可以调用Lua函数,还可以修改Lua文件
1)C++获取Lua值
使用lua_getglocal来获取值,然后将其压栈
使用lua_toXXX将栈中元素取出转成相应的C++类型的值
如果Lua值为table类型的话,通过lua_getfield和lua_setfield获取和修改表中元素的值
2)C++调用Lua函数
使用lua_getglobal来获取函数,然后将其压入栈;
如果这个函数有参数的话,就需要依次将函数的参数也压入栈;
这些准备工作都准备就绪以后,就调用lua_pcall开始调用函数了,调用完成以后,会将返回值压入栈中;
示例:
新建一个简单的lua放到工程的同级目录下
hello.lua
str = "I am x-man."
tbl = {name = "DC", id = 20114442}
function add(a,b)return a + b
end
然后,我们写一个Lua1.cpp来访问lua中的数据
#include <iostream>
#include <string.h>
using namespace std;extern "C"
{
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
int main()
{//1.创建Lua状态,返回一个指向堆栈的指针lua_State *L = luaL_newstate();if (L == NULL){return;}//2.加载lua文件int bRet = luaL_loadfile(L, "hello.lua");if (bRet){cout << "load file error" << endl;return;}//3.运行lua文件bRet = lua_pcall(L, 0, 0, 0);if (bRet){cout << "pcall error" << endl;return;}//4.读取全局变量,// 1.把 str 压栈 2.由lua去寻找全局变量str的值,并将str的值返回栈顶(替换str)// 如果存在相同命名的其他变量、table或函数,就会报错(读取位置发生访问冲突)lua_getglobal(L, "str");// -1取出栈顶元素,转化为stringstring str = lua_tostring(L, -1);cout << "str = " << str.c_str() << endl;//5.读取table,把table压栈lua_getglobal(L, "tbl");//-------------------------------// 1.把name压入栈中,2.由lua去寻找table中name键的值,并将键值返回栈顶(替换name)// 相当于lua_pushstring(L, "name") + lua_gettable(L, -2)执行结果是一样的lua_getfield(L, -1, "name");// 把name压入栈中//lua_pushstring(L, "name");// 弹出栈上的name,并从表中找到name的键值,把结果放在栈上相同的位置//lua_gettable(L, -2);//---------------------------------str = lua_tostring(L, -1);// 因为table在栈顶的下面,所以取-2,把id压栈,由lua找到table中id键的值,并返回栈顶(替换id)lua_getfield(L, -2, "id");// id的值已经在栈顶,取-1int id = lua_tonumber(L, -1);cout << "tbl:name = " << str.c_str() << endl;cout << "tbl:id = " << id << endl;// 读取函数,// 1.将函数add放入栈中,2.由lua去寻找函数add,并将函数add返回栈顶(替换add)。lua_getglobal(L, "add"); // 获取函数,压入栈中lua_pushnumber(L, 10); // 压入第一个参数lua_pushnumber(L, 20); // 压入第二个参数// 栈过程:参数出栈->保存参数->参数出栈->保存参数->函数出栈->调用函数->返回结果入栈// 调用函数,调用完成以后,会将返回值压入栈中,2表示参数个数,1表示返回结果个数。int iRet = lua_pcall(L, 2, 1, 0);if (iRet) {// 调用出错const char *pErrorMsg = lua_tostring(L, -1);cout << pErrorMsg << endl;lua_close(L);return;}if (lua_isnumber(L, -1)) //取值输出{int fValue = lua_tonumber(L, -1);cout << "Result is " << fValue << endl;}// 栈的索引方式可以是正数也可以是负数,区别是:1永远表示栈底元素,-1永远表示栈顶元素。//至此,栈中的情况是://=================== 栈顶 =================== // 索引 类型 值// 5或-1 int 30 // 4或-2 int 20114442// 3或-3 string shun // 2或-4 table tbl// 1或-5 string I am so cool~//=================== 栈底 =================== lua_pushstring(L, "Master");// 会将"Master"值出栈,保存值,找到到table的name键,如果键存在,存储到name键中lua_setfield(L, 2, "name");// 读取lua_getfield(L, 2, "name");str = lua_tostring(L, -1);cout << "tbl:name = " << str.c_str() << endl;// 创建新的tablelua_newtable(L);lua_pushstring(L, "A New Girlfriend");lua_setfield(L, -2, "name");// 读取lua_getfield(L, -1, "name");str = lua_tostring(L, -1);cout << "newtbl:name = " << str.c_str() << endl;//7.关闭state// 销毁指定 Lua 状态机中的所有对象, 并且释放状态机中使用的所有动态内存。// (如果有垃圾收集相关的元方法的话,会调用它们)lua_close(L);getchar();return 0;
}
代码中,已经有很详细的注释了,这里总结一下:
1. 读取lua的全局变量:
lua_getglobal(L, "str");
内部实现:1.把全局变量 str 里的值压栈 2.由lua去寻找全局变量str的值,并将str的值返回栈顶(替换str)
注意:如果存在相同命名的其他变量、table或函数,就会报错(读取位置发生访问冲突)
2. 读取table中的键值:
lua_getglobal(L, "tbl");
lua_getfield(L, -1, "name");
lua_getglobal方法跟上面的实现是一样的。
lua_getfield方法:
内部实现:1.把name压入栈中,2.由lua去寻找table中name键的值,如果键存在,将键值返回栈顶(替换name)
注意:这里的参数-1,就是表示把table中的键值返回到栈顶。
- 调用lua中的函数:
lua_getglobal(L, "add"); // 获取函数,压入栈中
lua_pushnumber(L, 10); // 压入第一个参数
lua_pushnumber(L, 20); // 压入第二个参数
// 栈过程:参数出栈->保存参数->参数出栈->保存参数->函数出栈->调用函数->返回结果入栈
// 调用函数,调用完成以后,会将返回值压入栈中,2表示参数个数,1表示返回结果个数,
// iRet为0表示调用成功
int iRet = lua_pcall(L, 2, 1, 0);
四、Lua 调用 C++
Lua可以调用由C++定义、实现具体的函数
步骤:
- 将C++的函数包装成Lua环境认可的Lua_CFunction格式
- 将包装好的函数注册到Lua环境中
- 像使用普通Lua函数那样使用注册函数
包装C++函数
为了从Lua脚本中调用C++函数,需要将被调用的C++函数从普通的C++函数包装成Lua_CFunction格式,并需要在函数中将返回值压入栈中,并返回返回值个数。
int (*lua_CFunction) (lua_State *L);
例如有一个C++函数:
int add(int a,int b)
{return a+b;
}
包装为:
int add(lua_state *L)
{int a = lua_tonumber(-1);int b = lua_tonumber(-2);int sum = a+b;// 将返回值压入栈中lua_pushnumber(L,sum);// 返回返回值个数return 1;
}
示例:
新建一个简单的lua放到工程的同级目录下
avg.lua
avg, sum = average(10, 20, 30, 40, 50)
print("The average is ", avg)
print("The sum is ", sum)print("age", age)
for k,v in pairs(newTable) do print("k = ",k," v = ",v)
end
print("name", newTable.name)
然后,创建一个Lua2.cpp:
#include <stdio.h>extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}static int average(lua_State *L)
{/* 得到参数个数 */int n = lua_gettop(L);double sum = 0;int i;/* 循环求参数之和 */for (i = 1; i <= n; i++){/* 求和 */sum += lua_tonumber(L, i);}/* 压入平均值 */lua_pushnumber(L, sum / n);/* 压入和 */lua_pushnumber(L, sum);/* 返回返回值的个数 */return 2;
}int main(int argc, char *argv[])
{/* 初始化Lua *//* 指向Lua解释器的指针 */lua_State* L = luaL_newstate();/* 载入Lua基本库 */luaL_openlibs(L);/* 注册函数 */lua_register(L, "average", average);// 设置lua中的全局变量lua_pushinteger(L, 18); //入栈lua_setglobal(L, "age"); //1.先将18值出栈,保存值,2.在lua中,把值存储到全局变量age中// 设置lua中tablelua_newtable(L); //创建一张空表,并将其压栈lua_pushstring(L, "lili");// 入栈// 1.先将"lili"值出栈,保存值,2.找table的name键,如果键存在,存储到name键中lua_setfield(L, -2, "name");//栈顶是lili,新创建的table在lili下,所以是-2// 将table赋值到lua中,并弹出tablelua_setglobal(L, "newTable");/* 运行脚本 */luaL_dofile(L, "avg.lua");/* 清除Lua */lua_close(L);/* 暂停 */printf("Press enter to exit…");getchar();return 0;
}
1. 读取C++的变量:
// 设置lua中的全局变量
lua_pushinteger(L, 18); //入栈
lua_setglobal(L, "age");
lua_setglobal(L, “age”) 内部实现: 1.先将值出栈,保存值,2.在lua中,把值存储到全局变量age中
2. 调用在C++中创建的新表的元素:
// 设置lua中table
lua_newtable(L); //创建一张空表,并将其压栈
lua_pushstring(L, "lili");// 入栈
// 1.先将"lili"值出栈,保存值,2.找table的name键,如果键存在,存储到name键中
lua_setfield(L, -2, "name");//栈顶是lili,新创建的table在lili下,所以是-2
// 将table赋值到lua中,并弹出table
lua_setglobal(L, "newTable");
lua_setglobal(L, “newTable”) 内部实现: 1.先将table出栈,保存table,2.在lua中,存储到newTable表中
在lua中,print(“name”, newTable.name),使用newTable.name调用在C++中创建的新表的元素。
3. 调用C++中的函数:
将C++的函数包装成Lua环境认可的Lua_CFunction格式
将包装好的函数注册到Lua环境中
像使用普通Lua函数那样使用注册函数
包装C++函数:
static int average(lua_State *L)
{/* 得到参数个数 */int n = lua_gettop(L);double sum = 0;int i;/* 循环求参数之和 */for (i = 1; i <= n; i++){/* 求和 */sum += lua_tonumber(L, i);}/* 压入平均值 */lua_pushnumber(L, sum / n);/* 压入和 */lua_pushnumber(L, sum);/* 返回返回值的个数 */return 2;
}
将包装好的函数注册到Lua环境中
/* 注册函数 */
lua_register(L, "average", average);
在lua中正常调用
avg, sum = average(10, 20, 30, 40, 50)
4.把C++的函数封装成模块
把C++的函数封装成模块:
①将C++的函数包装成Lua环境认可的Lua_CFunction格式,调用luaL_newlib,放入到一个lua表中压入栈里。
②将自定义模块,注册到Lua环境中。
③在lua中,加上自定义模块名调用C++函数。
avg.lua,这里的lua文件,在调用C++的函数时,需要加上自定义的模块名。 比如,我们定义模块名为mylib。
avg, sum = mylib.average(10, 20, 30, 40, 50)
print("The average is ", avg)
print("The sum is ", sum)
Lua1.cpp
#include <stdio.h>extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}static int average(lua_State *L)
{/* 得到参数个数 */int n = lua_gettop(L);double sum = 0;int i;/* 循环求参数之和 */for (i = 1; i <= n; i++){/* 求和 */sum += lua_tonumber(L, i);}/* 压入平均值 */lua_pushnumber(L, sum / n);/* 压入和 */lua_pushnumber(L, sum);/* 返回返回值的个数 */return 2;
}// 1. 列出需要封装的C++函数
// luaL_Reg为注册函数的数组类型
static const luaL_Reg mylibs_funcs[] = {{ "average", average },{ NULL, NULL }
};// 2. 将所有函数放到一个table中,并压入栈中
int lua_openmylib(lua_State* L) {//创建一个新的表,将所有函数放到一个table中//将这个table压到stack里luaL_newlib(L, mylibs_funcs);return 1;
}// 3. 将自定义模块加到注册列表里
static const luaL_Reg lua_reg_libs[] = {{ "base", luaopen_base },{ "mylib", lua_openmylib }, //这里为自定义的模块名字mylib{ NULL, NULL }
};int main(int argc, char *argv[])
{/* 初始化Lua *//* 指向Lua解释器的指针 */lua_State* L = luaL_newstate();/* 载入Lua基本库 */luaL_openlibs(L);//4. 注册让lua使用的模块const luaL_Reg* lua_reg = lua_reg_libs;for (; lua_reg->func; ++lua_reg) {// 加载模块// 首先查找 package.loaded 表, 检测 modname 是否被加载过。 // 如果被加载过,require 返回 package.loaded[modname] 中保存的值。// 如果 modname 不在 package.loaded 中, 则调用函数 openf ,并传入字符串 modname。// 将其返回值置入 package.loaded[modname]。// 如果最后一个参数为真, 同时也将模块设到全局变量 modname 里。在栈上留下该模块的副本。luaL_requiref(L, lua_reg->name, lua_reg->func, 1);// 从栈中弹出 1 个元素lua_pop(L, 1);}/* 运行脚本 */luaL_dofile(L, "avg.lua");/* 清除Lua */lua_close(L);/* 暂停 */printf("Press enter to exit…");getchar();return 0;
}
5. Lua调用C++类注册生成的Lua模块
由于篇幅的限制,请移步:https://blog.csdn.net/qq826364410/article/details/88652441
6. 在Lua中以面向对象的方式使用C++注册的类
由于篇幅的限制,请移步:https://blog.csdn.net/qq826364410/article/details/88639408
Lua和C++交互:全局数组交互
https://blog.csdn.net/qq826364410/article/details/88713839
补充
这里补充其他一些栈操作:
int lua_gettop (lua_State *L); //返回栈顶索引(即栈长度)
// lua_settop将栈顶设置为一个指定的位置,即修改栈中元素的数量。
// 如果值比原栈顶高,则高的部分nil补足,如果值比原栈低,则原栈高出的部分舍弃。
// 所以可以用lua_settop(0)来清空栈。
void lua_settop (lua_State *L, int idx);
void lua_pushvalue (lua_State *L, int idx); //将idx索引上的值的副本压入栈顶
void lua_remove (lua_State *L, int idx); //移除idx索引上的值
void lua_insert (lua_State *L, int idx); //弹出栈顶元素,并插入索引idx位置
void lua_replace (lua_State *L, int idx); //弹出栈顶元素,并替换索引idx位置的值
// 确保堆栈上至少有 n 个额外空位。 如果不能把堆栈扩展到相应的尺寸,
// 函数返回假。 失败的原因包括将把栈扩展到比固定最大尺寸还大 (至少是几
// 千个元素)或分配内存失败。 这个函数永远不会缩小堆栈; 如果堆栈已经
// 比需要的大了,那么就保持原样
int lua_checkstack (lua_State *L, int n);
下面就分两个主要部分进行介绍(C++和栈操作;以及Lua和栈操作)
2.C++和栈之间操作相关函数
①c -> stack 将C++数据压到栈里函数:lua_pushxxx
LUA_API void (lua_pushnil) (lua_State *L);
LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n);
LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n);
LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len);
LUA_API const char *(lua_pushstring) (lua_State *L, const char *s);
LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,va_list argp);
LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
LUA_API void (lua_pushboolean) (lua_State *L, int b);
LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p);
LUA_API int (lua_pushthread) (lua_State *L);
②stack -> c 判断栈里类型相关函数: lua_isxxx(lua_State *L, int idx)
LUA_API int (lua_isnumber) (lua_State *L, int idx);
LUA_API int (lua_isstring) (lua_State *L, int idx);
LUA_API int (lua_iscfunction) (lua_State *L, int idx);
LUA_API int (lua_isinteger) (lua_State *L, int idx);
LUA_API int (lua_isuserdata) (lua_State *L, int idx);
LUA_API int (lua_type) (lua_State *L, int idx);
③stack -> c 获取栈里数据相关函数:lua_toxxx (lua_State *L, int idx)
LUA_API lua_Number (lua_tonumberx) (lua_State *L, int idx, int *isnum);
LUA_API lua_Integer (lua_tointegerx) (lua_State *L, int idx, int *isnum);
LUA_API int (lua_toboolean) (lua_State *L, int idx);
LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len);
LUA_API size_t (lua_rawlen) (lua_State *L, int idx);
LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx);
LUA_API void *(lua_touserdata) (lua_State *L, int idx);
LUA_API lua_State *(lua_tothread) (lua_State *L, int idx);
LUA_API const void *(lua_topointer) (lua_State *L, int idx);
3.Lua和栈之间的操作相关函数
①从Lua中得到数据放到栈里进行操作:lua_getxxx
LUA_API int (lua_getglobal) (lua_State *L, const char *name);
LUA_API int (lua_gettable) (lua_State *L, int idx);
LUA_API int (lua_getfield) (lua_State *L, int idx, const char *k);
LUA_API int (lua_geti) (lua_State *L, int idx, lua_Integer n);
LUA_API int (lua_rawget) (lua_State *L, int idx);
LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n);
LUA_API int (lua_rawgetp) (lua_State *L, int idx, const void *p);LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec);
LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz);
LUA_API int (lua_getmetatable) (lua_State *L, int objindex);
LUA_API int (lua_getuservalue) (lua_State *L, int idx);
②从栈里将数据写入到Lua中:lua_setxxx
LUA_API void (lua_setglobal) (lua_State *L, const char *name);
LUA_API void (lua_settable) (lua_State *L, int idx);
LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k);
LUA_API void (lua_seti) (lua_State *L, int idx, lua_Integer n);
LUA_API void (lua_rawset) (lua_State *L, int idx);
LUA_API void (lua_rawseti) (lua_State *L, int idx, lua_Integer n);
LUA_API void (lua_rawsetp) (lua_State *L, int idx, const void *p);
LUA_API int (lua_setmetatable) (lua_State *L, int objindex);
LUA_API void (lua_setuservalue) (lua_State *L, int idx);
最后总结一下
-
Lua和C++是通过一个虚拟栈来交互通信的。
-
C++调用Lua: 由C++先把函数名、变量名、table中键放入栈中,然后把函数名、变量名、table中键出栈,并返回对应的值到栈顶,再由栈顶返回C++。
-
Lua调C++:
**Lua调用C++的函数:**先把普通的C++函数包装成Lua_CFunction格式,然后注册函数到Lua解释器中,然后由Lua去调用这个模块的函数。
**Lua以面向对象的方式调用C++的类:**新建一个元表metatable,并设置元表里key为"__index"的值的为metatable本身,然后将成员操作方法添加到元表metatable里,在创建对象函数中,把元表赋值给对象指针,这样通过":"操作符就可以找到对应的方法了。
相关文章:

C++和Lua交互总结
C和Lua交互总结 Chapter1. C和Lua交互总结一、Lua与C的交互机制——Lua堆栈二、堆栈的操作三、C 调用 Lua1)C获取Lua值2)C调用Lua函数示例: 四、Lua 调用 C包装C函数 最后总结一下 Chapter1. C和Lua交互总结 原文链接:https://bl…...

nvm安装和切换node版本
1、nvm list查看已安装的node版本 2、查看当前使用的npm和node版本 3、安装某版本的node 4、 切换node版本...

每日一题8.2 2536
2536. 子矩阵元素加 1 给你一个正整数 n ,表示最初有一个 n x n 、下标从 0 开始的整数矩阵 mat ,矩阵中填满了 0 。 另给你一个二维整数数组 query 。针对每个查询 query[i] [row1i, col1i, row2i, col2i] ,请你执行下述操作:…...

适配器模式(Adapter)
适配器模式用于将一个接口转换成用户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。 Adapter is a structural design pattern that…...

Spring学习笔记——1
Spring学习笔记——1 一、Spring入门1.1、学习路线1.2、传统Javaweb开发困惑及解决方法1.3、三种思想的提出和框架概念1.3.1、IoC、DI和AOP思想提出1.3.2、框架的基本特点 1.4、Spring概述1.5、BeanFactory快速入门1.6、ApplicationContext快速入门1.7、BeanFactory与Applicati…...

leetcode 406. 根据身高重建队列
2023.8.2 这题一开始有点让人懵逼的是有两个维度,一个是身高,还一个是前面人高于自己的人数。这种题一般需要先固定一个维度,再去确定另外一个维度,不要想着兼顾。 经过纸上模拟,我的思路是先通过身高进行从大到小排序…...
Matlab实现AGNES算法
在数据分析和机器学习中,聚类是一种常用的无监督学习方法,它可以将数据点按照某种相似度标准进行分组,从而发现数据中的结构和模式。聚类算法有很多种,其中一种比较经典的是AGNES算法,它是一种基于层次的聚类算法&…...

STM32F4_外部SRAM
目录 前言 1. SRAM控制原理 1.1 SRAM功能框图 1.2 SRAM读写时序 2. FSMC简介 2.1 FSMC架构 2.2 FSMC地址映射 2.3 FSMC控制SRAM时序 3. FSMC结构体 4. 库函数配置FSMC 5. 实验程序 5.1 main.c 5.2 SRAM.c 5.3 SRAM.h 前言 STM32F4自带了192K字节的SRAM࿱…...

Java的代理模式
java有三种代理模式 静态代理 jdk动态代理 cglib实现动态代理 代理模式的定义: 为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的…...
FilterAttributeOnClassMethod
目录 1 BadMethodFilterAttribute 2 FilterAttributeOnClassMethod 2.1 OnMethodExecuted 2.2 OnMethodExecutedAsync 2.3 OnMethodExecuting BadMethodFilterAttribute using System; using System.Threading.Tasks; namespace Flatwhite.Core.Tests.Attributes …...
springboot + (mysql/pgsql) + jpa 多数据源(不同类数据源)
配置文件: spring:datasource:primary:jdbc-url: jdbc:mysql://host:3306/数据库?useUnicodetrue&characterEncodingUTF-8&autoReconnecttrue&failOverReadOnlyfalse&serverTimezoneAsia/Shanghai&zeroDateTimeBehaviorconvertToNullusername…...
【Golang】Golang进阶系列教程--Go 语言 context 都能做什么?
文章目录 前言核心是 Context 接口:包含四个方法:遵循规则WithCancelWithDeadlineWithTimeoutWithValue 前言 很多 Go 项目的源码,在读的过程中会发现一个很常见的参数 ctx,而且基本都是作为函数的第一个参数。 为什么要这么写呢…...

画图干货!14种uml图类型及示例
1. 什么是 UML UML 是统一建模语言的缩写。UML 图是基于 UML(统一建模语言)的图表,目的是直观地表示系统及其主要参与者、角色、动作、工件或类,以便更好地理解、更改、维护或记录信息关于系统。简而言之,UML 是一种…...

计算机视觉实验:人脸识别系统设计
实验内容 设计计算机视觉目标识别系统,与实际应用有关(建议:最终展示形式为带界面可运行的系统),以下内容选择其中一个做。 1. 人脸识别系统设计 (1) 人脸识别系统设计(必做):根据…...

振弦采集仪完整链条的岩土工程隧道安全监测
振弦采集仪完整链条的岩土工程隧道安全监测 隧道工程是一种特殊的地下工程,其建设过程及运行期间,都受到各种内外力的作用,如水压、地震、地质变形、交通荷载等,这些因素都会对隧道的安全性产生影响。因此,对隧道的安…...

NLP实战9:Transformer实战-单词预测
目录 一、定义模型 二、加载数据集 三、初始化实例 四、训练模型 五、评估模型 🍨 本文为[🔗365天深度学习训练营]内部限免文章(版权归 *K同学啊* 所有) 🍖 作者:[K同学啊] 模型结构图: &a…...
使用Vue.js和Rust构建高性能的物联网应用
物联网(IoT)应用是现代技术的重要组成部分,它们可以在各种场景中(例如智能家居,工业自动化等)提供无缝的自动化解决方案。在这篇文章中,我们将探讨如何使用Vue.js和Rust构建高性能的物联网应用。 1. 为什么选择Vue.js…...

idea调节文字大小、日志颜色、git改动信息
idea调节菜单栏文字大小: 调节代码文字大小: 按住ctrl滚动滑轮可以调节代码文字大小: 单击文件即可在主窗口上打开显示: idea在控制台对不同级别的日志打印不同颜色 : “grep console”插件 点击某一行的时候&#x…...
避免大龄程序员边缘化:如何在技术行业中保持竞争力
目录 导语持续学习和进修维护专业形象寻找适合自己的领域构建个人品牌和网络拥抱变化和创新实例结语: 导语 导语:随着科技的不断发展,技术行业的竞争日益激烈。对于那些年龄稍长的程序员来说,如何保持竞争力并避免边缘化成为了一…...

Jenkins工具系列 —— 启动 Jenkins 服务报错
错误显示 apt-get 安装 Jenkins 后,自动启动 Jenkins 服务报错。 排查原因 直接运行jenkins命令 发现具体报错log:Failed to start Jetty或Failed to bind to 0.0.0.0/0.0.0.0:8080或Address already in use 说明:这里提示的是8080端口号…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...

相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...

Golang——7、包与接口详解
包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!
本文介绍了一种名为AnomalyAny的创新框架,该方法利用Stable Diffusion的强大生成能力,仅需单个正常样本和文本描述,即可生成逼真且多样化的异常样本,有效解决了视觉异常检测中异常样本稀缺的难题,为工业质检、医疗影像…...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态
前言 在人工智能技术飞速发展的今天,深度学习与大模型技术已成为推动行业变革的核心驱动力,而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心,系统性地呈现了两部深度技术著作的精华:…...

篇章二 论坛系统——系统设计
目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...
Vue3中的computer和watch
computed的写法 在页面中 <div>{{ calcNumber }}</div>script中 写法1 常用 import { computed, ref } from vue; let price ref(100);const priceAdd () > { //函数方法 price 1price.value ; }//计算属性 let calcNumber computed(() > {return ${p…...
node.js的初步学习
那什么是node.js呢? 和JavaScript又是什么关系呢? node.js 提供了 JavaScript的运行环境。当JavaScript作为后端开发语言来说, 需要在node.js的环境上进行当JavaScript作为前端开发语言来说,需要在浏览器的环境上进行 Node.js 可…...