跟我学c++高级篇——静态反射实现之二函数接口实现

一、函数反射

在实际的编程中,类和结构体应用最多,但也最难。这里先分析函数反射,类和结构体放到后面在分析。函数是什么?其实在PC看来就是一个地址,在编译顺看来就是一个符号(废话啊)。函数反射的应用也非常多,比如通过一个字符串来得到相关的API调用。这个在一些动态调用中,非常有用。
举一个简单例子,一般C/C++开发者都使用过函数指针,而函数指针就可以实现一些和函数反射有点类似的功能。一般函数指针在应用时,都是通过值来判断是什么来决定调用哪个函数指针,这些值其实就可以是字符串类型,这就和反射很像了。但函数指针的实现有点小问题在于,一个函数指针,其特征(名称、参数个数、参数类型)基本就定了下来。这就不如反射灵活了。
有的开发者可能说,可以使用变参、变参模板啊。非常棒。

二、实现方式

先实现一个初级版本,通过std:function来实现一个映射版本:

#include <iostream>
#include <unordered_map>
#include <functional>
#include <string>void getData(int t)
{std::cout << "call getData function,pars is:"<<t << std::endl;
}
void getContent(int t)
{std::cout << "call getContent function,pars is:"<<t << std::endl;
}std::unordered_map<std::string, std::function<void(int)>> umap;
void initMap()
{umap.emplace("getData",getData);umap.emplace("getContent",getContent);
}int main()
{initMap();int d = 100;if (umap.count("getData")){auto func = umap["getData"];func(d);}std::cout << "end" << std::endl;
}

代码很简单,但也很容易看明白。可前面提到过了,这种方法局限性还是有的,无法实现不同参数和参数类型的函数。这里有一种取巧的方法,可以用一个包含std::any的容器std::vector来组织一下,但这个就有一个问题,处理起来还是不方便。网上还有使用json字符串的,这个说法更麻烦了。如果本身反射就带着json处理还好,否则写个简单应用还需要带个json库,可就麻烦了。

三、利用模板万能函数

在前面分析过万能函数,可以在这个基础上实现一个动态处理函数反射的类:

#pragma once
#include <string>
#include <unordered_map>template <class T, class R, typename... Args>
class  CppDelegate
{R(T::* func_)(Args...);//万能函数typedef  decltype(func_) FUNC;//using FuncGloabl = R *(*)(Args...);public:CppDelegate() {}void AddFunction(T *t,const std::string & funcname, FUNC func ){umap_.emplace(funcname,func);umap1_.emplace(funcname,t);}template<typename ...Args>R StartFunc(const std::string& funcname,Args...args){auto type = this->getClass(funcname);auto func = this->getFunc(funcname);if (type != nullptr && func != nullptr){return (type->*func)(std::forward<Args>(args) ...);}return R();}
private:FUNC getFunc(const std::string &funcname){if (umap_.count(funcname) > 0){return umap_[funcname];}return nullptr;}T* getClass(const std::string& name){if (umap1_.count(name) > 0){return umap1_[name];}return nullptr;}private:std::unordered_map<std::string, FUNC> umap_;std::unordered_map<std::string, T*> umap1_;
};
class Data 
{
public:Data() {}~Data() = default;
public:int GetData(int a) { std::cout << "call getData function,a value:"<< a<< std::endl; return 0; };int GetContent(int a, int b) { std::cout << "call getContent function:" << std::endl; return 0; };
};
Data* d = new Data;
void testReflect()
{CppDelegate<Data, int,int> cpp;cpp.AddFunction(d,"getData",&Data::GetData);auto f = cpp.StartFunc("getData",100);std::cout << "f is:" << std::endl;
}
void testVoid() {return void();
}
int main()
{testVoid();//这个在VS中没有问题testReflect();return 0;
}

