当前位置: 首页 > article >正文

C++脚本化方案调研

1 什么是脚本化

  脚本化(Scripting)是指将脚本语言嵌入到主程序(C++等编译型语言)中,通过以下方式扩展程序能力:

  1. 动态逻辑控制:通过脚本实现运行时逻辑调整,无需重新编译主程序,提升开发效率;
  2. 跨语言交互:建立C++与脚本语言的双向调用通道,实现跨语言交互,实现跨平台开发;
  3. 热更新支持:通过替换脚本文件实现功能迭代,保障主程序持续运行,不影响业务;
  4. 配置数据驱动:将业务规则/数值表等易变内容外置为脚本文件,实现配置数据的灵活管理。

典型应用场景:

  • 游戏开发:NPC行为逻辑、AI行为树、动画逻辑、特效逻辑等;
  • 工业仿真:参数化建模流程,数据驱动仿真;
  • 图形渲染:Shader脚本动态编译,实现实时效果预览;
  • 自动化测试:用例脚本驱动,实现自动化测试。

2 为什么要脚本化

  C++等编译型语言的工程通常存在以下痛点:

  1. 编译期僵化:修改C++代码需重新编译(大型项目编译耗时可达10+分钟),影响开发效率;
  2. 硬编码困境:业务规则变更需重新部署,影响业务稳定性;
  3. 安全隔离:脚本虚拟机沙箱机制可防止崩溃扩散(对比直接调用DLL的风险)。
维度纯C++方案脚本化方案
迭代速度需全量编译部署热更新脚本文件即时生效
风险控制崩溃导致进程退出脚本异常局部捕获
协作效率要求C++全栈能力分离核心/脚本开发角色

  虽然脚本化方案可以解决上述痛点,但也存在以下问题:

  1. 开发门槛高:脚本语言与C++的语法差异较大,需要一定的学习成本;
  2. 性能损耗:脚本化方案通常需要额外的解释器/虚拟机,带来一定的性能损耗;
  3. 安全风险:脚本化方案需要保证脚本的安全性,避免恶意代码注入。

  因此,脚本化方案需要在开发成本、性能损耗、安全风险之间进行权衡。通常将性能敏感的部分用C++实现,将性能不敏感的部分用脚本实现来实现快速迭代,以及兼顾开发效率和性能。

3 如何脚本化

  C++脚本化方案主要有以下几种:

  1. 直接嵌入脚本语言的解释器,如Lua、Python等;
  2. 通过子程序的方式调用脚本模块。

3.1 嵌入解释器

  

3.1.1 常见方案对比

特性LuaPythonChaiScriptJavaScript
集成难度★★☆☆☆★★★☆☆★★★★★★★★★☆
性能表现1亿次/秒(JIT)100万次/秒5000万次/秒1.2亿次/秒(V8 JIT)
800万次/秒(QuickJS)
内存占用200KB ~ 2MB10MB ~ 100MB5MB ~ 20MB5MB ~ 50MB(V8)
1MB ~ 5MB(QuickJS)
线程安全需手动加锁GIL限制原生支持隔离上下文+Worker线程
调试支持ZeroBrane StudioPDB/VS Code原生GDB集成Chrome DevTools Protocol
典型应用场景游戏AI/UI逻辑机器学习管线实时控制系统Web嵌入/跨平台应用

3.1.2 绑定技术实现

LuaJIT集成示例

// 创建Lua虚拟机
lua_State* L = luaL_newstate();
luaL_openlibs(L);// 注册C++函数
lua_pushcfunction(L, &cpp_function);
lua_setglobal(L, "cpp_func");// 执行Lua脚本
luaL_dostring(L, "print('Lua调用C++:', cpp_func(42))");

pybind11绑定示例

PYBIND11_MODULE(example, m) {m.def("add", &add, "加法函数");py::class_<MyClass>(m, "MyClass").def(py::init<>()).def("process", &MyClass::process);
}

