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

一、函数反射

在实际的编程中,类和结构体应用最多,但也最难。这里先分析函数反射,类和结构体放到后面在分析。函数是什么?其实在PC看来就是一个地址,在编译顺看来就是一个符号(废话啊)。函数反射的应用也非常多,比如通过一个字符串来得到相关的API调用。这个在一些动态调用中,非常有用。

举一个简单例子,一般C/C++开发者都使用过函数指针,而函数指针就可以实现一些和函数反射有点类似的功能。一般函数指针在应用时,都是通过值来判断是什么来决定调用哪个函数指针,这些值其实就可以是字符串类型,这就和反射很像了。但函数指针的实现有点小问题在于,一个函数指针,其特征(名称、参数个数、参数类型)基本就定了下来。这就不如反射灵活了。

有的开发者可能说,可以使用变参、变参模板啊。非常棒。

二、实现方式

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

c 复制代码
#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库,可就麻烦了。

三、利用模板万能函数

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

c 复制代码
#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控制;第三个就比较麻烦了,不过,目前这样做也可以保证基本使用。

四、总结

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

不过在现在的环境下,就根据情况自己选择吧。

相关推荐
咖啡里的茶i14 分钟前
Vehicle友元Date多态Sedan和Truck
c++
海绵波波10720 分钟前
Webserver(4.9)本地套接字的通信
c++
@小博的博客26 分钟前
C++初阶学习第十弹——深入讲解vector的迭代器失效
数据结构·c++·学习
爱吃喵的鲤鱼1 小时前
linux进程的状态之环境变量
linux·运维·服务器·开发语言·c++
7年老菜鸡2 小时前
策略模式(C++)三分钟读懂
c++·qt·策略模式
Ni-Guvara2 小时前
函数对象笔记
c++·算法
似霰2 小时前
安卓智能指针sp、wp、RefBase浅析
android·c++·binder
芊寻(嵌入式)2 小时前
C转C++学习笔记--基础知识摘录总结
开发语言·c++·笔记·学习
獨枭2 小时前
C++ 项目中使用 .dll 和 .def 文件的操作指南
c++
霁月风2 小时前
设计模式——观察者模式
c++·观察者模式·设计模式