c++ 反射類型

來自: html

實現代碼===

//
// Created by lizhen on 2017/9/29.
//

#ifndef BOOST_ALL_CALLBACKFUNCTION_H
#define BOOST_ALL_CALLBACKFUNCTION_H

#include <iostream>
#include <map>
#include <string>
using namespace std;

typedef void* (*PTRCreateObject)(void);//定義一個函數指針類型,用於指向建立類實例的回調函數

class ClassFactory{
private:
    map<string,PTRCreateObject >map_classMap;
    ClassFactory(){};
public:
    void *getClassByName(string className);
    void registClass(string name,PTRCreateObject method);
    static ClassFactory& getInstance();
};

ClassFactory& ClassFactory::getInstance() {
    static ClassFactory sLo_factory;
    return sLo_factory;
}

void* ClassFactory::getClassByName(string className) {
    map<string,PTRCreateObject >::const_iterator iter;
    iter = map_classMap.find(className);
    if(iter==map_classMap.end()){
        return NULL;
    }else{
        return iter->second();
    }
}

void ClassFactory::registClass(string name, PTRCreateObject method) {
    map_classMap.insert(pair<string,PTRCreateObject>(name,method));
}

class RegisterAction{
public:
    RegisterAction(string className,PTRCreateObject ptrCreateFn){
        ClassFactory::getInstance().registClass(className,ptrCreateFn);
    }
};

//================================ClassA
class TestClassA{
public:
    void m_print(){
        cout<<"hello TestClassA"<<endl;
    }
};
TestClassA* createObjTestClassA(){
        return new TestClassA();
}
RegisterAction g_create_RegisterTestClassA("TestClassA",(PTRCreateObject)createObjTestClassA());
//<-----

//================================ClassB
//test class B
class TestClassB{
public:
    void m_print(){
        cout<<"hello TestClassB"<<endl;
    }
};
TestClassB* createObjTestClassB(){
        return new TestClassB();
}
RegisterAction g_create_RegisterTestClassB("TestClassB",(PTRCreateObject)createObjTestClassB());
//<---


#define REGISTER(className)                                     \
    className* objectCreater##className(){                      \
        return new className();                                 \
    }                                                           \
    RegisterAction g_createrRegister##className(                \
        #className,(PTRCreateObject)objectCreater##className)

class classC{
public:
    void m_print(){
        std::cout<<"hello classC"<<std::endl;
    }
};

REGISTER(classC);
class Reflact{
public:
    void run(){
        TestClassA* ptrObjA = (TestClassA*)ClassFactory::getInstance().getClassByName("TestClass");
        ptrObjA->m_print();
        //REGISTER(classC);

        classC* ptr_c = (classC*)ClassFactory::getInstance().getClassByName("classC");
        ptr_c->m_print();
    }
};

#endif //BOOST_ALL_CALLBACKFUNCTION_H

=====ios

轉自http://blog.csdn.net/brighlee/article/details/72885219c++


 

前言

反射的概念:git

指程序在運行時,訪問、檢測和修改它自己狀態或行爲的一種能力。wikipediagithub

簡單的來講,就是一種自描述和自控制的能力。若是聯想到鏡子,就能夠很好的理解,你能經過鏡子看到本身,包括本身的動做,本身的外表。惟一不一樣的地方是,計算機語言的反射能力還包含對看到的本身採起措施。編程

反射的做用json

在計算機編程語言中,反射機制能夠用來:設計模式

  • 獲取類型的信息,包括屬性、方法
  • 動態調用方法
  • 動態構造對象
  • 從程序集中得到類型

反射的缺點網絡

  • 性能:反射能夠理解成是一種解釋操做,這個過程老是要慢於直接調用的。固然,性能問題的程度是能夠控制的,若是程序在不多涉及的地方使用,性能將不會是一個問題。
  • 反射模糊了程序內部實際發生的事情,會比直接代碼更加複雜。

