c web网站开发 视频/seo管理平台
背景:
在项目开发过程中,难免碰到这种情况,当我们想要通过我们开发的库,调用主程序中的一些变量或者函数的时候,就会导致一些问题,因为在项目构建过程中,库都是不依赖于主程序编译的,但是在开发库函数的时候发现,有时候我们不可避免的会和主程序之间的变量或者函数进行沟通,在这种情况下,如何在构建库的过程中,能够调用到主程序的函数? 同时又要避免链接问题(库构建的过程中应该独立构建,不依赖主程序)?
简介:
在目前的项目中,遇到了一个比较特殊的问题,主程序中包括了各种各样的管理器,其中就有一个线程池管理器,这个管理器可以接收任务,然后在后台运行。管理器是一个单例(关于如何构建模版单例,可以查看我这篇博客 -- 博客 ),我在库当中需要通过
Worker::getInstance()->getRuntime()
的方式获取Worker实例下的一个类型,现在就是问题来了,worker是在主程序处实现的,我们在库当中只是单纯的包含了库的头文件,这样在链接阶段就会提示找不到Worker相关的符号(或者因这个原因导致的其他问题)。所以有什么方式能够在写库的时候,能通过某些方式调用到App里面的一些函数吗?
思考:
首先我们要想一下,要实现这样的工作会遇到哪些问题?
第一个如果直接在库当中调用App中的某些函数,一定会遇到链接时找不到对应符号的实现。这是因为我们可以在App中调用库的函数完全是得益于target_link_library(App PRIVATE myLib),这样在编译期间就会通过链接myLib,从而在其中找到App中想要用到的myLib中的函数实现。但是我们没有办法反过来这么指定呀target_link_library(myLib PUBLIC App),因为App是一个可执行文件,是没办法被库链接的!(但是后来查到好像在某些平台可以把App所有的符号都Export出去,但是这不是通用的解决方案)。
怎么解决呢? 我想是否可以将我们想要用到的某个具体函数,封装起来,然后保存到myLib中的某个全局变量中,这样在myLib中调用的话我们直接从这个全局变量中取出这个函数的封装,然后自己拆解就行了。
设计
那接下来是要考虑如何设计这个结构了。
我们想要这么一个结构,可以将App中的一些函数封装起来,然后存储到这个结构体中,然后用到的时候,我们可以把这个结构体转换成我们想用的东西。所以这个结构体可能会封装一个函数,这个函数可以有参数或者无参数,它也可以返回任意类型的参数,否则的话这个结构体的通用性就不强。
所以我设想的是,在一个“池子”中保存某个结构体,这个结构体可以通过名字取出来,然后这个结构体可以转换成具体的格式。
所以第一步,我们使用std::<map>(std::string ,xxx ); 来做这个“池子”。
其次我们要能够通过一种格式将我们的结构体转换成当时它实际的样子,比如我一开始这个结构体存储了一个std::function<void()> fun = [](){std::cout<<"Hello"<<std::endl}; 那我用这个xxx结构体把这个函数封装完成之后,我要用的时候,可以通过xxx.cast< std::function<void()> >()再把xxx转换成这个类型,然后调用,所以这个结构体一定是要可以抹除类型信息的,我们称它为Any。
实现
好了,具体的Any实现,我直接放出来,具体可以参考《深入理解c++11 代码优化与企业级应用》。具体咱们不赘述了。
#ifndef ANY_H
#define ANY_H#include <memory>
#include <typeindex>
#include <exception>
#include <iostream>struct Any
{Any(void) : m_tpIndex(std::type_index(typeid(void))) {}Any(const Any& that) : m_ptr(that.Clone()), m_tpIndex(that.m_tpIndex) {}Any(Any && that) : m_ptr(std::move(that.m_ptr)), m_tpIndex(that.m_tpIndex) {}//创建智能指针时,对于一般的类型,通过std::decay来移除引用和cv符,从而获取原始类型template<typename U, class = typename std::enable_if<!std::is_same<typename std::decay<U>::type, Any>::value, U>::type>Any(U && value) : m_ptr(new Derived < typename std::decay<U>::type>(std::forward<U>(value))),m_tpIndex(std::type_index(typeid(typename std::decay<U>::type))){}bool IsNull() const { return !bool(m_ptr); }template<class U> bool Is() const{return m_tpIndex == std::type_index(typeid(U));}//将Any转换为实际的类型template<class U>U& AnyCast(){if (!Is<U>()){std::cout << "can not cast " << typeid(U).name() << " to " << m_tpIndex.name() << std::endl;throw std::logic_error{"bad cast"};}auto derived = dynamic_cast<Derived<U>*> (m_ptr.get());return derived->m_value;}Any& operator=(const Any& a){if (m_ptr == a.m_ptr)return *this;m_ptr = a.Clone();m_tpIndex = a.m_tpIndex;return *this;}Any& operator=(Any&& a){if (m_ptr == a.m_ptr)return *this;m_ptr = std::move(a.m_ptr);m_tpIndex = a.m_tpIndex;return *this;}private:struct Base;typedef std::unique_ptr<Base> BasePtr;struct Base{virtual ~Base() {}virtual BasePtr Clone() const = 0;};template<typename T>struct Derived : Base{template<typename U>Derived(U && value) : m_value(std::forward<U>(value)) { }BasePtr Clone() const{return BasePtr(new Derived<T>(m_value));}T m_value;};BasePtr Clone() const{if (m_ptr != nullptr)return m_ptr->Clone();return nullptr;}BasePtr m_ptr = nullptr;std::type_index m_tpIndex;
};#endif // ANY_H
然后接下来的“池子”就很简单了
struct functionWrappers{static auto GetList() -> std::map<std::string,Any>&;static std::map<std::string,Any>::iterator&& getWrapper(std::string);template <typename T>static T getFunc(std::string name){auto funcWarpper = getWrapper(name);if( funcWarpper == Qml::Register::functionWrappers::GetList().end() ){qCritical()<<"Can not find Worker warpper in Qml::Register::functionWrapper::GetList().end()"<<__PRETTY_FUNCTION__;return std::move(funcWarpper->second.AnyCast<T>());}auto func = funcWarpper->second.AnyCast<T>();if(!func){qCritical()<<"Can not convert Worker warpper function "<<__PRETTY_FUNCTION__;return func;}return func;};static Any&& getAnyFunc(const std::string& );
};
使用
使用方式如下:
//App
quick::Qml::Register::funcType addTaskCount_f("addTaskCount",[](){return quick::App::Worker::getInstance()->addTaskCount();});
//myLib
Qml::Register::functionWrappers::getFunc<std::function<void()>>("addTaskCount")();
很简单,不再赘述。
通过结合我的另一篇文章,如何静态化注册某些组件 -- 博客 , 我们可以实现程序启动之后即自动注册。
有问题欢迎讨论。
相关文章:

设计模式 - 如何在库和主程序之间互相调用数据和函数
背景:在项目开发过程中,难免碰到这种情况,当我们想要通过我们开发的库,调用主程序中的一些变量或者函数的时候,就会导致一些问题,因为在项目构建过程中,库都是不依赖于主程序编译的,…...

Redis面试题:1~2亿条数据需要缓存,请问如何设计这个存储案例
目录 前言 一、哈希取余分区 优点 缺点 二、一致性哈希算法分区 背景 步骤 ① 算法构建一致性哈希环 ② 服务器IP节点映射 ③ key落到服务器的落键规则 优点 ① 容错性 ② 扩展性 缺点 三、哈希槽分区 前言 单机单台100%不可能,肯定是分布式存储&am…...

程序员必备的软技能-《如何阅读一本书》
阅读很重要,我们真的会阅读吗? 这本书的初版是 1940年,时隔 80年,其内容仍然不过时。第一次读这本书时,给我最大的影响就是主题阅读,每次学习一个新理论、技术,都入手多本关于这项理论、技术的书…...

Java数据结构-栈、队列常用类(Stack、ArrayDeque、LinkedLList)
数据结构的三要素包括:逻辑结构、存储结构、数据的运算。逻辑结构描述的是数据之间的逻辑关系,分为线性结构(线性表(数组、链表)、栈、队列)和非线性结构(图、树、集合)。物理结构也…...

拯救了大批爬虫程序员,因为一个简单的神器
相信大家应该都写过爬虫,简单的爬虫只需要使用 requests 即可。遇到复杂的爬虫,就需要在程序里面加上请求头和参数信息。类似这种:我们一般的步骤是,先到浏览器的网络请求中找到我们需要的请求,然后将请求头和参数信息…...

2023年美赛C题Wordle预测问题三、四建模及Python代码详细讲解
更新时间:2023-2-19 16:30 相关链接 (1)2023年美赛C题Wordle预测问题一建模及Python代码详细讲解 (2)2023年美赛C题Wordle预测问题二建模及Python代码详细讲解 (3)2023年美赛C题Wordle预测问题三、四建模…...

