C++通过类型的字符串名称获取该类型的对象--注册机制
Introduction
本来是想总结一下关于C++反射的知识,但是在查资料的过程中,发现自己认知中的反射不过是真正的反射的很小的一个应用,所以这里修改了自己的标题,以使其具体地反映其功能。
从字符串获取类对象的思路
为了从字符串获取一个类的对象,一个可行的思路是建立
字符串与类的一个映射关系
,为了方便操作,我们可以把这种映射关系放在一个map结构中:
std::map<std::string, ?>
。一种理想化的映射是我们直接从字符串获取类型名称
std::map<std::string, CLASS> string_class
,并在后续通过类型名称来构造对象
string_class[str] object
。但是在C++对反射尚没有很好的支持的时候,这种方法貌似也很少有人去尝试。
另外一个常见的思路就是将字符串与可以获得类对象的函数进行映射
std::map<std::string, std::function<void*()>> string_objcreator_map
,如此,我们便可以通过字符串获取可以构造所需要的类对象的函数,实现从字符串到类对象的获取。
static std::shared_ptr<void> classFromName(std::string str)
if(class_map.find(str)==class_map.end()) return nullptr;
return class_map[str]();
}
一个例子
#include <iostream>
#include <map>
#include <utility>
#include <string>
#include <functional>
#include <memory>
class Product{
public:
using Ptr=std::shared_ptr<Product>;
virtual void makeProduct()=0;
class ProductA : public Product {
public:
virtual void makeProduct()
std::cout<<"Make Product A!"<<std::endl;
static std::map<std::string, std::function<std::shared_ptr<void> ()>> class_map;
static std::shared_ptr<void> classFromName(std::string str)
if(class_map.find(str)==class_map.end()) return nullptr;
return class_map[str]();
class Register{
public:
Register(std::string str, std::function<std::shared_ptr<void>()> func)
class_map.insert(std::make_pair(str, func));
#define REGISTER(classname) \
class Register##classname { \
public: \
static std::shared_ptr<classname> instance(){ \
return std::make_shared<classname>(); \
auto temp##classname = Register(std::string(#classname), Register##classname::instance);
REGISTER(ProductA)
int main()
std::shared_ptr<ProductA> temp1=std::static_pointer_cast<ProductA>(classFromName("ProductA"));
temp1->makeProduct();
我们需要在main函数之前将字符串和函数之间的关系准备好,即在main函数之前进行注册。在这里,为了进行注册,利用了全局变量的构造函数在main函数之前调用这个特点。在 C++反射机制的实现 ,还有更多的在main函数之前执行一些动作的方式。
另外这里REGISTER宏中并不是直接定义构造对象的函数,而是定义了一个类,以从中获取对象instance,这样在注册多个类的时候,instance函数不至于冲突。
工厂注册
上面只有一个类型,我们其实没有必要根据字符串来获取对象,可以直接构造。而这里的这种注册机制的真正的应用场景更多是结合了工厂模式。而上面例子里的宏也主要是为了工厂模式中众多的重复代码准备的。
想象一下在有些场景下,有多种类型的对象可以创建,而我们需要做的就是根据需要选择合适的类型进行对象构造。比如,A和B程序员分别写了一个工厂用于A和B产品的生产,但是老板没有拿定主意用哪一个。于是,我们不能直接在代码里面写明用的哪种,而是留一个可选的结构来根据条件来实现。
// reflection_template.h
#ifndef _REFLECTION_TEMPLATE_H_
#define _REFLECTION_TEMPLATE_H_
#include <iostream>
#include <map>
#include <utility>
#include <string>
#include <functional>
#include <memory>
class Product{
public:
using Ptr=std::shared_ptr<Product>;
virtual void makeProduct()=0;
class ProductA : public Product {
public:
virtual void makeProduct()
std::cout<<"Make Product A!"<<std::endl;
class ProductB : public Product {
public:
virtual void makeProduct()
std::cout<<"Make Product B!"<<std::endl;
class Factory{
public:
using Ptr=std::shared_ptr<Factory>;
virtual Product::Ptr createProduct()=0;
class ProductAFactory : public Factory
public:
virtual Product::Ptr createProduct(/* args */)
return std::make_shared<ProductA>();
class ProductBFactory : public Factory {
public:
virtual Product::Ptr createProduct(){
return std::make_shared<ProductB>();
static std::map<std::string, std::function<std::shared_ptr<void> ()>> class_map;
static std::shared_ptr<void> classFromName(std::string str)
if(class_map.find(str)==class_map.end()) return nullptr;
return class_map[str]();
class Register{
public:
Register(std::string str, std::function<std::shared_ptr<void>()> func)
class_map.insert(std::make_pair(str, func));
#define REGISTER(classname) \
class Register##classname { \
public: \
static std::shared_ptr<classname> instance(){ \
return std::make_shared<classname>(); \
auto temp##classname= Register(std::string(#classname), Register##classname::instance);
REGISTER(ProductAFactory)
REGISTER(ProductBFactory)
#endif
#include "reflection_template.h"
int main()