反射:其實就是通過類的名字(類名是string)來獲取類的實例。比如講類名寫在txt文件中,然後解析文本文件,拿到類名,進而那到類的實例。
在caffe中,在網絡配置文件中,寫入了各個層的名字,如:relu。其實,這些算子,在caffe源碼裏面都有對應的類實現,且類名一致,當然,caffe是通過protobuf來搞定配置文件的,配置文件的解析過程中,就會通過string的類名來拿到對應類的實例,這裏,其實就是運用了反射機制。
【反射機制實現步驟】
1. 建立一個工廠類,負責存儲類名與創建類實例的函數的map。
2.該工廠類提供註冊函數,方便新類將其類名與類實例的創建函數放入map中。
3.該工廠類提供依據類名獲取類實例的接口。
基於此,代碼實現如下:
#include <iostream>
#include <string>
#include <map>
using namespace std;
//定義一個函數指針,來表示創建類的函數
typedef void * (*pFunc)();
// 定義一個工廠類
class DevFactory {
public:
static DevFactory & getInstance() {
static DevFactory cf;
return cf;
}
void * GetClassInstanceByClassName(string class_name) {
auto iter = Dev_map.find(class_name);
if (iter == Dev_map.end()) {
cout << "can not create this " << class_name << " instance" << endl;
return nullptr;
}
return iter->second();
}
void Register(string class_name,pFunc creater){
Dev_map[class_name] = creater;
}
private:
DevFactory() {}
map<string, pFunc> Dev_map;
};
class CPU {
public:
CPU() {}
void print() {
cout << "this is CPU" << endl;
}
};
void * creatCPU() {
return new CPU;
}
class GPU {
public:
GPU() {}
void print() {
cout << "this is GPU" << endl;
}
};
void * creatGPU() {
return new GPU;
}
int main(int argc, int * argv[])
{
DevFactory dev = DevFactory::getInstance();
dev.Register("CPU", creatCPU);
dev.Register("GPU",creatGPU);
CPU * cpu = (CPU *)dev.GetClassInstanceByClassName("CPU");
cpu->print();
GPU *gpu = (GPU *)dev.GetClassInstanceByClassName("GPU");
gpu->print();
system("pause");
}
結果輸出如下:
【利用全局變量,實現類的自動註冊】
基於事實:全局變量的初始化實在main函數運行之前完成的。
所以,我們可以完成類的自動註冊,而不用在main函數內部進行手動註冊。
因此,需要一個輔助類,在該來的構造函數裏面,完成註冊。
代碼如下:
#include <iostream>
#include <string>
#include <map>
using namespace std;
//定義一個函數指針,來表示創建類的函數
typedef void * (*pFunc)();
// 定義一個工廠類
class DevFactory {
public:
static DevFactory * getInstance() {
static DevFactory *cf = new DevFactory;
return cf;
}
void * GetClassInstanceByClassName(string class_name) {
auto iter = Dev_map.find(class_name);
if (iter == Dev_map.end()) {
cout << "can not create 55 this " << class_name << " instance" << endl;
return nullptr;
}
return iter->second();
}
void Register(string class_name,pFunc creater){
cout << "register " << class_name << endl;
Dev_map[class_name] = creater;
}
private:
DevFactory() {}
map<string, pFunc> Dev_map;
};
class CPU {
public:
CPU() {}
void print() {
cout << "this is CPU" << endl;
}
};
void * creatCPU() {
return new CPU;
}
class GPU {
public:
GPU() {}
void print() {
cout << "this is GPU" << endl;
}
};
void * creatGPU() {
return new GPU;
}
class CreaterDev {
public:
CreaterDev(string class_name, pFunc creater) {
DevFactory *dev = DevFactory::getInstance();
dev->Register(class_name, creater);
}
};
CreaterDev g_dev_cpu("CPU",creatCPU); //利用全局變量的初始化,自動完成註冊
CreaterDev g_dev_gpu("GPU", creatGPU);
int main(int argc, int * argv[])
{
DevFactory *dev = DevFactory::getInstance();
//dev.Register("CPU", creatCPU);
//dev.Register("GPU",creatGPU);
CPU * cpu = (CPU *)dev->GetClassInstanceByClassName("CPU"); //直接依據類名獲取類的實例
cpu->print();
GPU *gpu = (GPU *)dev->GetClassInstanceByClassName("GPU");
gpu->print();
system("pause");
}
結果如下:
可以看到,利用全局變量的初始化,可以方便實現新增類的自動註冊。
但是也可以看出一個問題,那就是每新增一個類,都需要新增一個create函數與一個全局變量,這些代碼是完全一樣的,因此,我們可以編寫一個宏,來同時實現create函數的定義與全局變量的定義。
#define REGISTER(className) \
className* objectCreator##className(){ \
return new className; \
} \
CreaterDev g_creatorRegister##className( \
#className,(pFunc)objectCreator##className)
利用這個宏,來實現類的自動註冊。
全部代碼如下:
#include <iostream>
#include <string>
#include <map>
using namespace std;
//定義一個函數指針,來表示創建類的函數
typedef void * (*pFunc)();
// 定義一個工廠類
class DevFactory {
public:
static DevFactory * getInstance() {
static DevFactory *cf = new DevFactory;
return cf;
}
void * GetClassInstanceByClassName(string class_name) {
auto iter = Dev_map.find(class_name);
if (iter == Dev_map.end()) {
cout << "can not create 55 this " << class_name << " instance" << endl;
return nullptr;
}
return iter->second();
}
void Register(string class_name,pFunc creater){
cout << "register " << class_name << endl;
Dev_map[class_name] = creater;
}
private:
DevFactory() {}
map<string, pFunc> Dev_map;
};
class CreaterDev {
public:
CreaterDev(string class_name, pFunc creater) {
DevFactory *dev = DevFactory::getInstance();
dev->Register(class_name, creater);
}
};
#define REGISTER(className) \
className* objectCreator##className(){ \
return new className; \
} \
CreaterDev g_creatorRegister##className( \
#className,(pFunc)objectCreator##className)
class CPU {
public:
CPU() {}
void print() {
cout << "this is CPU" << endl;
}
};
REGISTER(CPU);
class GPU {
public:
GPU() {}
void print() {
cout << "this is GPU" << endl;
}
};
REGISTER(GPU);
int main(int argc, int * argv[])
{
DevFactory *dev = DevFactory::getInstance();
//dev.Register("CPU", creatCPU);
//dev.Register("GPU",creatGPU);
CPU * cpu = (CPU *)dev->GetClassInstanceByClassName("CPU"); //直接依據類名獲取類的實例
cpu->print();
GPU *gpu = (GPU *)dev->GetClassInstanceByClassName("GPU");
gpu->print();
system("pause");
}
結果輸出如下:
可以看出,利用全局變量的初始化,可以實現類在mian之前完成好註冊。當然,也有其他的方式來實現這種機制,只是這種實現方法簡單,優雅。