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

C++高手进阶:Windows 模块加载的艺术与策略

前文我们讲到了怎么不依赖第三库,搭建自己的测试框架
没有看的读者可以通过这个链接自行阅读:
👉👉👉 自力更生:0依赖三方库,手把手教你打造专属C++测试框架
作为项目开发来说,我们通常会将不同的功能、数据进行分层或分块,这其实就涉及到不同模块之间的相互调用。选择什么样的策略来实现模块间能力的交互,其实是非常重要的。
本文中,我们就来抽象业务逻辑,看下模块间的加载是如何实现的。

文章目录

    • 模块间的加载方式
      • 动态加载(Dynamic Loading)
      • 静态加载(Static Loading)
    • 什么时候用什么方式
    • 怎么用
      • 动态加载示例介绍
      • 静态加载示例介绍
    • 总结

模块间的加载方式

通常而言,模块间的加载方式可以分为两类:

  • 动态加载
  • 静态加载

什么是动态加载呢?简单而言就是模块间遵循动态加载协议,平台层模块或业务层模块主动的调用 LoadLibrary()GetProcAddress接口来加载其他模块。

什么是静态加载呢?简单而言就是模块间遵循静态加载协议,平台层模块或业务层模块在编译时就将其他模块的依赖关系链接进来(通常建议在编写平台层 CMakeLists.txt 时,将依赖的库或模块列入链接库的列表,并设置链接类型为静态库)。

笔者在撰写此文时特地查了相关资料,对它们的特点做下介绍(PS: 文中有些地方可能有误,如有疑问,欢迎指正):

动态加载(Dynamic Loading)

动态加载指的是在程序运行时,根据需要加载和卸载库或模块。这种方式有以下几个特点:

  1. 运行时链接:库的代码在程序运行时才被加载,通常是通过动态链接库(Dynamic Link Library,DLL)在Windows上或共享库(Shared Object,SO)在Unix-like系统上实现。
  2. 灵活性:可以在不重启程序的情况下加载或卸载模块,提供更高的灵活性。
  3. 内存使用:只需要加载正在使用的库,节省内存。
  4. 更新和维护:可以独立更新库而不需要重新编译整个程序。
  5. 依赖管理:需要更复杂的依赖管理,因为库在运行时才被加载。

静态加载(Static Loading)

静态加载指的是在程序编译时,库或模块已经被链接到程序中。这种方式有以下几个特点:

  1. 编译时链接:库的代码在编译期间被链接到最终的可执行文件中。
  2. 加载时间:因为库的代码已经是程序的一部分,所以不需要在程序运行时加载。
  3. 内存使用:静态加载的库会占用更多的内存,因为所有库的代码都会被包含在最终的可执行文件中。
  4. 更新和维护:更新静态加载的库可能需要重新编译整个程序。
  5. 依赖管理:静态加载简化了依赖管理,因为所有依赖都包含在程序中。

什么时候用什么方式

也许读者在其他地方看到过它的使用建议,比如说根据加载的时间内存的使用模块间交互的频繁性模块间的依赖关系复杂度模块的更新频率等方面进行选择。

笔者就不在从这些维度进行阐述了。

笔者从工程实践的角度出发,讲讲两种使用的场景:

  1. 当主模块没有代码逻辑依赖该模块暴露的API接口时,该模块的能力仅仅是主模块能力的增强,就可以采用动态加载方式。
  2. 当主模块有代码逻辑依赖该模块暴露的API接口时,该模块的能力是主模块的核心功能,就可以采用静态加载方式。

怎么用

动态加载示例介绍

场景: 假设有主模块A,在其上开发了模块B,以进行能力增强。

那么对于模块 A 来说,得做如下事情:

  1. 定义模块间交互的协议类 TestModuleBase,用于模块 B 来派生,自定义自己的加载、卸载业务逻辑。
