C++ 反射

本文介绍了一种在C++中实现反射机制的方法,通过自定义ClassFactory类来完成类对象、成员变量及成员函数的反射操作。利用宏指令简化注册过程。

概念:

    反射机制允许程序在运行时借助Reflection API 取得任何类的内部信息,并能直接操作对象的内部属性和方法。

问题:

  1:c++不支持反射

  2:很多业务场景需要依赖反射机制,比如rpc、web mvc 、对象序列化。

目标:

  类对象的反射

  类成员数据的反射

  类成员函数的反射

  用法:

#include <iostream>
using namespace std;

#include "ClassFactory.h"
using namespace yazi::reflect;

#include "test/A.h"

int main()
{
    ClassFactory * factory = Singleton<ClassFactory>::instance();
    Object * a = factory->create_class("A");
    
    string name;
    a->get("m_name", name);
    a->set("m_name", "kitty");

    int age;
    a->get("m_age", age);
    a->set("m_age", 30);

    a->call("f1");
    int num = a->call<int>("f2", 123);

    return 0;
}

  类对象的反射:

   先创建下模板单例类

  Singleton.h

#pragma once

namespace yazi {
namespace utility {

template <typename T>
class Singleton
{
public:
    static T * instance()
    {
        if (m_instance == NULL)
            m_instance = new T();
        return m_instance;
    }

private:
    Singleton() {}
    Singleton(const Singleton<T> &);
    Singleton<T> & operator = (const Singleton<T> &);
    ~Singleton() {}

private:
    static T * m_instance;
};

template <typename T>
T * Singleton<T>::m_instance = NULL;

}}

   ClassFactory.h

#pragma once

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

#include "Singleton.h"
using namespace yazi::utility;

#include "ClassField.h"
#include "ClassMethod.h"

namespace yazi {
namespace reflect {

class Object
{
public:
    Object();
    virtual ~Object();

    void set_class_name(const string & className);
    const string & get_class_name() const;

    int get_field_count();
    ClassField * get_field(int pos);
    ClassField * get_field(const string & fieldName);

    template <typename T>
    void get(const string & fieldName, T & value);

    template <typename T>
    void set(const string & fieldName, const T & value);
    void set(const string & fieldName, const char * value);
    
    template <typename R = void, typename ...Args>
    R call(const string & methodName, Args... args);

private:
    string m_className;
};

typedef Object * (*create_object)(void);

class ClassFactory
{
    friend class Singleton<ClassFactory>;
public:
    // reflect class
    void register_class(const string & className, create_object method);
    Object * create_class(const string & className);

    // reflect class field
    void register_class_field(const string & className, const string & fieldName, const string & fieldType, size_t offset);
    int get_class_field_count(const string & className);
    ClassField * get_class_field(const string & className, int pos);
    ClassField * get_class_field(const string & className, const string & fieldName);

    // reflect class method
    void register_class_method(const string & className, const string &methodName, uintptr_t method);
    int get_class_method_count(const string & className);
    ClassMethod * get_class_method(const string & className, int pos);
    ClassMethod * get_class_method(const string & className, const string & methodName);

private:
    ClassFactory() {}
    ~ClassFactory() {}

private:
    std::map<string, create_object> m_classMap;
    std::map<string, std::vector<ClassField *>> m_classFields;
    std::map<string, std::vector<ClassMethod *>> m_classMethods;
};

template <typename T>
void Object::get(const string & fieldName, T & value)
{
    ClassField * field = Singleton<ClassFactory>::instance()->get_class_field(m_className, fieldName);
    size_t offset = field->offset();
    value = *((T *)((unsigned char *)(this) + offset));
}

template <typename T>
void Object::set(const string & fieldName, const T & value)
{
    ClassField * field = Singleton<ClassFactory>::instance()->get_class_field(m_className, fieldName);
    size_t offset = field->offset();
    *((T *)((unsigned char *)(this) + offset)) = value;
}

template <typename R, typename ...Args>
R Object::call(const string & methodName, Args... args)
{
    ClassFactory * factory = Singleton<ClassFactory>::instance();
    ClassMethod * method = factory->get_class_method(m_className, methodName);
    auto func = method->method();
    typedef std::function<R(decltype(this), Args...)> class_method;
    return (*((class_method *)func))(this, args...);
}

}
}

 ClassFactory.cpp

#include "ClassFactory.h"
using namespace yazi::reflect;

Object::Object() : m_className("")
{
}

Object::~Object()
{
}

void Object::set_class_name(const string & className)
{
    m_className = className;
}

const string & Object::get_class_name() const
{
    return m_className;
}

int Object::get_field_count()
{
    return Singleton<ClassFactory>::instance()->get_class_field_count(m_className);
}

ClassField * Object::get_field(int pos)
{
    return Singleton<ClassFactory>::instance()->get_class_field(m_className, pos);
}

