C++ 與 Lua 聯合編程

在軟件開發的廣闊天地里,不同編程語言各有所長。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) doprint(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);//還是-3lua_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) doprint(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"
endfunction 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.pswp.cn/diannao/82087.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/82087.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/82087.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

烤箱面包烘焙狀態圖詳解:從UML設計到PlantUML實現

題目&#xff1a;假設你正著手設計一個烤箱。建立一個跟蹤烤箱中面包狀態的狀態圖。要包括必要的觸發器事件、動作和監視條件。 一、狀態圖概述 狀態圖是UML&#xff08;統一建模語言&#xff09;中的一種行為圖&#xff0c;它用于描述系統中對象的狀態變化以及觸發這些變化的…

三款實用工具推薦:配音軟件+Windows暫停更新+音視頻下載!

各位打工人請注意&#xff01;今天李師傅掏出的三件套&#xff0c;都是經過實戰檢驗的效率放大器。先收藏再劃走&#xff0c;說不定哪天就能救命&#xff01; 一.祈風TTS-配音大師 做短視頻的朋友肯定深有體會——配個音比寫腳本還費勁&#xff01;要么付費買聲音&#xff0c…

物流無人機結構與載貨設計分析!

一、物流無人機的結構與載貨設計模塊運行方式 1.結構設計特點 垂直起降與固定翼結合&#xff1a;針對復雜地形&#xff08;如山區、城市&#xff09;需求&#xff0c;采用垂直起降&#xff08;VTOL&#xff09;與固定翼結合的復合布局&#xff0c;例如“天馬”H型無人機&am…

Decode rpc invocation failed: null -> DecodeableRpcInvocation

DecodeableRpcInvocation 異常情況解決方法 錯誤警告官方FAQ 異常情況 記錄一下Dubbo調用異常 java.util.concurrent.ExecutionException: org.apache.dubbo.remoting.TimeoutException: Waiting server-side response timeout by scan timer. start time: 2025-05-07 22:09:5…

Excel VBA 詞頻統計宏

在Excel中&#xff0c;我們經常需要分析文本數據&#xff0c;例如統計某個單詞或短語在文檔中出現的次數。雖然Excel本身提供了一些文本處理功能&#xff08;如COUNTIF&#xff09;&#xff0c;但對于復雜的詞頻統計&#xff0c;手動操作可能效率低下。這時&#xff0c;VBA宏可…

DRV8301 三相電機驅動芯片的硬件參數與應用設計

DRV8301 硬件參數分析 1. 電源與驅動能力 輸入電壓范圍&#xff1a;PVDD1&#xff08;主電源&#xff09;6V~60V&#xff0c;PVDD2&#xff08;降壓轉換器電源&#xff09;3.5V~60V&#xff0c;支持寬電壓應用場景。 驅動電流&#xff1a;1.7A 源極驅動電流&#xff08;Sourc…

QT Sqlite數據庫-教程03 插入數據-下

【1】手動提交事務 #include <QtSql/QSqlDatabase> #include <QtSql/QSqlQuery> #include <QtSql/QSqlRecord>QSqlDatabase db; db.transaction(); for(int i0; i<100000; i){QSqlQuery cmd(QString("UPDATE %1 SET %2%3 WHERE id%4").arg(tab…

LeetCode 每日一題 2025/4/28-2025/5/4

記錄了初步解題思路 以及本地實現代碼&#xff1b;并不一定為最優 也希望大家能一起探討 一起進步 目錄 4/28 2302. 統計得分小于 K 的子數組數目4/29 2962. 統計最大元素出現至少 K 次的子數組4/30 1295. 統計位數為偶數的數字5/1 2071. 你可以安排的最多任務數目5/2 838. 推多…

三、Hadoop1.X及其組件的深度剖析

作者&#xff1a;IvanCodes 日期&#xff1a;2025年5月7日 專欄&#xff1a;Hadoop教程 一、Hadoop 1.X 概述 &#xff08;一&#xff09;概念 Hadoop 是 Apache 開發的分布式系統基礎架構&#xff0c;用 Java 編寫&#xff0c;為集群處理大型數據集提供編程模型&#xff0c;…

Java中字符轉數字的原理解析 - 為什么char x - ‘0‘能得到對應數字