// xx.h
class TestModuleBase
{
public:TestModuleBase();virtual ~TestModuleBase();virtual SystemStatus initialize();virtual SystemStatus uninitialize();private:TestModuleBase(const TestModuleBase&) = delete;TestModuleBase& operator=(const TestModuleBase&) = delete;
};typedef TestModuleBase* (__cdecl* RxProlModule)();// xx.cpp
TestModuleBase::TestModuleBase()
{}TestModuleBase::~TestModuleBase()
{}SystemStatus TestModuleBase::initialize()
{return SystemStatus::e_Ok;
}SystemStatus TestModuleBase::uninitialize()
{return SystemStatus::e_Ok;
}
  1. 定义一个模块加载器 TestModuleLoader 类,用来管理模块的加载和卸载。
// xx.h
class DEMO_TEST_STATIC_EXPORT TestModuleLoader
{
public:TestModuleLoader() = delete;static SystemStatus loadModule(const wchar_t* pPath);static SystemStatus unloadModule(const wchar_t* pPath);private:TestModuleLoader(const TestModuleLoader&) = delete;TestModuleLoader& operator=(const TestModuleLoader&) = delete;
};// xx.cpp
SystemStatus TestModuleLoader::loadModule(const wchar_t* pPath)
{HMODULE pHandleModule = LoadLibrary(pPath);if (!pHandleModule == NULL) {return SystemStatus::e_Fail;}RxProlModule rxProlModule = (RxProlModule)(GetProcAddress(pHandleModule, "rxProtocalModule"));if (rxProlModule == NULL) {FreeLibrary(pHandleModule);return SystemStatus::e_Fail;}TestModuleBase* pModule = rxProlModule();if (pModule == NULL) {FreeLibrary(pHandleModule);return SystemStatus::e_Fail;}return pModule->initialize();
}SystemStatus TestModuleLoader::unloadModule(const wchar_t* pPath)
{if (pPath == NULL) {return SystemStatus::e_Fail;}HMODULE pHandleModule = GetModuleHandle(pPath);if (pHandleModule == NULL) {return SystemStatus::e_Fail;}RxProlModule rxProlModule = (RxProlModule)(GetProcAddress(pHandleModule, "rxProtocalModule"));if (rxProlModule == NULL) {FreeLibrary(pHandleModule);return SystemStatus::e_Fail;}TestModuleBase* pModule = rxProlModule();if (pModule == NULL) {FreeLibrary(pHandleModule);return SystemStatus::e_Fail;}SystemStatus uninitResult = pModule->uninitialize();if (uninitResult != SystemStatus::e_Ok) {return uninitResult;}FreeLibrary(pHandleModule);return SystemStatus::e_Ok;
}
  1. 在适当的时机,比如说模块A初始化和反初始化的时候,对模块B进行主动的加载和卸载。
SystemStatus PlatformModule::initialize()
{...auto ss = TestModuleLoader::loadModule("DemoModule1.dll")if (ss != SystenStatus::e_Ok) {return ss;}...
}SystemStatus PlatformModule::uninitialize()
{...auto ss = TestModuleLoader::unloadModule("DemoModule1.dll")if (ss != SystenStatus::e_Ok) {return ss;}...
}

那么对于模块 B 来说,得做如下事情:

  1. 派生协议类 TestModuleBase,实现自己的加载、卸载业务逻辑。