3.1.3 内存安全机制

  • 隔离堆管理:为脚本分配独立内存池
  • 引用跟踪:使用智能指针包装C++对象
  • 沙箱策略
    -- 限制脚本访问危险函数
    debug = nil
    os.execute = nil
    

3.1.4 混合调试方案

  1. 符号映射:在编译时生成PDB符号文件
  2. 断点代理:通过VS Code调试器同时捕获C++和脚本异常
  3. 日志追踪
# 跨语言调用追踪
import inspect
print(f"[Trace] {inspect.stack()[1].function}()")

3.1.5 模块化集成方案

  将脚本解释器封装为独立模块,通过明确接口边界实现松耦合集成:

模块化架构要素

  1. 接口隔离层:定义ScriptEngine抽象接口
  2. 动态加载机制:支持运行时模块热替换
  3. ABI兼容保障:采用C接口+版本号校验
  4. 资源隔离:独立内存池与异常处理边界

Lua模块化示例(CMake)

# 模块化Lua解释器
add_library(lua_module SHAREDlua_engine.cpplua_bindings.cpp)
target_link_libraries(lua_module PRIVATE Lua::Lua)
set_target_properties(lua_module PROPERTIESCXX_VISIBILITY_PRESET hiddenVERSION "1.0.0")# 主程序动态加载
add_executable(main_app main.cpp)
target_compile_definitions(main_app PRIVATE MODULE_DIR="${CMAKE_BINARY_DIR}")

跨语言对象传递机制

语言对象封装方式生命周期管理
Lualightuserdata+元表引用计数+GC标记
Pythonpybind11::class_智能指针+Gil锁
ChaiScriptBoxed_Value移动语义+类型擦除

热加载实现流程

  1. 使用dlopen加载模块.so/dll
  2. 通过dlsym获取create_engine符号
  3. 双缓冲引擎实例平滑切换
  4. 旧模块延迟卸载(引用计数归零)

ABI兼容性测试方案

// 版本号校验
void verify_abi_version(const ScriptEngine* engine) {assert(engine->get_abi_version() == SCRIPT_ABI_VERSION && "ABI不兼容");
}// 结构体对齐测试
static_assert(sizeof(ModuleHeader) == 64, "头结构体大小变化,需重新编译模块");

性能提升数据(模块化前后对比):

测试项集成式方案模块化方案
冷启动时间120ms80ms(-33%)
内存碎片率15%8%(-47%)
函数调用开销45ns28ns(-38%)
热加载耗时N/A12ms

3.2 子程序调用

3.2.1 通过FFI直接绑定

LuaJIT调用C++示例

// 导出C++函数
LUA_API int luaopen_mylib(lua_State* L) {lua_pushcfunction(L, [](lua_State* L) -> int {double x = luaL_checknumber(L, 1);lua_pushnumber(L, x * 2);return 1;});lua_setglobal(L, "cpp_double");return 0;
}-- Lua调用示例
print(cpp_double(21))  -- 输出42.0

Python ctypes调用DLL

from ctypes import CDLL, c_double
lib = CDLL('./mylib.dll')
lib.process_data.argtypes = [c_double]
lib.process_data.restype = c_double
print(lib.process_data(3.14))  # 输出处理结果

3.2.2 CLI命令调用

封装C++为可执行文件

# 编译为可执行文件
g++ -std=c++17 -o processor main.cpp# 脚本调用示例
#!/bin/bash
INPUT=42
OUTPUT=$(./processor $INPUT)
echo "处理结果: $OUTPUT"

3.2.3 RPC服务框架

gRPC服务定义

syntax = "proto3";
service Processor {rpc Calculate (Request) returns (Response) {}
}message Request {double input = 1;
}message Response {double result = 1;
}

序列化方案对比

特性ProtobufMsgPack
编码效率高(二进制)高(二进制)
解码速度快(预生成代码)较快
类型安全强类型约束动态类型
跨语言支持官方支持多语言社区实现
数据验证内置schema校验需额外验证