ClassField * Object::get_field(const string & fieldName)
{
    return Singleton<ClassFactory>::instance()->get_class_field(m_className, fieldName);
}

void Object::set(const string & fieldName, const char * value)
{
    ClassField * field = Singleton<ClassFactory>::instance()->get_class_field(m_className, fieldName);
    size_t offset = field->offset();
    *((string *)((unsigned char *)(this) + offset)) = string(value);
}

void ClassFactory::register_class(const string & className, create_object method)
{
    m_classMap[className] = method;
}

Object * ClassFactory::create_class(const string & className)
{
    auto it = m_classMap.find(className);
    if (it == m_classMap.end())
    {
        return nullptr;
    }
    return it->second();
}

void ClassFactory::register_class_field(const string & className, const string & fieldName, const string & fieldType, size_t offset)
{
    m_classFields[className].push_back(new ClassField(fieldName, fieldType, offset));
}

int ClassFactory::get_class_field_count(const string & className)
{
    return m_classFields[className].size();
}

ClassField * ClassFactory::get_class_field(const string & className, int pos)
{
    int size = m_classFields[className].size();
    if (pos < 0 || pos >= size)
    {
        return nullptr;
    }
    return m_classFields[className][pos];
}

ClassField * ClassFactory::get_class_field(const string & className, const string & fieldName)
{
    auto fields = m_classFields[className];
    for (auto it = fields.begin(); it != fields.end(); it++)
    {
        if ((*it)->name() == fieldName)
        {
            return *it;
        }
    }
    return nullptr;
}

void ClassFactory::register_class_method(const string & className, const string &methodName, uintptr_t method)
{
    m_classMethods[className].push_back(new ClassMethod(methodName, method));
}

int ClassFactory::get_class_method_count(const string & className)
{
    return m_classMethods[className].size();
}

ClassMethod * ClassFactory::get_class_method(const string & className, int pos)
{
    int size = m_classMethods[className].size();
    if (pos < 0 || pos >= size)
    {
        return nullptr;
    }
    return m_classMethods[className][pos];
}

ClassMethod * ClassFactory::get_class_method(const string & className, const string & methodName)
{
    auto methods = m_classMethods[className];
    for (auto it = methods.begin(); it != methods.end(); it++)
    {
        if ((*it)->name() == methodName)
        {
            return *it;
        }
    }
    return nullptr;
}

其中ClassFactory中有个友元类Singleton,这样Singleteon就可以调用ClassFactory工厂类中的api了。

其中ClassRegister.h 是注册类、注册元素、和注册方法的类。类中获取单列类Singleton的接口,在用此接口依次注册类、元素、方法。

ClassRegister.h:

#pragma once

#include "ClassFactory.h"

namespace yazi {
namespace reflect {

class ClassRegister
{
public:
    ClassRegister(const string & className, create_object method)
    {
        // register class
        Singleton<ClassFactory>::instance()->register_class(className, method);
    }

    ClassRegister(const string & className, const string & fieldName, const string & fieldType, uintptr_t offset)
    {
        // register class field
        Singleton<ClassFactory>::instance()->register_class_field(className, fieldName, fieldType, offset);
    }

    ClassRegister(const string & className, const string & methodName, uintptr_t method)
    {
        // register class method
        Singleton<ClassFactory>::instance()->register_class_method(className, methodName, method);
    }
};


#define REGISTER_CLASS(className)                                       \
    Object * createObject##className()                                  \
    {                                                                   \
        Object * obj = new className();                                 \
        obj->set_class_name(#className);                                \
        return obj;                                                     \
    }                                                                  	\
    ClassRegister classRegister##className(#className, createObject##className)

#define REGISTER_CLASS_FIELD(className, fieldName, fieldType) \
    className className##fieldName; \
    ClassRegister classRegister##className##fieldName(#className, #fieldName, #fieldType, (size_t)(&(className##fieldName.fieldName)) - (size_t)(&className##fieldName))

#define REGISTER_CLASS_METHOD(className, methodName, returnType, ...) \
    std::function<returnType(className *, ##__VA_ARGS__)> className##methodName##method = &className::methodName; \
    ClassRegister classRegister##className##methodName(#className, #methodName, (uintptr_t)&(className##methodName##method))

}
}

其中REGISTER_CLASS(className)宏主要做两件事,一:创建classname的对象函数,调用ClassRegister向工厂类中注册。

要注册的类:

#pragma once

#include <string>
using namespace std;

#include "ClassRegister.h"
using namespace yazi::reflect;


class A : public Object
{
public:
    A() : m_name("a"), m_age(18) {}

    void f1()
    {
        std::cout << "f1" << std::endl;
    }