其实如果只是适配静态和全局函数,这个就非常简单了,这里需要适配类成员函数,所以比较麻烦。上面的代码还有几个问题:
1、不同类的不同函数如何存储在一个容器中
2、return R()如何处理void 等特殊情况
3、如何保证t*的生命周期
解决其来也有办法,只是怎么看更优雅一些。第一个可以在调用类上再抽象一层;第二个可以用概念或者SFINAE控制;第三个就比较麻烦了,不过,目前这样做也可以保证基本使用。

四、总结

不断的抽象实现可以保证设计上的依赖于抽象而不依赖于实现,也就使得代码更有普适性。但多层次的抽象导致的结果可能是代码阅读上的困难和维护上不方便。这个就是仁者见仁了,一般来说,对于库等升级比较正式而且不怎么频繁的项目可以尽量抽象,而对于应用层,抽象要适当。
不过在现在的环境下,就根据情况自己选择吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/214703.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Leetcode—228.汇总区间【简单】

2023每日刷题&#xff08;五十六&#xff09; Leetcode—228.汇总区间 解题思路 我们可以用双指针left 和 right找出每个区间的左右端点。 遍历数组&#xff0c;当right 1< n 且 nums[right1]nums[right]1 时&#xff0c;指针right向右移动&#xff0c;否则区间 [left, …

Mysql8和Oracle实际项目中递归查询树形结构

背景&#xff1a; 项目升级&#xff0c;引入MySQL数据库&#xff0c;之前一直用的是Oracle数据&#xff0c;在做用户登录单位维护的时候&#xff0c;需要返回该用户所属单位下的所有子单位。下边是模拟项目数据实践的过程。 数据准备&#xff1a; 准备一张单位表&#xff0c…

Flask存储在内存中的密钥被读取

局限性&#xff1a;查找的密钥具有特征码 一、Flask环境源码 1.Flask主文件main.py import os import uuid from flask import Flask, request, session, render_template from cat import catflag "" app Flask(__name__,static_url_path/,static_folderstatic …

51.Go操作kafka示例(kafka-go库)

文章目录 一、简介二、生产者三、消费者 代码地址&#xff1a;https://gitee.com/lymgoforIT/golang-trick/tree/master/31-kafka-go 一、简介 之前已经介绍过一个操作kafka的go库了&#xff0c;28.windows安装kafka&#xff0c;Go操作kafka示例&#xff08;sarama库&#xf…

二叉搜索树的最近公共祖先【数据结构】

二叉搜索树的最近公共祖先 题目描述 给定一棵二叉搜索树的先序遍历序列&#xff0c;要求你找出任意两结点的最近公共祖先结点&#xff08;简称 LCA&#xff09;。 输入 输入的第一行给出两个正整数&#xff1a;待查询的结点对数 M&#xff08;≤ 1 000&#xff09;和二叉搜索…

基于JavaWeb+SpringBoot+Vue在线拍卖系统的设计和实现

基于JavaWebSpringBootVue在线拍卖系统系统的设计和实现 源码获取入口Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 Lun文目录 摘 要 1 Abstract 1 1 系统概述 4 1.1 概述 4 1.2课题意义 4 1.3 主要内容 4 2 …

Git命令---绑定远程仓库

介绍 使用git命令绑定远程仓库 命令 git remote add origin https://gitee.com/x.xx.com/test.git

什么是多态