3.2.4 安全调用策略

参数校验机制

// C++参数校验示例
try {if (value < 0) throw std::invalid_argument("值不能为负");// 处理逻辑
} catch (const std::exception& e) {std::cerr << "错误: " << e.what() << std::endl;
}

超时控制实现

# Python子进程超时控制
import subprocess
try:result = subprocess.run(['./processor', '42'],timeout=5,check=True,capture_output=True)
except subprocess.TimeoutExpired:print("处理超时!")

IPC通信加密

// 使用OpenSSL加密通信
SSL_CTX* ctx = SSL_CTX_new(TLS_method());
SSL* ssl = SSL_new(ctx);
SSL_set_fd(ssl, socket_fd);
SSL_connect(ssl);
SSL_write(ssl, data, data_len);

4 详细示例

4.1 Lua与C++交互

4.1.1 C++调用Lua函数

  lua与C++交互需要在C++创建lua的虚拟机,通过lua的虚拟机将参数传递给C++函数。调用C++函数时需要提前注册调用的函数,随后通过Lua虚拟机将参数传递给C++函数。Lua 是一种基于栈的虚拟机,使用一个单一的栈来存储所有变量和函数参数,对应C++的函数就是:

  • 压栈: 使用 lua_pushintegerlua_pushstring等函数将数据压入栈中。
  • 弹栈: 使用 lua_pop函数从栈中移除数据。
  • 访问栈: 使用 lua_tointegerlua_tostring等函数从栈中读取数据(需要注意的是lua虚拟机的栈从1开始计数而不是0)。

  下面是一个简单的示例,演示了如何在C++中调用Lua函数并传递参数:

#include <iostream>
#include <lua.hpp>
#include <format>static int add(lua_State* lua) {if (lua_gettop(lua) < 2) {lua_pushstring(lua, "Add need two parameters");lua_error(lua);}const auto a = luaL_checkinteger(lua, 1);const auto b = luaL_checkinteger(lua, 2);const auto sum = a + b;lua_pushnumber(lua, sum);return 1;
}int main(int argc, char **argv){std::cout<<"hello world\n";auto lua = luaL_newstate();luaL_openlibs(lua);lua_register(lua, "add", add);const std::string script = "main.lua";if (luaL_dofile(lua, script.c_str()) != LUA_OK) {auto error = lua_tostring(lua, -1);std::cerr << "Error Msg: " << error <<std::endl;lua_settop(lua, 0); // 清空栈保证状态}lua_close(lua);return 0;
}

  对应的lua脚本比较简单:

print("Hello Lua")
print(add(1, 2))

4.1.2 LuaBridge

  直接使用liblua的api和C++交互比特别是自定义数据需要构建userdata比较麻烦,可以使用一些第三方库比如LuaBridge,luabind,Kahlua,Sol2等。LuaBridge 是一个轻量级的 C++ 库,用于将 C++ 类和函数绑定到 Lua,简单易用,支持基本的类型绑定,没有外部依赖,易于集成,支持 C++11 的功能。因此,下面就使用LuaBridge来实现一个简单的例子:

#include <iostream>
#include <lua.hpp>
#include <LuaBridge/LuaBridge.h>
class MyClass {
public:MyClass(const std::string& name) : name(name) {}std::string getName() {return name;}void setName(const std::string& newName) {name = newName;}static void bind(lua_State* L) {luabridge::getGlobalNamespace(L).beginClass<MyClass>("MyClass").addConstructor<void(*)(const std::string&)>().addFunction("getName", &MyClass::getName).addFunction("setName", &MyClass::setName).endClass();}
private:std::string name;
};int main() {lua_State* L = luaL_newstate();luaL_openlibs(L);MyClass::bind(L);// 执行 Lua 脚本if (luaL_dofile(L, "main.lua") != LUA_OK) {std::cerr << "Error: " << lua_tostring(L, -1) << std::endl;lua_pop(L, 1);}lua_close(L);return 0;
}

  Lua的脚本内容如下:

local obj = MyClass("MyClass")
print(obj:getName())
obj:setName("LuaMyClass")
print(obj:getName())

  更详细的例子可以参考LuaBridge Source Example。

  需要注意的是,虽然使用了LuaBridge库,但是底层还是使用的liblua的api,因此还是需要在C++中创建lua的虚拟机,通过lua的虚拟机将参数传递给C++函数。另外LuaBridge库的使用比较简单,但是也有一些限制,比如不能使用C++11的特性,LuaBridge 对 C++ 的模板、多态、异常处理、复杂类型、引用和指针、运算符重载、命名空间以及最新 C++ 特性的支持有限。

4.1.3 lua调用动态库

  lua调用动态库需要使用C的接口,和C++调用动态库的方式类似,需要使用dlopen打开动态库,然后使用dlsym获取函数指针,最后使用函数指针调用函数。下面是一个简单的示例:

#include <lua.hpp>
#include <LuaBridge/LuaBridge.h>
#include <iostream>#if defined(_WIN32)
#define SYM_EXPORT __declspec(dllexport)
#else
#define SYM_EXPORT
#endifclass SYM_EXPORT MyClass {
public:MyClass(const std::string& name) : name(name) {}std::string getName() {return name;}void setName(const std::string& newName) {name = newName;}private:std::string name;
};// 导出到 Lua 的初始化函数
extern "C" {extern "C" SYM_EXPORT int luaopen_mylib(lua_State* L) {luabridge::getGlobalNamespace(L).beginClass<MyClass>("MyClass") // 注册 MyClass.addConstructor<void(*)(const std::string&)>().addFunction("getName", &MyClass::getName).addFunction("setName", &MyClass::setName).endClass();return 0;}
}

  调用动态库的lua脚本如下:

local mylib, err = package.loadlib("mylib.dll", "luaopen_mylib")
if not mylib thenerror("Failed To Load DLL: "..tostring(err))
end
mylib()
local obj = MyClass("World")
print(obj:getName())
obj:setName("LuaBridge")
print(obj:getName())

4.2 Python与C++交互

  Python本身也支持与C++交互,可以使用Python的C API来调用C++的函数。使用一些额外的库来实现Python与C++的交互,比如pybind11,cffi,swig等更加简单高效。

  • ctypes: 最简单的方式,适合快速集成。
  • Cython: 提供更高的性能和更好的类型安全,适合中小型项目。
  • SWIG: 自动化程度高,适合大型项目。
  • pybind11: 简单易用,适合现代 C++,依赖较少。

  swig是一个跨语言的编译器,它可以将C++代码编译成多种语言的接口,比如Python,Java,C#,Ruby等。swig的使用比较简单,但是也有一些限制。下面是一个简单的示例:

//add.h
#pragma once
int add(const int a, const int b);//add.c
#include "add.h"int add(const int a, const int b) {return a + b;
}

  除了上面两个文件还需要一个add.i的文件,这个文件是swig的接口文件,里面定义了C++的函数和参数类型。接口文件是告诉 Swig 应该怎样把原生语言(这里指的是 C++),转换为目标语言(这里指的是 Python),如原生语言中一些数据接口与目标语言不一样的地方。那 C++ 来说,C++ 中会用到指针,vector, string, map, pair 等部分,那么这部分就需要明确的告诉 Swig 应该怎样处理。Swig 有一些标准库的接口文件,已经对这个做了很好的处理,我们只需要在接口文件中对此做个简单的描述即可。

%module add%{
#define SWIG_FILE_WITH_INIT
#include "add.h"
%}int add(const int a, const int b);

  然后使用swig命令生成对应的python文件:

swig -c++ -python add.i

  最后编写自动编译的脚本:

from distutils.core import setup, Extensionadd_module = Extension('_add',sources=['add_wrap.c', 'add.c'],)setup(name='add',version='0.1',author="gg",description="""Simple swig example from docs""",ext_modules=[add_module],py_modules=["add"],)

  最后执行下面的脚本编译可得到python的模块文件,随后在python中导入使用即可。

python setup.py build_ext --inplace

4.3 JS与C++交互

  一些流行的 JavaScript 调用 C++ 的框架和工具,适合不同场景。Node.js 原生模块允许通过 C++ 扩展与 JavaScript 直接交互,适合服务器端高性能需求。Emscripten将 C/C++ 代码编译为 WebAssembly,可在浏览器中直接调用,适合游戏和图形处理。SWIG自动生成 C/C++ 与多种语言的接口,适合需要快速多语言绑定的项目。N-API为 Node.js 提供稳定的 API,适合长期维护的扩展。WebAssembly是一种现代二进制格式,能在浏览器中高效运行,适合高性能客户端应用。Qt for WebAssembly支持将 Qt 应用程序编译为 WebAssembly,适合图形用户界面开发。最后,nan简化了在 Node.js 中编写 C++ 扩展的过程,尤其是处理 V8 API 的复杂性。这些工具和框架为开发者在 JavaScript 环境中高效调用 C++ 提供了多种选择。

  JS和C++交互和Python都可以使用SWIG来进行,实现比较简单。代码文件和Python类似也需要.i文件。需要额外准备编译的gyp文件,下面文件中的example_wrap.cxx是通过swig命令swig.exe -c++ -javascript -node .\example.i生成的。

{"targets": [{"target_name": "example","sources": [ "example.cxx", "example_wrap.cxx" ]}]
}

  生成之后再调用node-gyp configure build编译即可得到node模块,随即可以在js中直接调用。

4.4 其他

  其他语言也都提供了和C/C++交互的方式或者库,rbo语言,比如Rust,Go,Dart等。这里就不再赘述。除了语言提供的一些native能力外,还可以通过其他通信方式来实现,比如消息队列,RPC等。这些属于其他的话题了,这里不详细展开。

  另外还有一些专门为C++脚本化开发的库或者第三方工具,比如:

  • ChaiScript: 一种基于Lua的脚本语言,支持C++的绑定,支持动态类型和自动类型转换,轻量级脚本语言,专门为 C++ 设计,易于嵌入和使用。
  • AngelScript:一种基于C++的脚本语言,支持C++的绑定,支持静态类型和类型安全,强类型的嵌入式脚本语言,适合游戏开发和其他 C++ 项目。
  • Squirrel:一种基于C++的脚本语言,支持C++的绑定,支持静态类型和类型安全,高性能的嵌入式脚本语言,适合游戏和应用程序的扩展。

5 总结

  能够看到不同语言的脚本化方案都有自己的优缺点,需要根据具体的场景来选择合适的方案。总的来说,脚本化方案可以提高开发效率,减少开发成本,提高代码的可维护性和可扩展性。但是,脚本化方案也存在一些问题,比如性能损失,安全风险,开发难度等。因此,在选择脚本化方案时,需要综合考虑这些问题,选择合适的方案。

6 参考文献

  • C++/Lua交互指南
  • Lua - Lua 与 C/C++ 交互
  • Chapter 26. Calling C from Lua
  • Swig Wrap C++ for Python
  • SWIG JS
  • C/C++脚本化: 探索篇

相关文章:

C++脚本化方案调研

1 什么是脚本化 脚本化&#xff08;Scripting&#xff09;是指将脚本语言嵌入到主程序&#xff08;C等编译型语言&#xff09;中&#xff0c;通过以下方式扩展程序能力&#xff1a; 动态逻辑控制&#xff1a;通过脚本实现运行时逻辑调整&#xff0c;无需重新编译主程序&#x…...

蓝桥杯(N皇后问题)------回溯法

