C++ uses macro definition to realize reflection

Reflection is a powerful function of java. We often have " the need to create an instance of the class according to the class name ", that is to say, the class instance can be obtained through a string. This requirement also exists in C++. Let me share the idea of ​​realizing similar functions through macro definition in C++.

1. Create a common base class.

We need to 反射create a common base class for the classes being " ". We create the following Basebase class, work()the main method is to print out different content to distinguish it from other subclasses.

Base class Base.h

#pragma once
#include <stdio.h>
#include "Factory.h"

class Base {
    
    
public:
	Base() {
    
     printf("base class is created\n"); };
	virtual ~Base() {
    
    };
	virtual void work() {
    
     printf("base class is work\n"); }
};

2. Create a factory class

Instead of creating subclasses, after we have a base class, we can implement a factory class first Factory. The core methods are registerClass()and registerClass(), the former registers the class with the Factory, and the latter implements the creation of class instances through strings. classPool saves the registered class name and its corresponding constructor.

Factory.h

#pragma once

#include <vector>
#include <utility>

class Base;
typedef Base* (*CREATER)();

class Factory
{
    
    
public:
	static Factory* getInstance();
	/// <summary>
	/// 注册类到 Factory 中
	/// </summary>
	/// <param name="className">类名称</param>
	/// <param name="creater">该类对应的静态构造方法</param>
	void registerClass(const char *className, CREATER creater);

	/// <summary>
	/// 通过类名创建类实例
	/// </summary>
	/// <param name="className">类的名称</param>
	/// <returns>该类的一个实例</returns>
	Base* create(const char *className);

private:
	// date
	static Factory* sThis;
	// 存储 <类名,类构造方法> 的 key-value 对
	std::vector<std::pair<const char*, CREATER> > classPool;

	// method
	Factory() {
    
    };
	~Factory() {
    
    };
};

Factory.cpp

#include "Factory.h"

Factory* Factory::sThis = nullptr;

Factory* Factory::getInstance() {
    
    
	if (!sThis) {
    
    
		sThis = new Factory();
	}
	return sThis;
}

void Factory::registerClass(const char *className, CREATER creator) {
    
    
	for (auto it = classPool.begin(); it != classPool.end(); it++) {
    
    
		if (strcmp(it->first, className) == 0) {
    
    
			printf("%s is already regitered\n", className);
			return;
		}
	}
	printf("%s is regitered\n", className);
	classPool.emplace_back(std::make_pair(className, creator));
}

Base* Factory::create(const char* className) {
    
    
	for (auto it = classPool.begin(); it != classPool.end(); it++) {
    
    
		if (strcmp(it->first, className) == 0) {
    
    
			CREATER creater = it->second;
			return (*creater)();
		}
	}
	printf("%s has not been registered\n", className);
	return nullptr;
}

3. Improve the Base class

With the Factory class, it seems like you're done. But there are still some problems. For example, when calling registerClass()the method , a static constructor is required, and each subclass needs to be defined once. It seems a bit troublesome. Consider defining it in the base class, so that you don’t need to write it complicatedly in the subclass , it's time for our macro definition to appear. The following is the improved Basebase class.

#pragma once
#include <stdio.h>
#include "Factory.h"

class Base {
    
    
public:
	Base() {
    
     printf("base class is created\n"); };
	virtual ~Base() {
    
    };
	virtual void work() {
    
     printf("base class is work\n"); }
};

#define REGISTER_CLASS(_class) \
static Base* create() {
      
       \
	return new _class(); \
} \
void register##_class() {
      
       \
	Factory::getInstance()->registerClass(#_class, create); \
}

#define DO_REGISTER_CLASS(_class) \
	extern void register##_class(); \
	register##_class();

REGISTER_CLASSThis macro defines two methods, create()which are the static constructor methods we defined for subclasses. register##_class()Convert the class name to a string and register it with the Factory. After each subclass calls this macro, these two methods can be automatically defined.

DO_REGISTER_CLASSThis macro is the real registration method, which needs to be called in main() or other special places in the project to register the class.

4. Look at the subclasses

Child1

Child1.h

#pragma once
#include "Base.h"
class Child1 : public Base
{
    
    
public:
	Child1();
	void work();
};

Child1.cpp

#include "Child1.h"

REGISTER_CLASS(Child1);

Child1::Child1() {
    
    
	printf("child1 class is created\n");
}

void Child1::work() {
    
    
	printf("child1 class is work\n");
}

Called in Child1.cpp REGISTER_CLASS(Child1);Defines the aforementioned two methods.
Similarly, when Child2 is defined, it is no longer displayed.

5. main method and test


#include <iostream>
#include "Base.h"

int main()
{
    
    
    // 向 factory 注册类,调用定义在 Base 中的宏
    DO_REGISTER_CLASS(Child1);
    DO_REGISTER_CLASS(Child2);
    DO_REGISTER_CLASS(Child2);

    // 根据类名创建 Child1
    Base* mBase = Factory::getInstance()->create("Child1");
    mBase->work();
    // 根据类名创建 Child2
    mBase = Factory::getInstance()->create("Child2");
    mBase->work();
    // 根据类名,尝试创建 Child3,但并不存在 Child3
    Factory::getInstance()->create("Child3");
}

The following is the running result. It can be seen that both Child1 and Child2 can be successfully created through the class name, but Child3 cannot be successfully created because it is not registered.
insert image description here
For detailed code, please refer to the following. ClassFactory

Guess you like

Origin blog.csdn.net/hejnhong/article/details/128708816