/*** Description 什么是多态*/ package com.oop;import com.oop.demo06.Person; import com.oop.demo06.Student;public class Application {public static void main(String[] args) {//一个对象的实际类型是确定的//new Student();//new Person();//可以指向的引用类型就不确…

C++新经典模板与泛型编程:策略技术中的算法策略

策略技术中的算法策略 在之前博客中funcsum()函数模板中&#xff0c;实现了对数组元素的求和运算。求和在这里可以看作一种算法&#xff0c;扩展一下思路&#xff0c;对数组元素求差、求乘积、求最大值和最小值等&#xff0c;都可以看作算法。而当前的funcsum()函数模板中&…

MySQL使用教程

数据构成了我们日益数字化的社会基础。想象一下&#xff0c;从移动应用和银行系统到搜索引擎&#xff0c;再到如 ChatGPT 这样的先进人工智能聊天机器人&#xff0c;这些工具若没有数据支撑&#xff0c;将寸步难行。你有没有好奇过这些海量数据都存放在哪里呢&#xff1f;答案正…

2023年团体程序设计天梯赛——总决赛题

F-L1-1 最好的文档 有一位软件工程师说过一句很有道理的话&#xff1a;“Good code is its own best documentation.”&#xff08;好代码本身就是最好的文档&#xff09;。本题就请你直接在屏幕上输出这句话。 输入格式&#xff1a; 本题没有输入。 输出格式&#xff1a; 在一…

读excel文件,借助openpyxl工具

读excel文件&#xff0c;借助openpyxl工具 import osimport requestsos.environ["http_proxy"] "http://127.0.0.1:7890" os.environ["https_proxy"] "http://127.0.0.1:7890"base_url "https://testnet.starscan.io/explore…

ALNS4VRPTWTF

文章概述 文章研究了城市物流背景下带有第三方转运设施的车辆路径问题。与经典的车辆路径问题不同&#xff0c;这些问题提供了将客户需求交付给第三方转运设施&#xff08;如城市集散中心&#xff09;的选择&#xff0c;并收取一定的费用。为了解决这些挑战&#xff0c;该研究…

LeetCode 279完全平方数 139单词拆分 卡码网 56携带矿石资源(多重背包) | 代码随想录25期训练营day45

动态规划算法6 LeetCode 279 完全平方数 2023.12.11 题目链接代码随想录讲解[链接] int numSquares(int n) {//1确定dp数组&#xff0c;其下标表示j的完全平方数的最少数量//3初始化&#xff0c;将dp[0]初始化为0&#xff0c;用于计算&#xff0c;其他值设为INT_MAX用于递推…

物料分类帐概览

原文地址&#xff1a;Overview: What is SAP Material Ledger? | SAP Blogs 物料分类账是收集物料主数据存储在物料主数据中的物料交易数据的工具。 物料分类帐使用此数据来计算价格以评估这些物料。 物料台账是实际成本核算的基础。它允许以多种货币对材料库存进行评估&am…

对象的生离死别

对象的生离死别 实验介绍 在构建一个类时&#xff0c;一般情况下需要编写构造函数、拷贝构造函数以及析构函数&#xff0c;这将直接影响程序的运行。而初始化列表是在调用构造函数时初始化参数的方式。 一个对象从实例化到销毁的历程&#xff1a; 知识点 内存分区构造函数exp…

java中什么是Spring Bean?

在Spring框架中&#xff0c;一个"Bean"是指由Spring IoC容器所管理的对象。这个对象可以是Java类的实例&#xff0c;也可以是引用其他对象的引用、集合或者是简单类型。Spring Bean是应用中由IoC容器负责创建、装配和管理的对象。 Spring中的Bean具有以下特征&#…

地牢手册-3d

Description 你进入了一个3D的宝藏地宫中探寻到了宝藏&#xff0c;你可以找到走出地宫的路带出宝藏&#xff0c;或者使用炉石空手回家。 地宫由立方体单位构成&#xff0c;立方体中不定会充满岩石。向上、下、前、后、左、右移动一个单位需要一分钟。你不能对角线移动并且地宫…

LabVIEW开发矿井排水监控系统

LabVIEW开发矿井排水监控系统 针对矿井水害对煤矿安全生产构成的威胁&#xff0c;设计了一种基于嵌入式PLC和LabVIEW的矿井排水监控系统。该系统结合了PLC的可靠控制与单片机的应用灵活性&#xff0c;有效克服了传统排水方法中的不足&#xff0c;如测量不准确、效率低下等问题…

react相关hooks(二)

不写性能优化的时候 const Child (props) > {console.log(child function is recalled)// count1改变时多次执行return (<div><h1>{ props.count2}</h1></div>) } function app () {const [count1.setCount1] useState(0)const [count2.setCount…