题目描述 在 NN 的方格棋盘放置了 N 个皇后&#xff0c;使得它们不相互攻击&#xff08;即任意 2 个皇后不允许处在同一排&#xff0c;同一列&#xff0c;也不允许处在与棋盘边框成 45 角的斜线上。你的任务是&#xff0c;对于给定的 N&#xff0c;求出有多少种合法的放置方法…...

再探C语言(1)

温馨提示&#xff1a; 学C语言就像玩《掘地求升》——你以为懂了语法就能通关&#xff1f; 不&#xff01;编译器会用铁锤教你做人&#xff01;(╯‵□′)╯︵┻━┻ &#x1f431;Part 1&#xff1a;sizeofの跨平台迷惑行为 Q1. 不同环境下sizeof(int)的结果 运行环境结果&a…...

高项第十三章——项目资源管理

什么是资源管理&#xff1f;项目资源管理包括识别、获取和管理所需资源以成功完成项目的各个过程。 本过程关注两类资源&#xff1a;实物资源包括设备、材料、设施和基础设施 团队资源或人员指的是团队的人力资源 13_1 项目资源管理基础 项目团队是执行项目工作&#xff0c…...

C/C++转换为字符串宏和字符串拼接宏的综合使用

本文内容参考: C/C++ 宏拼接和宏展开为字符串 - DoubleLi - 博客园 特此致谢! 1. 转换为字符串宏与字符串拼接宏 (1)转换为字符串宏 转换为字符串的宏为: #define STR(x) #x //转字符串 (2)字符串拼接宏 字符串拼接的宏为: #define CONCAT(x,y) x##y //拼接 2…...

Linux:xxx is not in the sudoers file. This incident will be reported.

报错 xxx is not in the sudoers file. This incident will be reported.解决方式 切换到root用户下操作 # 1、修改/etc/sudoers文件为可修改&#xff0c;默认是只读的 ls -lh /etc/sudoers -r--r----- 1 root root 4.3K Dec 1 01:45 /etc/sudoerschmod uw /etc/sudoersls…...

掌握新编程语言的秘诀:利用 AI 快速上手 Python、Go、Java 和 Rust

网罗开发 &#xff08;小红书、快手、视频号同名&#xff09; 大家好&#xff0c;我是 展菲&#xff0c;目前在上市企业从事人工智能项目研发管理工作&#xff0c;平时热衷于分享各种编程领域的软硬技能知识以及前沿技术&#xff0c;包括iOS、前端、Harmony OS、Java、Python等…...

个人常用的chrome好用插件

chrome可以说是兼容性和实用性较高的浏览器 没有复杂的ui 沉重的广告 加上各种各样的浏览器插件 现在罗列一下个人常用的几款好用的插件 1. Adblock Plus 一款免费的广告拦截器&#xff0c;可以拦截大部分网站上的广告推荐&#xff0c;还你一个干净舒服的页面 以下为b站演示…...

Redis 内存优化

Redis 内存优化 Redis性能优化可以从多个方面进行&#xff0c;主要包括以下几个方面&#xff1a; 1. 内存优化 Redis 是基于内存的数据库&#xff0c;优化内存使用可以提高性能并降低成本。 (1) 使用合适的数据结构 不同的数据结构占用的内存不同&#xff0c;选择合适的数据…...

数据库设计-笔记2

1.介绍一下MySQL 历史与发展 MySQL 最初由瑞典的 MySQL AB 公司开发&#xff0c;于 1995 年正式发布。2008 年&#xff0c;MySQL AB 公司被 Sun Microsystems 收购&#xff0c;之后 Sun 又被甲骨文&#xff08;Oracle&#xff09;公司收购&#xff0c;MySQL 成为 Oracle 旗下…...

【大模型】什么是循环神经网络(RNNs)