缺點不能掩飾其優勢,針對不一樣的場景使用合理的技術纔是最高境界。app

反射的使用場景

  • 序列化(Serialization)和數據綁定(Data Binding)
  • 遠程方法調用(RMI)
  • 對象/關係數據映射(O/R mapping)

關於c++的反射

咱們知道,Java是原生支持反射機制的,經過Class類能夠經過名稱得到類對象,進一步操做。Python也支持反射機制,能夠經過globals()獲取對象map,也能夠經過inspect模塊,提供了自省的方法。可是C++呢?C++原生不支持反射機制,RTTI(運行時類型識別)也僅僅提供了類型的判斷。

開閉原則是設計模式的原則之一,對修改是封閉,對擴展開放。通常來講,須要咱們對類進行抽象,針對抽象的類進行編程。許多的設計模式中,爲了可以知足這一點,咱們經常使用一個配置文件,映射字符串與類型。而後經過反射機制得到字符串對應的對象,而後自動裝配已達到易於擴展的目的。

本文主要介紹兩個小的場景如何實現C++反射。實際上,C++並非對反射支持的很好,要支持動態和靜態反射,還須要慢慢去尋找,我給出一些資料

C++11 reflection library

RTTR 庫

Boost.Mirror 庫

Mirror C++ reflection library

本文討論如何在C++中實現簡單的反射。

場景

  • C++序列化,與反序列化。序列化就是將對象編程二進制的形式存儲在磁盤上,或者經過網絡傳輸給另外一臺機器。反序列化就是序列化的逆過程。可是這個逆過程,必需要根據字符串來判斷將二進制流轉化成什麼類型的對象。

  • 工廠模式,經常是根據一個字符串來獲取想要的對象。可是爲了知足開閉原則,咱們不能簡單的在工廠類中不斷的修改生產函數來擴展不一樣的類型。這個時候,須要利用反射,使用抽象類。

實現

思路是:

  • 使用map,映射字符串和生產函數
  • 每次構造新類型時,將生產函數註冊到map中
  • 工廠函數經過map得到生產函數,建造不一樣的對象

方案一

  • map存儲在Object抽象父類中
  • 使用ClassInfo輔助類型保存子類對象(包括了子類對象的構造函數)
  • map映射結構—->子類名稱:ClassInfo*
// Reflex.h 
class Object{
public:
    Object(){}
    virtual ~Object(){}
    static bool Register(ClassInfo *ci); // 註冊函數
    static Object *CreateObject(string name);
}

using ObjectConstructorFn = Object *(*)(void); // 構造函數指針
class ClassInfo {
public:
    ClassInfo(const string classname, ObjectConstructorFn ctor)
        :class_name_(classname), m_object_constructor_(ctor) {
        Object::Register(this); // 注入到Object中
    }

    virtual ~ClassInfo(){};
    Object *CreateObject() const { // 返回當前類型的構造函數
        return m_object_constructor_ ? (*m_object_constructor_) : 0;
    }

    const string GetClassName() const {return class_name_;}
    ObjectConstructorFn GetConstructor() {return m_object_constructor_;} 

private:
    string class_name_;
    ObjectConstructorFn m_object_constructor_; // 維護對象信息
}


==============================================================
// Reflex.cpp
#include "Reflex.h"

static unordered_map<string, ClassInfo *> *class_map = nullptr; // 延遲到第一次註冊

bool Object::Register(ClassInfo *ci) {
    if (!class_map) {
        class_map = new unordered_map<string, ClassInfo *>();
    }

    if (ci) {
        // 若是沒有註冊過
        string c_name = ci -> GetClassName();
        if (class_map -> find(c_name) == class_map -> end()) {
            class_map[c_name] = ci;
        }
        return true;
    }

    return false;
}

Object *Object::CreateObject(string name) {
    // 若是註冊過就直接調用classinfo的createobject
    if (class_map -> find(name) != class_map.end())
        return class_map[name] -> CreateObject();

    return nullptr;
}


