跟我學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.pswp.cn/news/214703.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/214703.shtml
英文地址,請注明出處:http://en.pswp.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…