在人工智能&#xff08;AI&#xff09;的世界里&#xff0c;**循环神经网络&#xff08;Recurrent Neural Networks, RNNs&#xff09;**是一种非常强大的工具&#xff0c;特别适合处理序列数据。无论是语言、时间序列还是音乐&#xff0c;RNNs都能帮助我们理解和预测这些数据的…...

hexo+butterfly搭建博客网站总结篇

hexobutterfly搭建博客网站总结篇 文章目录 hexobutterfly搭建博客网站总结篇0.往期栏目1.发现的不错的butterfly博主2.待实现的功能 && 结语笔者待实现的功能结语 0.往期栏目 个人博客网站搭建_为了前进而后退&#xff0c;为了走直路而走弯路的博客-CSDN博客 【Node…...

损失函数理解(二)——交叉熵损失

损失函数的目的是为了定量描述不同模型&#xff08;例如神经网络模型和人脑模型&#xff09;的差异。 交叉熵&#xff0c;顾名思义&#xff0c;与熵有关&#xff0c;先把模型换成熵这么一个数值&#xff0c;然后用这个数值比较不同模型之间的差异。 为什么要做这一步转换&…...

基于随机森林回归预测葡萄酒质量

基于随机森林回归预测葡萄酒质量 1.作者介绍2.随机森林算法与数据集介绍2.1定义2.2核心思想2.3主要步骤2.4数据集介绍 3.算法实现3.1数据加载与探索3.2数据可视化3.3数据预处理&#xff08;标准化、划分训练/测试集&#xff09;3.4模型训练与优化&#xff08;随机森林回归 超参…...

【Qt】QWidget属性2

&#x1f3e0;个人主页&#xff1a;Yui_ &#x1f351;操作环境&#xff1a;Qt Creator &#x1f680;所属专栏&#xff1a;Qt 文章目录 1. windowOpacity属性2. cursor属性2.1 自定义光标 3. font属性4.tooltip属性5. focusPolicy属性6. 总结 由于QWidget的常见属性实在太多&a…...

OpenGL ES ->乒乓缓冲,计算只用两个帧缓冲对象(Frame Buffer Object)+叠加多个滤镜作用后的Bitmap

乒乓缓冲核心思想 不使用乒乓缓冲&#xff0c;如果要每个滤镜作用下的绘制内容&#xff0c;也就是这个滤镜作用下的帧缓冲&#xff0c;需要创建一个Frame Buffer Object加上对应的Frame Buffer Object Texture使用乒乓缓冲&#xff0c;只用两个Frame Buffer Object加上对应的F…...

数据库练习2

目录 1.向heros表中新增一列信息&#xff0c;添加一些约束&#xff0c;并尝试查询一些信息 2.课堂代码练习 3.题目如下 一、单表查询 1、显示所有职工的基本信息。 2、查询所有职工所属部门的部门号&#xff0c;不显示重复的部门号。 3、求出所有职工的人数。 4…...

macOS Sequoia 15.3 一直弹出“xx正在访问你的屏幕”

&#x1f645; 问题描述 macOS 系统升级后&#xff08;15.2或者15.3均出现过此问题&#xff09;&#xff0c;不管是截图还是开腾讯会议&#xff0c;只要跟捕捉屏幕有关&#xff0c;都一直弹出这个选项&#xff0c;而且所有软件我都允许访问屏幕了&#xff0c;这个不是询问是否…...

OpenCV图像拼接(1)自动校准之校准旋转相机的函数calibrateRotatingCamera()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::detail::calibrateRotatingCamera 是OpenCV中用于校准旋转相机的函数。它特别适用于那种相机相对于一个固定的场景进行纯旋转运动的情况&…...

Conda常用命令汇总(持续更新中)

原文章&#xff1a;安装和使用Miniconda来管理Python环境-CSDN博客 一、Miniconda的使用 Miniconda没有GUI界面&#xff0c;只能通过conda命令对Python环境和软件包进行管理&#xff0c;所以这里主要介绍一下conda的常用命令。 1. Conda相关 (1)查询conda版本 conda --vers…...