相关性-回忆录(持续更新)
1.TODO方向 (1)数据增强:finetuning阶段需要大量人工标注样本,消耗时间和成本。用户点击数据作为弱监督学习,可以尝试图网络构建节点和边(query聚合); 使用展现未点击生成对抗网络进…...

(必备技能)使用Python实现屏幕截图
(必备技能)使用Python实现屏幕截图 文章目录 (必备技能)使用Python实现屏幕截图 一、序言二、环境配置 1、下载pyautogui包2、下载opencv-python包3、下载PyQt5包4、下载pypiwin32包 三、屏幕截屏源码与解析 1、使用pyautogui方法实现截屏2、使用PyQt方法实现截屏 a.获取窗口…...

「数据仓库」怎么选择现代数据仓库?
构建自己的数据仓库时要考虑的基本因素我们用过很多数据仓库。当我们的客户问我们,对于他们成长中的公司来说,最好的数据仓库是什么时,我们会根据他们的具体需求来考虑答案。通常,他们需要几乎实时的数据,价格低廉&…...

6.3 使用 Swagger 生成 Web API 文档
第6章 构建 RESTful 服务 6.1 RESTful 简介 6.2 构建 RESTful 应用接口 6.3 使用 Swagger 生成 Web API 文档 6.4 实战:实现 Web API 版本控制 6.3 使用 Swagger 生成 Web API 文档 高质量的 API 文档在系统开发的过程中非常重要。本节介绍什么是 Swaggerÿ…...

Day894.加锁规则的一些问题 -MySQL实战
加锁规则的一些问题 Hi,我是阿昌,今天学习记录的是关于加锁规则的一些问题的内容。 加锁规则,这个规则中,包含了两个“原则”、两个“优化”和一个“bug”: 原则 1:加锁的基本单位是 next-key lock。nex…...

【Flutter入门到进阶】Dart进阶篇---Dart异步编程
1 并行与并发的编程区别 1.1 并发与并行 1.1.1 说明 我们举个例子,如果有条高速公路 A 上面并排有 8 条车道,那么最大的并行车辆就是 8 辆此条高速公路 A 同时并排行走的车辆小于等于 8 辆的时候,车辆就可以并行运行。 CPU 也是这个原理,一个 CPU 相当于一个高速公路 A,核心数…...

