首页 > C++ 通过模版工厂实现 简单反射机制

C++ 通过模版工厂实现 简单反射机制

前言

我们知道Java/Python这种语言能够很好得 支持反射。反射机制 就是一种用户输入的字符串到对应实现方法的映射,比如http接口中 用户传入了url,我们需要调用该url对应的方法/函数对象 从而做出对应的操作。

而C++ 并没有友好得支持这样的操作,而最近工作中需要通过C++实现http接口,这个过程想要代码实现得优雅一些,就需要实现这样的功能。

最终的实现结果就是:

 int main() { Initial();JudgeName jm;auto res = jm.Judge("Zhang");res->ShowName();return 0;
}

我们只需要通过输入一个字符串,就能得到一个字符串对应的类的对象,从而调用该对象对应的方法。

实现过程

  1. 实现模版工厂

    反射机制本身是依赖工厂模式实现的,也就是提供一个创建函数对象的工厂,能够创建各种类型的函数对象。

    这里面的各种类型 – 其实就需要实现一个通用的模版工厂来达到创建各种类型的目的。

    // 工厂类模版,创建各种类型的类对象
    template <class OperationType_t>
    class OperationFactory { 
    public:// 创建一个工厂单例,只会有一个对象工厂static OperationFactory<OperationType_t>& Instance() { static OperationFactory<OperationType_t> instance;return instance;}private:// We don't allow to constructor, copy constructor and align constructorOperationFactory() = default;~OperationFactory() = default;OperationFactory(const OperationFactory&) = delete;const OperationFactory& operator= (const OperationFactory&) = delete;};
    
  2. 实现类的对象到类名的映射。

    我们实现了能够创建所有类对象的函数工厂,但是还需要将创建好的对象和他们的类名字对应起来,做一个映射。

    在模版工厂类中补充映射逻辑:

    // Factory class template
    template <class OperationType_t>
    class OperationFactory { 
    public:// Single pattern of the factorystatic OperationFactory<OperationType_t>& Instance() { static OperationFactory<OperationType_t> instance;return instance;}// 类名 和 类对象的映射void RegisterOperation(const std::string& op_name,OperationRegister<OperationType_t>* reg) { operationRegister[op_name] = reg;}// 获取指定类名字 对应的 对象// 也就是从映射map中查找OperationType_t* GetOperation(const std::string& op_name) { if (operationRegister.find(op_name) != operationRegister.end()) { return operationRegister[op_name]->CreateOperation(op_name);}return nullptr;}private:// We don't allow to constructor, copy constructor and align constructorOperationFactory() = default;~OperationFactory() = default;OperationFactory(const OperationFactory&) = delete;const OperationFactory& operator= (const OperationFactory&) = delete;// 类名字 和 类对象之间的映射std::map<std::string, OperationRegister<OperationType_t>* > operationRegister;
    };
    
  3. 实现一个注册类

    此时 我们通过这个注册类的入口 将类名 和类的对象完整映射起来。

    template <class OperationType_t>
    class OperationRegister { 
    public:virtual OperationType_t* CreateOperation(const std::string& op_name) = 0;protected:OperationRegister() { }virtual ~OperationRegister() { }
    };// 注册一个以基类的子类对象。
    template <class OperationType_t, class OperationImpl_t>
    class OperationImplRegister : public OperationRegister<OperationType_t> { 
    public:// 获取一个基类的工厂explicit OperationImplRegister(const std::string& op_name) { OperationFactory<OperationType_t>::Instance().RegisterOperation(op_name, this);}// 获取这个基类对应的子类对象OperationType_t* CreateOperation(const std::string& op_name) { return new OperationImpl_t(op_name);}
    };
    

应用

有了上面的模版工厂 以及 注册类名字 和 类的映射模版,接下来我们看看怎么使用:

我们实现了如下一个父类 以及 几个父类对应的子类实现

class Father { 
public:virtual std::string Name() = 0;virtual void ShowName() = 0;
};class Wang : public Father{ 
public:Wang(const std::string& name) : name_(name){ }std::string Name() override {  return name_; }void ShowName() override {  cout << "Name: " << name_ << endl; }private:std::string name_;
};class Zhang : public Father { 
public:Zhang(const std::string& name) : name_(name) { }std::string Name() override {  return name_; }void ShowName() override {  cout << "Name: " << name_ << endl; }private:std::string name_;
};class Li : public Father { 
public:Li(const std::string& name) : name_(name) { }std::string Name() override {  return name_; }void ShowName() override {  cout << "Name: " << name_ << endl; }private:std::string name_;
};

接下来需要统一注册一下这几个子类 的 名字 以及他们的对象:

// Construct the string name with there object's map
void Initial() { static bool init = false;if (init == false) { static OperationImplRegister<Father, Wang> wang("Wang");static OperationImplRegister<Father, Zhang> zhang("Zhang");static OperationImplRegister<Father, Li> li("Li");init = true;}
}

最后就是一个通过字符串 获取对象

class JudgeName { 
public:JudgeName(){ }Father* Judge(const std::string& name) { OperationFactory<Father>& fac = OperationFactory<Father>::Instance();return fac.GetOperation(name);}
};

这个类的函数中,我们用户只需要传入一个字符串,就能够得到这个字符串的类的对象,从而进行后续的对象方法相关的操作。

能够极大得简化我们的if-else if 这样的分支代码,同时提升了代码的可扩展性。用户只需要继续补充想要使用的类,并将这个类的名字和对象注册到映射map中就好了,后续只需要使用类名就能够创建出这个类的对象。

完整代码实现

#include 
#include using namespace std;// Register the operation
// The 'OperationTyple_t' is the abstract class
template <class OperationType_t>
class OperationRegister { 
public:virtual OperationType_t* CreateOperation(const std::string& op_name) = 0;protected:OperationRegister() { }virtual ~OperationRegister() { }
};// Factory class template
template <class OperationType_t>
class OperationFactory { 
public:// Single pattern of the factorystatic OperationFactory<OperationType_t>& Instance() { static OperationFactory<OperationType_t> instance;return instance;}void RegisterOperation(const std::string& op_name,OperationRegister<OperationType_t>* reg) { operationRegister[op_name] = reg;}OperationType_t* GetOperation(const std::string& op_name) { if (operationRegister.find(op_name) != operationRegister.end()) { return operationRegister[op_name]->CreateOperation(op_name);}return nullptr;}private:// We don't allow to constructor, copy constructor and align constructorOperationFactory() = default;~OperationFactory() = default;OperationFactory(const OperationFactory&) = delete;const OperationFactory& operator= (const OperationFactory&) = delete;std::map<std::string, OperationRegister<OperationType_t>* > operationRegister;
};// An template class to create the detail Operation
template <class OperationType_t, class OperationImpl_t>
class OperationImplRegister : public OperationRegister<OperationType_t> { 
public:explicit OperationImplRegister(const std::string& op_name) { OperationFactory<OperationType_t>::Instance().RegisterOperation(op_name, this);}OperationType_t* CreateOperation(const std::string& op_name) { return new OperationImpl_t(op_name);}
};class Father { 
public:virtual std::string Name() = 0;virtual void ShowName() = 0;
};class Wang : public Father{ 
public:Wang(const std::string& name) : name_(name){ }std::string Name() override {  return name_; }void ShowName() override {  cout << "Name: " << name_ << endl; }private:std::string name_;
};class Zhang : public Father { 
public:Zhang(const std::string& name) : name_(name) { }std::string Name() override {  return name_; }void ShowName() override {  cout << "Name: " << name_ << endl; }private:std::string name_;
};class Li : public Father { 
public:Li(const std::string& name) : name_(name) { }std::string Name() override {  return name_; }void ShowName() override {  cout << "Name: " << name_ << endl; }private:std::string name_;
};// Construct the string name with there object's map
void Initial() { static bool init = false;if (init == false) { static OperationImplRegister<Father, Wang> wang("Wang");static OperationImplRegister<Father, Zhang> zhang("Zhang");static OperationImplRegister<Father, Li> li("Li");init = true;}
}class JudgeName { 
public:JudgeName(){ }Father* Judge(const std::string& name) { OperationFactory<Father>& fac = OperationFactory<Father>::Instance();return fac.GetOperation(name);}
};int main() { Initial();JudgeName jm;auto res = jm.Judge("Zhang");res->ShowName();return 0;
}

更多相关:

  • 引言 在这个-SLAM建图和导航仿真实例-项目中,主要分为三个部分,分别是 (一)模型构建(二)根据已知地图进行定位和导航(三)使用RTAB-MAP进行建图和导航 该项目的slam_bot已经上传我的Github。 这是第三部分,完成效果如下 图1 建图和导航 三、使用RTAB-Map进行建图和导航 1. rtab...

  • 引言 在这个-SLAM建图和导航仿真实例-项目中,主要分为三个部分,分别是 (一)模型构建(二)根据已知地图进行定位和导航(三)使用RTAB-MAP进行建图和导航 该项目的slam_bot已经上传我的Github。 由于之前的虚拟机性能限制,我在这个项目中使用了新的ubantu 16.04环境,虚拟机配置 内存 8GCPU...

  • [{name:1},{name:2}].forEach((v,i,ar) => {console.log(v,i,ar)});//基础遍历[{name:1},{name:2}].map((v) => v.name);//[1,2]返回对象数组中指定字段值的一位数组(不改变原始数组)[{name:1},{name:2},{name:3}...

  • 体验内容 使用gmapping方法利用turtlebot底盘移动信息和激光雷达数据进行建图。 1. 安装一些依赖包 sudo apt-get install ros-melodic-move-base* sudo apt-get install ros-melodic-map-server* sudo apt-get insta...

  • 经过长期探索,发现一个不需要手动设置线程休眠时间(e.g. std::this_thread::sleep_for(std::chrono::microseconds(1)))的代码: Github: https://github.com/log4cplus/ThreadPool #ifndef THREAD_POOL_H_7e...

  • nth_element(first,nth,last) first,last 第一个和最后一个迭代器,也可以直接用数组的位置。  nth,要定位的第nn 个元素,能对它进行随机访问. 将第n_thn_th 元素放到它该放的位置上,左边元素都小于它,右边元素都大于它. 测试代码: http://www.cplusplus.com...

  • c/c++老版本的rand()存在一定的问题,在转换rand随机数的范围,类型或者分布时,常常会引入非随机性。 定义在 中的随机数库通过一组协作类来解决这类问题:随机数引擎 和 随机数分布类 一个给定的随机数发生器一直会生成相同的随机数序列。一个函数如果定义了局部的随机数发生器,应该将(引擎和分布对象)定义为 st...

  • jsoncpp 是一个C++ 语言实现的json库,非常方便得支持C++得各种数据类型到json 以及 json到各种数据类型的转化。 一个json 类型的数据如下: {"code" : 10.01,"files" : "","msg" : "","uploadid" : "UP000000" } 这种数据类型方便我们人阅读以...

  • 问题如下: 已知一组数(其中有重复元素),求这组数可以组成的所有子集中,子 集中的各个元素和为整数target的子集,结果中无重复的子集。 例如: nums[] = [10, 1, 2, 7, 6, 1, 5], target = 8 结果为: [[1, 7], [1, 2, 5], [2, 6], [1, 1, 6]] 同样之前有...

  • importjava.security.SecureRandom;importjavax.crypto.Cipher;importjavax.crypto.SecretKey;importjavax.crypto.SecretKeyFactory;importjavax.crypto.spec.DESKeySpec;//结果与DES算...

  • 题目:替换空格 请实现一个函数,把字符串 s 中的每个空格替换成"%20"。 输入:s = "We are happy." 输出:"We%20are%20happy." 限制: 0 <= s 的长度 <= 10000 解题: 时间复杂度:O(n) 空间复杂度:O(n) class Solution { public:s...

  • 在C++11标准库中,string.h已经添加了to_string方法,方便从其他类型(如整形)快速转换成字面值。 例如: for (size_t i = 0; i < texArrSize; i++)RTX_Shader.SetInt(string("TexArr[") + to_string(i) + "]", 7 + i);...

  • Ubuntu 14.04安装并升级之后,变成楷体字体非常难看,我昨天搞了一晚上,终于理了个头绪,这里整理一下。 经过网上调研,大家的一致看法是,使用开源字体库文泉驿的微黑字体效果比较理想,甚至效果不输windows平台的雅黑字体。下面我打算微黑来美化Ubuntu 14.04. 1.安装文泉驿微黑字体库 sudo aptitude...

  • 使用string时发现了一些坑。 我们知道stl 容器并不是线程安全的,所以在使用它们的过程中往往需要一些同步机制来保证并发场景下的同步更新。 应该踩的坑还是一个不拉的踩了进去,所以还是记录一下吧。 string作为一个容器,随着我们的append 或者 针对string的+ 操作都会让string内部的数据域动态增加,而动态增加的...