前言 在Java編程中&#xff0c;我們經常需要將字符形式的數字轉換為實際的數值。有很多方法可以實現這一轉換&#xff0c;比如使用Integer.parseInt()或Character.getNumericValue()等方法。但有一種簡便且高效的方式是直接使用char - 0運算&#xff0c;本文將詳細解析這種方法…

第5講、Transformer 編碼器(Encoder)處理過程詳解

&#x1f50d; Transformer 編碼器&#xff08;Encoder&#xff09;處理過程詳解 Transformer Encoder 是一個由 N 層&#xff08;一般為 6 層&#xff09;堆疊而成的模塊結構。每一層的本質是兩個核心子模塊&#xff1a; 多頭自注意力&#xff08;Multi-Head Self-Attention…

SWiRL:數據合成、多步推理與工具使用

SWiRL&#xff1a;數據合成、多步推理與工具使用 在大語言模型&#xff08;LLMs&#xff09;蓬勃發展的今天&#xff0c;其在復雜推理和工具使用任務上卻常遇瓶頸。本文提出的Step-Wise Reinforcement Learning&#xff08;SWiRL&#xff09;技術&#xff0c;為解決這些難題帶…

【Windows 常用工具系列 22 -- vscode markdown preview 字體大小設置】

文章目錄 解決辦法 解決辦法 打開設置&#xff08;快捷鍵 Ctrl , 。或者左下角圖標齒輪 ?&#xff09;搜索設置選項 Markdown ? Preview: Font Size控制 Markdown 預覽中使用的字號(以像素為單位)。 推薦閱讀 https://blog.csdn.net/yanglsbb/article/details/127306685

【風控】模型監控和異常處理

在風控模型的全生命周期中&#xff0c;模型監控與異常處理是保障模型持續、穩定、可靠運行的關鍵環節。本指南旨在提供系統化、可落地的監控指標、預警策略及異常處置流程&#xff0c;幫助團隊快速定位、響應并修復線上模型問題&#xff0c;最大限度降低風險。 1.模型監控與預…

第4章 遞推法

4.1 遞推法概述 設計思想&#xff1a; 遞推法&#xff08;Recurrence Method&#xff09;通過已知的初始條件和遞推關系&#xff0c;逐步推導出問題的最終結果&#xff0c;常用于序列計算和分階段問題求解。 示例&#xff1a;猴子和桃子問題 題目描述&#xff1a; 猴子每天吃…

可視化魔法指南

?? ECharts數據可視化魔法指南 ?? ECharts:數據的藝術畫筆 #mermaid-svg-ARwFHUrXBJ03Gpo9 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-ARwFHUrXBJ03Gpo9 .error-icon{fill:#552222;}#mermaid-svg-ARwFHUr…

SpringBoot學生宿舍管理系統開發實現

概述 一款基于SpringBoot框架開發的學生宿舍管理系統完整項目&#xff0c;該系統包含管理員、學生、宿管員和維修員四大角色模塊&#xff0c;功能完善&#xff0c;非常適合作為設計或二次開發的基礎項目。 主要內容 5.1 管理員功能模塊 管理員登錄界面采用驗證碼驗證機制&a…

同步 / 異步、阻塞 / 非阻塞

前言 同步異步&#xff0c;在計算機科學中是一個非常重要的概念。作為一位軟件開發工程師&#xff0c;我們每天都在和同步和異步打交道。 同步 同步-阻塞&#xff0c;顧名思義&#xff0c;就是同步和阻塞。調用方法后&#xff0c;必須等到結果返回&#xff0c;才能繼續執行別…

AOP封裝進行批量的數據查詢并填充

在我們日常的項目開發中&#xff0c;我們經常會遇到這樣的問題。我們有一張用戶表&#xff0c;用戶表中有用戶ID和用戶名稱。我們其他表中會記錄我們當前操作人的ID&#xff0c;一般&#xff0c;我們會記錄一個創建人ID和修改人ID。那么&#xff0c;這個時候問題來了&#xff0…

Java學習手冊:數據庫事務相關知識

一、事務的概念與特性 概念 &#xff1a;事務是數據庫中一系列操作的集合&#xff0c;這些操作要么全部成功&#xff0c;要么全部失敗&#xff0c;是一個不可分割的工作單位。例如&#xff0c;在銀行轉賬系統中&#xff0c;從一個賬戶扣款和向另一個賬戶存款這兩個操作必須作為…