    int f2(int a)
    {
        std::cout << "f2" << std::endl;
        return a;
    }

public:
    string m_name;
    int m_age;
};

REGISTER_CLASS(A);
REGISTER_CLASS_FIELD(A, m_name, string);
REGISTER_CLASS_FIELD(A, m_age, int);
REGISTER_CLASS_METHOD(A, f1, void);
REGISTER_CLASS_METHOD(A, f2, int, int);

类成员数据的反射:

  关键问题:如何获取类成员数据的内存地址偏移量。

  下面是具体的两种方法。

#define OFFSET(className, fieldName) \
    ((size_t)(&((className *)0)->fieldName))

Test t;
size_t offset = (size_t)(&(t.member)) - (size_t)(&t);

 ClassField.h

#pragma once

#include <string>
using namespace std;

namespace yazi {
namespace reflect {

class ClassField
{
public:
    ClassField() : m_name(""), m_type(""), m_offset(0) {}
    ClassField(const string & name, const string & type, size_t offset) : m_name(name), m_type(type), m_offset(offset) {}
    ~ClassField() {}

    const string & name()
    {
        return m_name;
    }

    const string & type()
    {
        return m_type;
    }

    size_t offset()
    {
        return m_offset;
    }

private:
    string m_name;
    string m_type;
    size_t m_offset;
};

}
}

  类成员函数的反射:

 ClassMethod.h:

#pragma once

#include <string>
using namespace std;

namespace yazi {
namespace reflect {

class ClassMethod
{
public:
    ClassMethod() : m_name(""), m_method(0) {}
    ClassMethod(const string & name, uintptr_t method) : m_name(name), m_method(method) {}
    ~ClassMethod() {}

    const string & name()
    {
        return m_name;
    }

    uintptr_t method()
    {
        return m_method;
    }

private:
    string m_name;
    uintptr_t m_method;
};

}
}

      

### 三级标题:C++ 中的反射机制及其实现方法 C++ 标准语言本身并不直接支持运行时反射(Runtime Reflection)功能,如 Java 或 C# 那样可以在运行时动态获取类信息、调用方法或访问成员变量。然而,通过模板元编程、宏定义和外部工具等方式,可以实现编译期反射或模拟部分运行时反射的功能。 #### 编译期反射(Compile-time Reflection) 现代 C++(尤其是 C++17 及以后版本)引入了模板元编程和 constexpr 技术,使得在编译期进行一定程度的“反射”成为可能。例如,使用 `std::tuple` 和结构化绑定可以提取类成员的信息,并结合类型特征(type traits)进行操作。 以下是一个使用模板元编程和 `std::tuple` 模拟反射字段的示例: ```cpp #include <iostream> #include <tuple> #include <string> struct Person { std::string name; int age; auto as_tuple() const { return std::tie(name, age); } }; template<typename T> void reflect(const T& obj) { auto t = obj.as_tuple(); std::cout << "Fields: " << std::tuple_size<decltype(t)>::value << std::endl; } int main() { Person p{"Alice", 30}; reflect(p); } ``` 该方法利用了结构体到元组的映射,在编译期静态地获取字段数量和类型信息。 #### 使用宏和代码生成模拟反射 许多大型项目(如 Unreal Engine、Google Protocol Buffers)采用宏定义和代码生成工具来实现反射机制。例如,通过宏定义标记类成员,并在编译前自动生成反射信息注册代码。 以下是一个使用宏定义模拟反射字段的示例: ```cpp #define REFLECTABLE(class_name) \ template<typename F> \ void for_each_field(F&& f) const #define FIELD(name) f(#name, name) struct Point { int x, y; REFLECTABLE(Point) { FIELD(x); FIELD(y); } }; template<typename Func> void inspect_fields(const Point& p, Func func) { p.for_each_field(func); } int main() { Point p{10, 20}; inspect_fields(p, [](const char* name, int value) { std::cout << name << ": " << value << std::endl; }); } ``` 该方式通过宏定义将字段名称与值传递给回调函数,从而实现对字段的遍历和处理。 #### 第三方库支持 一些第三方库(如 Boost.Hana、RTTR、magic_get)提供了更高级别的反射能力。其中,Boost.Hana 提供了基于编译期元编程的结构化数据操作接口,而 RTTR(Run Time Type Reflection)则提供了完整的运行时反射机制,支持动态创建对象、调用方法和访问属性等操作。 例如,使用 RTTR 实现类成员注册和访问: ```cpp #include <rttr/type> #include <rttr/registration> using namespace rttr; struct MyStruct { int value; }; RTTR_REGISTRATION { registration::class_<MyStruct>("MyStruct") .property("value", &MyStruct::value); } int main() { type t = type::get_by_name("MyStruct"); property prop = t.get_property("value"); MyStruct obj; variant val = prop.get_value(&obj); std::cout << val.to_int() << std::endl; } ``` 该代码通过 RTTR 注册类成员,并在运行时通过字符串名称访问其值。 ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水火汪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值