==============================================================
// test.cpp
class A : public Object {
public:
    A(){}
    ~A(){}
    ClassInfo *GetClassInfo const{ return &m_class_info_;}

    // 自定義生產函數
    static Object *CreateObject() {
        return new A;
    }

protected:
    static ClassInfo m_class_info_;
}

// 最重要的一步,將當前類註冊到Object中
ClassInfo A::m_class_info_("A", A::CreateObject);

int main() {
    Object *obj = Object::CreateObject("A");
    delete obj;

    return 0;
}

上面的代碼實現了簡單的反射機制,可是還不夠好。每次構建類都須要寫許多重複性的代碼。而C++的宏爲咱們提供了很好的工具來簡化重複性的代碼。以下:

// Reflex.h

// 向類中添加 class_info 屬性以及 CreateObject、GetClassInfo方法
#define DECLEAR_CLASS(name) \
    protected: \
        static ClassInfo m_class_info_; \
    public:
        ClassInfo *GetClassInfo const; \
        static Object *CreateObject(); \

// 實現CreateObject和GetClassInfo兩個方法
#define IMPLEMENT_CLASS_COMMON(name, func) \
    ClassInfo name::m_class_info_((#name), (ObjectConstructorFn) func); \

    ClassInfo *name::GetClassInfo() const \
    { return &name::m_class_info_;}

// classInfo 屬性的初始化
#define IMPLEMENT_CLASS(name) \
    IMPLEMENT_CLASS_COMMON(name, name::CreateObject) \
    Object* name::CreateObject() \
    { return new name;}

==============================================================
// test.cpp
class B : public Object {
    DECLEAR_CLASS(B)
public:
    B(){}
    ~B(){}
};

IMPLEMENT_CLASS(B)

方案二

  • map存儲在單例工廠類中
  • 定義RegisterAction類型完成註冊動做
  • 一樣使用宏將重複性的代碼簡化
//工廠類的定義
class ClassFactory{
private:  
    map<string, PTRCreateObject> m_classMap ;  
    ClassFactory(){}; //構造函數私有化

public:   
    void* getClassByName(string className);  
    void registClass(string name, PTRCreateObject method) ;  
    static ClassFactory& getInstance() ;  
};

//工廠類的實現
//@brief:獲取工廠類的單個實例對象  
ClassFactory& ClassFactory::getInstance(){
    static ClassFactory sLo_factory;  
    return sLo_factory ;  
}  

//@brief:經過類名稱字符串獲取類的實例
void* ClassFactory::getClassByName(string className){  
    map<string, PTRCreateObject>::const_iterator iter;  
    iter = m_classMap.find(className) ;  
    if ( iter == m_classMap.end() )  
        return NULL ;  
    else  
        return iter->second() ;  
}  

//@brief:將給定的類名稱字符串和對應的建立類對象的函數保存到map中   
void ClassFactory::registClass(string name, PTRCreateObject method){  
    m_classMap.insert(pair<string, PTRCreateObject>(name, method)) ;  
}  

//註冊動做類
class RegisterAction{
public:
    RegisterAction(string className,PTRCreateObject ptrCreateFn){
        ClassFactory::getInstance().registClass(className,ptrCreateFn);
    }
};

==============================================================

//test class B
class TestClassB{
public:
    void m_print(){
        cout<<"hello TestClassB"<<endl;
    };
};

//@brief:建立類實例的回調函數
TestClassB* createObjTestClassB{
        return new TestClassB;
}
//註冊動做類的全局實例
RegisterAction g_creatorRegisterTestClassB("TestClassB",(PTRCreateObject)createObjTestClassB);

==============================================================
// 使用宏簡化重複性代碼

#define REGISTER(className)                                             \
    className* objectCreator##className(){                              \
        return new className;                                           \
    }                                                                   \
    RegisterAction g_creatorRegister##className(                        \
        #className,(PTRCreateObject)objectCreator##className)

既然提到了宏,這裏簡單的複習一下,宏是由 #define 定義而來。在預處理階段進行宏展開。它的格式是:

#define <宏名> (<參數表>) <宏體> 
#define N 2 + 2  // 僅僅是字符串替換
#define N (2 + 2)  // 也是字符串 ,可是是(2 + 2)

#define area(x) (x) * (x) // 帶參的宏定義參會看成字符串直接替換

三種特殊的符號:
#               #define Conn(x, y) x##y     // 表示鏈接,數字,字符串均可以
##             #define ToString(x) #x      // 就是加上雙引號 
#@           #define ToChar(x) #@x       //就是加上單引號, 越界會報錯

總結

反射在不少狀況下都須要使用,應用場景比較普遍,但願讀者可以仔細閱讀代碼。將反射機制使用在本身的工程裏,實現一些設計良好的框架。另外,C++宏的使用能夠極大的簡化一些重複性的代碼,能夠仔細研究一下。

 

=============

來自http://blog.csdn.net/scythe666/article/details/51718864


 

在 C++ 中實現反射

反射(Reflection)是許多語言(如 C#,Java)都擁有的特性,用於在運行時獲取類型信息,大大的提升了編程的靈活性,好比利用反射能夠極大的簡化 json/xml 解析、腳本綁定、屬性編輯器等的實現。可是 C++ 並無提供反射的支持,本文討論在 C++ 中實現反射機制的一種方式。

實現原理

 

在 C# 等語言中,類型信息是在編譯時由編譯器解析並存儲到元數據(Meta Data)中的,其中包括類的名稱、方法、屬性等信息。每新創建一個類,編譯器就會自動生成對應的類型信息,類型信息會被關聯在每個對象上。

可是在 C++ 中,編譯器並無爲咱們作這樣的事情,咱們只能本身將這些信息獲取並保存下來。咱們使用相似下面的結構存儲類的相關信息:

// 類信息
class Type {
    // 類名
    std::string name;

    // 基類
    const Type* baseType;

    // 是不是枚舉類型
    bool isEnum;

    // 構造方法
    std::vector<const Constructor*> constructors;

    // 屬性
    std::unordered_map<std::string, const Field*> fieldsMap;

    // 方法,因爲要支持重載,因此一個方法名對應多個方法
    std::unordered_map<std::string, std::vector<const Method*>> methodsMap;
};

 

// 引用類型
enum class ReferType {
    None,           // 無引用類型(值類型)
    Refer,          // 左值引用
    RightRefer,     // 右值引用
};

// 修飾類型,好比 int*、const Type、float&
class QualifiedType {
    // 基本類型
    const Type* type = nullptr;

    // 是否使用 const 修飾
    bool isConst = false;

    // 是否使用 volatile 修飾
    bool isVolatile = false;

    // 指針層級數量
    int pointerCount = 0;

    // 引用類型
    ReferType referType = ReferType::None;
};

// 屬性
class Field {
    // 屬性名
    std::string name;

    // 所屬的類
    const Type* ownerType;

    // 屬性類型,注意是修飾類型
    const QualifiedType fieldType;

    // 是否爲類屬性
    bool isStatic;
};

// 方法
class Method {
    // 方法名
    std::string name;

    // 返回類型
    const QualifiedType returnType = nullptr;

    // 所屬的類
    const Type* ownerType = nullptr;

    // 參數列表
    std::vector<QualifiedType> paramTypes;

    // 是否爲類方法
    bool isStatic;
};

 

經過在類聲明中插入代碼來註冊類型信息,並大量使用模板和宏來簡化代碼,以 Constructor 爲例,其實現以下:

template<class T, class... Args>
class ConstructorImpl : public Constructor {
private:
    const Type* type;

public:
    ConstructorImpl()
        : Constructor(typeof(T), { GetQualifiedType<Args>::Value() ... }) {
        static_assert(std::is_constructible<T, Args...>::value, "tried to register an undeclared constructor");
    }

    virtual Any Invoke(typename AsType<Args, Any>::Value... params) const override {
        return (Any)new T(std::forward<Args>((Args)params)...);
    }
};

 

註冊方法信息的實現相似這樣:

template<class OwnerType, class ReturnType, class... Args>
const Method* make_method(const std::string& name, ReturnType(OwnerType::*fun)(Args...)){
    return new MemberMethod<OwnerType, ReturnType, Args...>(name.substr(name.find_last_of(':') + 1), fun);
}

#define METHOD(fun) make_method(#fun, fun)

 

使用示例

下面演示了反射的枚舉定義,和反射類型信息的描述

REFLECT_ENUM(Sex, Male, Female)

class PhoneNumber{
    BEGIN_TYPE(PhoneNumber)
        FIELDS(FIELD(&PhoneNumber::areaCode), FIELD(&PhoneNumber::number))
        CTORS(DEFAULT_CTOR(PhoneNumber), CTOR(PhoneNumber, const std::string&, const std::string&))
        METHODS(METHOD(&PhoneNumber::ToString))
    END_TYPE
public:
    std::string areaCode;
    std::string number;

    PhoneNumber() {}
    PhoneNumber(const std::string& areaCode, const std::string& number) : areaCode(areaCode), number(number) {}

    std::string ToString() const { return areaCode + " " + number; }
};

class Person{
    BEGIN_TYPE(Person)
        FIELDS(FIELD(&Person::name), FIELD(&Person::sex), FIELD(&Person::phoneNumber), FIELD(&Person::totalNumber))
        CTORS(DEFAULT_CTOR(Person), CTOR(Person, const std::string&, float, Sex))
        METHODS(METHOD(&Person::Name), METHOD(&Person::GetSex), METHOD(&Person::GetPhoneNumber), METHOD(&Person::SetPhoneNumber), METHOD(&Person::GetTotalNumber))
    END_TYPE
protected:
    std::string name;
    Sex         sex;
    PhoneNumber phoneNumber;
    static int  instanceCount;

public:
    Person() { totalNumber++; }

    std::string&        Name() { return name; }
    Sex                 GetSex() const { return sex; }
    const PhoneNumber&  GetPhoneNumber() { return phoneNumber; }
    void                SetPhoneNumber(const PhoneNumber& phoneNumber) { this->phoneNumber = phoneNumber; }
    static int          GetInstanceCount() { return instanceCount; }
};

int Person::instanceCount = 0;

 

而後能夠像下面這樣使用

// 利用反射能夠實現通用的 json/xml 轉換
auto p = JsonParser::Parse<Person>(R"(
{
    "name": "John",
    "sex": "Female",
    "phoneNumber": { "areaCode": "+86", "number": "13888888888" }
}
)");

// 經過反射調用構造方法
auto newPhone = Type::GetType("PhoneNumber")->GetConstructor({qualified_typeof(const std::string&), qualified_typeof(const std::string&)})->Invoke(std::string("+86"), std::string("13000000000"));

// 調用帶參數的方法
p->GetType()->GetMethod("SetPhoneNumber")->Invoke(p.get(), newPhone);

// 調用類方法
int instanceCount = typeof(Person)->GetMethod("GetInstanceCount")->Invoke(nullptr);

// 獲取屬性值
Sex sex = p->GetType()->GetField("sex")->Get(p.get());

// 獲取枚舉值的名字
std::cout << Enum::GetName(sex) << std::endl;

// 輸出全部屬性的名字
for (auto f : typeof(Person)->GetFields()){
    std::cout << f->GetName() << "," << std::endl;
}

 

不足之處

  • 因爲大量使用模版技術,會致使代碼膨脹
  • 侵入式的聲明方式,必須對現有代碼作改動才能使用,若是不須要運行時類型信息,能夠改進成在一個單獨的初始化方法裏進行註冊

Demo 地址

Github: https://github.com/Sleen/cpp_reflection

相關文章
相關標籤/搜索