C# 调用 VITS,推理模型 将文字转wav音频调试 -数字人分支

Microsoft.ML.OnnxRuntime.OnnxRuntimeException: [ErrorCode:InvalidArgument] Input name: input_name is not in the metadata在 Microsoft.ML.OnnxRuntime.InferenceSession.LookupInputMetadata(String nodeName) 位置 D:\a\_work\1\s\csharp\src\Microsoft.ML.OnnxRuntim…...

Java锁等待唤醒机制

在 Java 并发编程中&#xff0c;锁的等待和唤醒机制至关重要&#xff0c;通常使用 wait()、notify() 和 notifyAll() 来实现线程间的协调。本文将详细介绍这些方法的用法&#xff0c;并通过示例代码加以说明。 1. wait()、notify() 与 notifyAll() 在 Java 中&#xff0c;Obj…...

Docker Compose 常用命令详解

Docker Compose 常用命令详解 Docker Compose 是 Docker 官方提供的一个用于管理多个容器的工具&#xff0c;可以使用 docker-compose.yml 文件定义和运行多容器应用。本篇博客将详细介绍 Docker Compose 的常用命令&#xff0c;帮助你更高效地管理容器。 1. docker compose u…...

C站算法技能题-题解(javascript)

切面条 const 切面条 (n10)>{return 2 ** n 1; } 切面条(0) 2 切面条(1) 3 切面条(2) 5 切面条(10) 1025大衍数列 const 大衍数列 (n100) > {let ans []for(let i1;i<n;i){if(i%2 0){ans.push((i ** 2 ) / 2)}else{ans.push((i ** 2 - 1) / 2)}}return ans…...

【Docker系列一】Docker 简介

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

C++进阶——封装红黑树实现map和set

目录 1、源码及框架分析 2、模拟实现map和set 2.1 复用的红黑树框架及Insert 2.2 iterator的实现 2.2.1 iterator的核心源码 2.2.2 iterator的实现思路 2.3 map支持[ ] 2.4 map和set的代码实现 2.4.1 MyMap.h 2.4.2 MySet.h 2.4.3 RBTree.h 2.4.4 Test.cpp 1、源码及…...

python基础-02-列表+序列数据类型

文章目录 【README】【4】python列表【4.1】列表数据类型【4.1.1】用索引取得列表中的单个值【4.1.2】负数索引【4.1.3】利用切片获取子列表【4.1.4】用索引改变列表中的值【4.1.5】列表连接与复制【4.1.6】del语句删除列表中的元素 【4.2】使用列表【4.2.1】列表用于循环【补充…...

‘闭包‘, ‘装饰器‘及其应用场景

‘闭包’, 装饰器’及其应用场景 一, 闭包及其应用场景 图解 闭包的定义 概述: 内部函数 使用了 外部函数 的变量, 这种写法就称之为闭包. 格式: def 外部函数名(形参列表):外部函数的(局部)变量def 内部函数名(形参列表):内部函数的(局部)变量return 内部函数名前提条件: …...

IDEA 快捷键ctrl+shift+f 无法全局搜索内容的问题及解决办法

本篇文章主要讲解IDEA、phpStrom、webStrom、pyCharm等jetbrains系列编辑器无法进行全局搜索内容问题的主要原因及解决办法。 日期&#xff1a;2025年3月22日 作者&#xff1a;任聪聪 现象描述&#xff1a; 1.按下ctrlshiftf 输入法转为了繁体。 2.快捷键ctrlshiftr 可以全局检…...

Java——Random库

一、作用 Random库——生成随机数 二、实现步骤 1.导包&#xff1a;import java.util.Random; #快捷键&#xff1a;“Random”回车键 2.取得随机数&#xff1a;Random 变量1 new Random(); 3.调用随机数&#xff1a;类型 变量2 变量1.nextInt(n); &#xff08;代表变量…...