// xx.h
class DemoTestModule :public TestModuleBase
{
public:DemoTestModule();~DemoTestModule();SystemStatus initialize() override;SystemStatus uninitialize() override;private:DemoTestModule(const DemoTestModule&) = delete;DemoTestModule& operator=(const DemoTestModule&) = delete;bool m_initialized = false;
};DEMO_TEST_C_EXPORT TestModuleBase* rxProtocalModule();// xx.cpp
DEMO_TEST_C_EXPORT TestModuleBase* rxProtocalModule()
{static DemoTestModule module;return &module;
}DemoTestModule::DemoTestModule()
{}DemoTestModule::~DemoTestModule()
{}SystemStatus DemoTestModule::initialize()
{if (m_initialized) {return SystemStatus::e_NotSpecified;}m_initialized = true;// do something for initreturn SystemStatus::e_Ok;
}SystemStatus DemoTestModule::uninitialize()
{if (m_initialized) {// do uninit thingm_initialized = false;}return SystemStatus::e_Ok;
}

静态加载示例介绍

场景: 假设有主模块A,在其上开发了模块B,以实现核心功能。

  1. 对于模块B 来说,需定义一个模块初始化的类,实现该模块中对象的初始化和反初始化。同时给该类设置一个静态对象。
class DemoModuleInit
{
public:DemoModuleInit(){// do init thing}~DemoModuleInit(){// do uninit thing}
};static DemoModuleInit g_demoModuleInit;
  1. 在模块 A 的CMakeLists.txt中,将模块B的静态库或模块链接进来。
link_directories("${PROJECT_SOURCE_DIR}/lib")
...
link_libraries(DemoModule1)

总结

以上就是模块间加载的两种方式,动态加载和静态加载,以及它们的使用场景和示例。希望能对读者有所帮助。

如果读者有不理解的地方,欢迎私信交流。

相关文章:

C++高手进阶:Windows 模块加载的艺术与策略

前文我们讲到了怎么不依赖第三库,搭建自己的测试框架 没有看的读者可以通过这个链接自行阅读: 👉👉👉 自力更生:0依赖三方库,手把手教你打造专属C测试框架 作为项目开发来说,我们通常…...

基于STM32单片机老人体温心率血氧跌倒定位短信报警

一.硬件及设计功能 以STM32F103C8T6为中央处理器,GPS模块用采集数据,将数据发送给单片机后,单片机根据定位计算公式得出当前位置的经纬度信息和时间信息。经过LCD显示器处理后得出和时间信息SIM800模块发送短信到设定的手机号上,将…...

【测评】雨云香港三区云服务器,2核2G 5兆,仅需38元/月

写在前面 雨云香港三区云服务器,高性能的 AMD EPYC 处理器 企业级 NVME SSD 高性能云服务器。2核2G 10兆 400G防御,仅需38元/月,年付7折仅 319.2元/年。 官网:https://www.rainyun.com 本次测评服务器配置如下: C…...

如何应对Android面试官 -> 玩转 Fragment

前言 本章主要讲解下 Framgent 的核心原理; 基础用法 线上基础用法,其他的可以自行百度 FragmentManager manager getSupportFragmentManager(); FragmentTransaction transaction manager.beginTransaction(); transaction.add(R.id.contentlayout,…...

sdbusplus:通过文件描述符传递数据

有的时候需要传递大量的数据,如果将数据通过dbus传递,会消耗大量的带宽。可以通过传递一个文件描述符替代传递数据: 以下的service通过文件描述符接收数据: //fd_service.cpp #include <sdbusplus/asio/connection.hpp> #include <sdbusplus/asio/object_server…...

HyperLPR3 车牌识别

Linux 之前安装了python3 apt install python3.8-venv cd /root python3 -m venv HyperLPR3 REM cd HyperLPR3 source HyperLPR3/bin/activate 参考 https://www.jb51.net/article/222885.htm python -m pip install hyperlpr3 里面有fastapi&#xff0c;opencv等 错误&#x…...

面试的内容

1.C的三大特性&#xff1a;封装&#xff0c;继承&#xff0c;多态 2.C11的特性 3.NULL与Nullptr的区别: nullptr是一个特殊的空指针常量&#xff0c;不能被隐式转换为其他类型。 NULL 在一些情况下可能会发生隐式类型转换 4.智能指针 5.stl/Qt stl stl容器包含哪些&…...

剪映网页版

https://www.capcut.cn/web 免费&#xff0c;免安装&#xff0c;跨平台&#xff0c;视频云合成&#xff0c;简直太好用了&#xff01;...

pgsql

创建分区表&#xff1a; PostgreSQL分区表_pg分区表-CSDN博客 创建list分区的函数 create or replace function create_list_fq(tb_name char, row_name char) returns int AS $$ declares char; beginraise notice CREATE TABLE if not exists %_% PARTITION OF % FOR VALU…...

Kotlin学习笔记 泛型

在 Kotlin 中&#xff0c;T 通常用作类型参数的占位符&#xff0c;它在实例化或传递参数时会被替换成具体的类型。 Kotlin 支持泛型&#xff0c;这意味着您可以编写可以与多种数据类型一起工作的代码&#xff0c;而不必为每种数据类型编写单独的代码。 ### 泛型类和函数 在 …...

开发者必看:Linux终端的10大装逼神器,让你的命令行炫酷起来!

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

20 VUE学习:插件

介绍 插件 (Plugins) 是一种能为 Vue 添加全局功能的工具代码。下面是如何安装一个插件的示例&#xff1a; import { createApp } from vueconst app createApp({})app.use(myPlugin, {/* 可选的选项 */ })一个插件可以是一个拥有 install() 方法的对象&#xff0c;也可以直接…...

python之any用法

写法对比 代码一&#xff1a; any(i for i in [0,1]) 代码2&#xff1a; any([i for i in [0,1]]) 优劣 结论&#xff1a;代码一写法更好 解释&#xff1a; 在 Python 中&#xff0c;any() 函数可以接受一个可迭代对象作为参数&#xff0c;并返回 True 如果可迭代对象…...

【前端学习——react坑】useState使用

问题 使用useState 时&#xff0c;例如 const [selectedId, setSelectedId] useState([false,true,false]);这样直接利用&#xff0c;无法引发使用selectedId状态的组件的变化&#xff0c;但是selectedId是修改了的 let tempselectedId;temp[toggledId]selectedId[toggledId…...

【前端每日基础】day28——async/await

async/await 是ES2017&#xff08;ES8&#xff09;引入的用于处理异步操作的语法糖&#xff0c;基于Promise实现。它使得异步代码看起来像同步代码&#xff0c;从而提高了代码的可读性和可维护性。以下是对 async/await 的详细讲解。 基本语法 async 函数 在一个函数前加上 as…...

错误记录:从把项目从Tomcat8.5.37转到Tomcat10.1.7

错误信息&#xff1a;在本地Servlet项目里没有报错&#xff0c;但是浏览器跳转该servlet时报错 型 异常报告 消息 实例化Servlet类[com.wangdao.lx.MyServlet1]异常 描述 服务器遇到一个意外的情况&#xff0c;阻止它完成请求。 例外情况 jakarta.servlet.ServletExceptio…...

AJAX基础知识

定义 Ajax 异步 JavaScript 和 XML &#xff08; async javascript and xml &#xff09;&#xff0c;使用 Ajax 技术网页应用能够快速地将数据更新呈现在用户界面上&#xff0c;而不需要重载&#xff08;刷新&#xff09;整个页面&#xff0c;这使得程序能够更快地回应用户的操…...

xcode依赖包package已经安装,但是提示No such module ‘Alamofire‘解决办法

明明已经通过xcode自带的swift包管理器安装好了依赖包&#xff0c;但是却还是提示&#xff1a;No such module&#xff0c;这个坑爹的xcode&#xff0c;我也只能说服气&#xff0c;但是无奈&#xff0c;没办法攻打苹果总部&#xff0c;只能自己想解决办法了 No such module Ala…...

基于Centos7 安装k8s一主两从

一、资源准备 mac下虚拟机环境搭建 1、使用搜狐的iso源 http://mirrors.sohu.com/centos/7.5.1804/isos/x86_64/CentOS-7-x86_64-Minimal-1804.iso 下载 iso镜像。 2、https://www.macwk.com/soft/vmware 下载 mac vm虚拟机 3、搭建一主两从集群所需虚拟机 4、新建虚拟机…...

基于java实现图片中任意封闭区域识别

需求&#xff1a; 在浏览器中给用户呈现一张图片&#xff0c;用户点击图片中的某些标志物&#xff0c;需要系统给出标志物的信息反馈&#xff0c;达到一个交互的作用。 比如下图中&#xff0c;点击某个封闭区域时候&#xff0c;需要告知用户点击的区域名称及图形形状特性等等。…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

19c补丁后oracle属主变化,导致不能识别磁盘组

补丁后服务器重启&#xff0c;数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后&#xff0c;存在与用户组权限相关的问题。具体表现为&#xff0c;Oracle 实例的运行用户&#xff08;oracle&#xff09;和集…...

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

基于matlab策略迭代和值迭代法的动态规划

经典的基于策略迭代和值迭代法的动态规划matlab代码&#xff0c;实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求&#xff0c;本次涉及的主要是收费汇聚交换机的配置&#xff0c;浪潮网络设备在高速项目很少&#xff0c;通…...