点云配准方法原理(NDT、ICP)
配准是点云处理中的一个基础问题,众多学者此问题进行了广泛而深入的研究,也出现了一系列优秀成熟的算法,在三维建模、自动驾驶等领域发挥着重要的作用。 本文主要介绍粗配准NDT (Normal Distribution Transform) 与 精配准ICP (Iterative Cl…...

大规模 IoT 边缘容器集群管理的几种架构-0-边缘容器及架构简介
📚️Reference: IoT 边缘计算系列文章 什么是边缘容器? 边缘容器的概念 边缘容器是分散的计算资源,尽可能靠近最终用户或设备,以减少延迟、节省带宽并增强整体数字体验。 可以访问互联网的设备数量每天都在增加。有包括但不限于…...

代码随想录算法训练营第45天动态规划 背包基础 1 2、 416. 分割等和子集
文章目录01背包基础 (二维数组)思路递推公式初始化遍历顺序一维dp数组(滚动数组)一维数组的递推公式遍历顺序LeetCode 416. 分割等和子集思路总结01背包基础 (二维数组) 思路 根据动态规划五部进行分析&a…...

QT学习记录(六)类对象属性
类对象属性用来描述类对象的一些信息和当前的状态。类对象属性可以由类的编写者在编写类的时候定义,也可以由类的使用者在使用对象的时候定义。 由类的编写者定义 QPROPERTY()宏就是用来定义一个对象属性。 以第二行属性举例 QPROPERTY(bool enabled READ isEnabl…...

Spring Cloud Alibaba从搭建到源码完整进阶教程
微服务简介 Spring Cloud Alibaba 微服务简介 Nacos注册中心配置中心 Spring Cloud Nacos实战(一)- 下载和安装 Spring Cloud Nacos实战(二)- 服务提供者注册 Spring Cloud Nacos实战(三)- 服务消费者…...

Spring Cloud Nacos实战(一)- 下载和安装
Spring Cloud Alibaba Nacos下载和安装 Nacos介绍 Nacos(Naming Configuration Service) 是一个易于使用的动态服务发现、配置和服务管理平台,用于构建云原生应用程序 服务发现是微服务架构中的关键组件之一。Nacos 致力于帮助您发现…...

深入理解设备像素比
文章目录参考描述像素分辨率显示分辨率图像分辨率物理分辨率分辨率单位(仅部分)DPIPPI设备像素比设备物理像素设备独立像素设备像素比产生放大与缩小尾声参考 项目描述关于物理像素、逻辑像素(css像素)、分辨率、像素比的超详细讲…...

Revisiting Distributed Synchronous SGD 带有Back-up机制的分布式同步SGD方法 论文精读
论文链接:Revisiting Distributed Synchronous SGD ABS 本文介绍了用于分布式机器学习的同步和异步SGDSGDSGD,同时指出各自的缺点:stragglersstragglersstragglers和stalenessstalenessstaleness。 同时为了解决同步SGDSGDSGD存在straggle…...

shiro CVE-2020-13933
0x00 前言 同CVE-2020-1957,补充一下笔记,在CVE-2020-1957的基础上进行了绕过。 影响版本:Apache Shiro < 1.6.0 环境搭建参考:shiro CVE-2020-1957 0x01 漏洞复现 CVE-2020-13933中使用%3b绕过了shiro /*的检测方式&…...

斐波那契数列(递归+迭代)
目录什么是斐波那契数列递归写法使用递归写法的缺点迭代写法(效率高)什么是斐波那契数列 斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多斐波那契(Leonardo Fibonacci)以兔子繁殖为例…...

2022黑马Redis跟学笔记.实战篇(六)
2022黑马Redis跟学笔记.实战篇 六4.7.达人探店功能4.7.1.分享探店图文1. 达人探店-发布探店笔记2. 达人探店-查看探店笔记4.7.2.点赞功能4.7.3.基于List实现点赞用户列表TOP104.7.4.基于SortedSet实现点赞排行榜4.8.关注列表4.8.1.关注列表实现原理4.8.2.添加关注1. 好友关注-关…...

Linux-VMware常用设置(时间+网络)及网络连接激活失败解决方法-基础篇②
目录一、设置时间二、网络设置1. 激活网卡方法一:直接启动网卡(仅限当此)方法二:修改配置文件(永久)2. 将NAT模式改为桥接模式什么是是NAT模式?如何改为桥接模式?三、虚拟机网络连接…...

vue3学习总结1
一.vue3与vue2相比带来哪些变化?a.性能的提升(包括打包大小减少,初次渲染的速度加快,更新渲染速度加快,内存减少)b.源码的升级(响应式的原理发生了变化,由原来的defineProperty变成了…...

SpringBoot统一功能处理
一、统一用户登录权限验证 1.1Spring拦截器 实现拦截器需要以下两步: 1.创建自定义拦截器,实现 HandlerInterceptor 接⼝的 preHandle(执行具体方法之前的预处理)方法。 2.将⾃定义拦截器加⼊ WebMvcConfigurer 的 addIntercept…...

2022年3月电子学会Python等级考试试卷(五级)答案解析
目录 一、单选题(共25题,共50分) 二、判断题(共10题,共20分) 三、编程题(共3题,共30分) 青少年软件编程(Python)等级考试试卷(五级&#...

【C++】智能指针
目录 一、先来看一下什么是智能指针 二、 auto_ptr 1、C98版本 2、C11的auto_ptr 三、boost 库中的智能指针 1. scoped_ptr 2、shared_ptr(最好的智能指针) 四、C11中新提供的智能指针 unique_ptr shared_ptr std::shared_ptr的循环引用问题…...

Seata架构篇 - AT模式
AT 模式 概述 Seata AT 模式是一种非侵入式的分布式事务解决方案,Seata 在内部做了对数据库操作的代理层,我们使用 Seata AT 模式时,实际上用的是 Seata 自带的数据源代理 DataSourceProxy,Seata 在这层代理中加入了很多逻辑&am…...

加油站会员管理小程序实战开发教程12
我们上一篇介绍了会员数据源的开发,本节我们介绍一下会员注册功能。 首先呢梳理一下会员注册的业务逻辑,如果用户是首次登录,那他肯定还没有给我们的小程序提交任何的信息。那么我们就在我的页面给他显示一个注册的按钮,如果他已经注册过了,那么就正常显示会员的信息,他…...