AlgebraMaster

Modern C++ 创造非凡 . 改变世界

导航

C++ Runtime Reflection

参考:

1. A Flexible Reflection System in C++: Part 1 (preshing.com)

2. C++ Reflection | Austin Brunkhorst 

2做的更好。反射的代码是自动生成的。

其实还有个做法,C++ 代码里嵌个pythonVM,天生反射

python yyds:

class Base:
  def get_color(self) -> str:
    return "blue"

a = eval("Base")()
print(a.get_color())

 

 

目的

为了学习这个理念,先是从0 感受一个最简单的C++运行时反射实现:

假设:

struct Node {
    std::string key;
    int value;
}

可以实现:

int main() {
    using namespace std::string_literals;

    Node node = {"apple",3};
    auto *metaStruct = dynamic_cast<RF::MetaType_Struct*>(RF::MetaTypeResolver<Node>::get());
    metaStruct->dump(&node);
    std::cout << "after changed\n";
    metaStruct->setMember(&node, "key", "Houdini"s);
    metaStruct->setMember(&node, "value", 123);
    metaStruct->dump(&node);
    return 0;
}

运行时输出:

Node{
    key=std::string{apple}
    value=int{3}
}
after changed
Node{
    key=std::string{Houdini}
    value=int{123}
}

 

V0 一个结构体 2个成员:

#include <iostream>
#include <vector>
#include <any>
#include <concepts>
namespace RP_Reflection{
    struct MetaType{
        std::string typeName; // for example int, std::string
        size_t size;
        MetaType(std::string valueTypeName, size_t size) : typeName{std::move(valueTypeName)}, size{size} {}
        virtual ~MetaType() =default;
        [[nodiscard]] virtual std::string getFullTypeName() const { return typeName;}
        virtual void dump(const void *data) const= 0;
        virtual void set(void *data, std::any ) {}
    };
    // meta->string
    struct MetaType_Int: MetaType{
        explicit MetaType_Int() : MetaType{"int", sizeof(int)}{}
        void dump(const void *ptr) const override{ std::cout <<"int{" << *(static_cast<const int*>(ptr)) << "}";}
        void set(void *ptr, std::any v ) override{
            *static_cast<int*>(ptr) = std::any_cast<int>(v);
        }
    };
    // meta->int
    struct MetaType_String: MetaType{
        MetaType_String() : MetaType("std::string", sizeof(std::string)){}
        void dump(const void *ptr) const override{ std::cout <<"std::string" << "{"<< *(static_cast<const std::string*>(ptr))<< "}";}
        void set(void *ptr, std::any v ) override{
            *static_cast<std::string*>(ptr) = std::move(std::any_cast<std::string>(std::move(v)));
        }
    };
    // meta->struct
    struct MetaType_Struct: MetaType{
        struct MetaMember{
            std::string fieldName;
            size_t offset;
            MetaType *type;
        };
        std::vector<MetaMember> members;

        explicit MetaType_Struct(void (*init)(MetaType_Struct *)): MetaType{"",0} {
            init(this);
        }

        void dump(const void *ptr)const override{
            std::cout << typeName << "{"<< std::endl;
            for(const auto &member : members){
                std::cout << std::format("    {}=", member.fieldName);
                member.type->dump(static_cast<const char*>(ptr) + member.offset);
                std::cout << "\n";
            }
            std::cout << "}\n";
        }

        void setMember(void *ptr,const char *name, std::any data){
            for(auto &member : members){
                if(member.fieldName != name ) continue;
                member.type->set( static_cast<char*>(ptr) + member.offset, std::move(data));
            }
        }
    };

    // Declare the function template that handles primitive types such as int, std::string, etc.:
    template<typename T>
    MetaType *GetPrimitiveMetaType();

    // check class is MetaType
    template<typename T>
    concept isMetaType = std::is_base_of_v<MetaType, std::remove_cvref_t<std::remove_pointer_t<T>> >;

    // check a class that was reflected
    template<typename T>
    concept isReflectedClass = requires(T){
        { T::reflection } -> isMetaType;
    };

    // TemplateMethod Get MetaType
    template<typename T>
    struct MetaTypeResolver{
        static MetaType *get(){
            if constexpr (isReflectedClass<T>)
                return &T::reflection;
            else return GetPrimitiveMetaType<T>();
        }
    };

    template<>
    MetaType *GetPrimitiveMetaType<int>(){
        static MetaType_Int metaInt;
        return &metaInt;
    }
    template<>
    MetaType *GetPrimitiveMetaType<std::string>(){
        static MetaType_String metaStr;
        return &metaStr;
    }

} // end of namespace


namespace RF = RP_Reflection;

struct Node {
    std::string key;
    int value;

    // REFLECTION CODE HERE
    static RF::MetaType_Struct reflection;
    static void initReflection(RF::MetaType_Struct *);
};

RF::MetaType_Struct Node::reflection(Node::initReflection);

void Node::initReflection(RF::MetaType_Struct *ms) {
    ms->typeName = "Node";
    ms->size = sizeof(Node);
    ms->members = {
            {"key", offsetof(Node, key), RF::MetaTypeResolver<decltype(Node::key)>::get()},
            {"value", offsetof(Node, value), RF::MetaTypeResolver<decltype(Node::value)>::get()},
    };
}

int main() {
    using namespace std::string_literals;

    Node node = {"apple",3};
    auto *metaStruct = dynamic_cast<RF::MetaType_Struct*>(RF::MetaTypeResolver<Node>::get());
    metaStruct->dump(&node);
    std::cout << "after changed\n";
    metaStruct->setMember(&node, "key", "Houdini"s);
    metaStruct->setMember(&node, "value", 123);
    metaStruct->dump(&node);
    return 0;
}

 输出:

Node{
    key=std::string{apple}
    value=int{3}
}
after changed
Node{
    key=std::string{Houdini}
    value=int{123}
}

 

 V1 Json Dump 和std::vector<Node>

 

 

#include <iostream>
#include <vector>
#include <any>
#include <concepts>
#include <functional>
#include <ranges>
#include "json.hpp"

namespace RP_Reflection{
    struct MetaType{
        std::string typeName; // for example int, std::string
        size_t size;
        MetaType(std::string valueTypeName, size_t size) : typeName{std::move(valueTypeName)}, size{size} {}
        virtual ~MetaType() =default;
        [[nodiscard]] virtual std::string getFullTypeName() const { return typeName;}
        virtual void dump(const void *data, nlohmann::json &io) const= 0;
        virtual void set(void *data, std::any ) {}
    };
    // meta->string
    struct MetaType_Int: MetaType{
        explicit MetaType_Int() : MetaType{"int", sizeof(int)}{}
        void dump(const void *ptr, nlohmann::json &io) const override{
            io["type"] = typeName;
            io["value"] =  *(static_cast<const int*>(ptr));
        }
        void set(void *ptr, std::any v ) override{
            *static_cast<int*>(ptr) = std::any_cast<int>(v);
        }
    };
    // meta->int
    struct MetaType_String: MetaType{
        MetaType_String() : MetaType("std::string", sizeof(std::string)){}
        void dump(const void *ptr,nlohmann::json &io) const override{
            io["type"] = typeName;
            io["value"] =  *(static_cast<const std::string *>(ptr));
        }
        void set(void *ptr, std::any v ) override{
            *static_cast<std::string*>(ptr) = std::move(std::any_cast<std::string>(std::move(v)));
        }
    };
    // meta->struct
    struct MetaType_Struct: MetaType{
        struct MetaMember{
            std::string fieldName;
            size_t offset;
            MetaType *type;
        };
        std::vector<MetaMember> members;

        explicit MetaType_Struct(void (*init)(MetaType_Struct *)): MetaType{"",0} {
            init(this);
        }

        void dump(const void *ptr,nlohmann::json & io)const override{
            io["type"] = typeName;
            io["value"] ={};
            for(const auto &member : members){
                nlohmann::json tempIO;
                tempIO["member_name"] = member.fieldName;
                member.type->dump(static_cast<const char*>(ptr) + member.offset, tempIO);
                io["value"].emplace_back(tempIO);
            }

        }


        void setMember(void *ptr,const char *name, std::any data){
            for(auto &member : members){
                if(member.fieldName != name ) continue;
                member.type->set( static_cast<char*>(ptr) + member.offset, std::move(data));
            }
        }
    };

    // Declare the function template that handles primitive types such as int, std::string, etc.:
    template<typename T>
    MetaType *GetPrimitiveMetaType();

    // check class is MetaType
    template<typename T>
    concept isMetaType = std::is_base_of_v<MetaType, std::remove_cvref_t<std::remove_pointer_t<T>> >;

    // check a class that was reflected
    template<typename T>
    concept isReflectedClass = requires(T){
        { T::reflection } -> isMetaType;
    };

    // TemplateMethod Get MetaType
    template<typename T>
    struct MetaTypeResolver{
        static MetaType *get(){
            if constexpr (isReflectedClass<T>)
                return &T::reflection;
            else return GetPrimitiveMetaType<T>();
        }
    };

    template<>
    MetaType *GetPrimitiveMetaType<int>(){
        static MetaType_Int metaInt;
        return &metaInt;
    }
    template<>
    MetaType *GetPrimitiveMetaType<std::string>(){
        static MetaType_String metaStr;
        return &metaStr;
    }


    struct MetaType_StdVector: MetaType{
        MetaType *elementMetaType; // init in ctor
        // three function set value
        size_t (*getSize) (const void *vecPtr);
        const void* (*getItem)(const void *vecPtr, size_t idx);
        void (*setItem)(void *vecPtr, size_t idx, std::any v);
        void (*anyCasting)(void *vecPtr, std::any v);

        //
        template<typename ItemType>
        explicit MetaType_StdVector(ItemType *) : MetaType("std::vector<>", sizeof(std::vector<ItemType>)),elementMetaType(MetaTypeResolver<ItemType>::get()) {
            using vec_t = std::vector<ItemType>;
            getSize = [](const void *vecPtr){
                const auto& vec = *static_cast<const vec_t*>(vecPtr);
                return vec.size();
            };
            getItem = [](const void *vecPtr, size_t idx){
                const auto& vec = *static_cast<const vec_t*>(vecPtr);
                return static_cast<const void*>(&vec[idx]);
            };
            setItem = [](void *vecPtr, size_t idx, std::any v){
                auto elem_v = std::any_cast<ItemType>(v);
                (*static_cast<vec_t *>(vecPtr) ) [idx] = elem_v;
            };
            anyCasting = [](void *vecPtr, std::any v){
                auto vec = std::any_cast<vec_t>(std::move(v) );
                *static_cast<vec_t*>(vecPtr) = std::move(vec);
            };

        }
        [[nodiscard]] std::string getFullTypeName() const override{
            return std::string("std::vector<") + elementMetaType->getFullTypeName() + ">";
        }

        void dump(const void *obj,nlohmann::json &io) const override{
            io["type"] = getFullTypeName();
            io["value"] ={};
            for(int i=0;i<getSize(obj);i++){
                nlohmann::json tempIO;
                elementMetaType->dump(getItem(obj, i), tempIO);
                io["value"].emplace_back(tempIO);
            }

        }
        void set(void *ptr, std::any v) override{
            anyCasting(ptr,v);
        }

    };

    template<typename T>
    struct MetaTypeResolver<std::vector<T>>{
        static MetaType *get(){
            static MetaType_StdVector typeDesc{(T*) nullptr};
            return &typeDesc;
        }
    };



} // end of namespace


namespace RF = RP_Reflection;

struct Node {
    std::string key;
    int value;
    std::vector<Node> children;
    // REFLECTION CODE HERE
    static RF::MetaType_Struct reflection;
    static void initReflection(RF::MetaType_Struct *);
};

RF::MetaType_Struct Node::reflection(Node::initReflection);

void Node::initReflection(RF::MetaType_Struct *ms) {
    ms->typeName = "Node";
    ms->size = sizeof(Node);
    ms->members = {
            {"key", offsetof(Node, key), RF::MetaTypeResolver<decltype(Node::key)>::get()},
            {"value", offsetof(Node, value), RF::MetaTypeResolver<decltype(Node::value)>::get()},
            {"children", offsetof(Node, children), RF::MetaTypeResolver<decltype(Node::children)>::get()},
    };
}


int main() {
    using namespace std::string_literals;
    Node subNode1 = {"jog", 123,{}};
    Node subNode2 = {"sprint", 1500,{}};

    Node node = {"apple",3,
                 { std::move(subNode1), std::move(subNode2)}   };
    auto *metaStruct = dynamic_cast<RF::MetaType_Struct*>(RF::MetaTypeResolver<Node>::get());

    // dump
    nlohmann::json j;
    metaStruct->dump(&node, j);
    std::cout << j.dump(4) << std::endl;

    // change and dump
    std::cout << "----after changed----\n";
    metaStruct->setMember(&node, "key", "Houdini"s);
    metaStruct->setMember(&node, "value", 123);
    metaStruct->dump(&node, j);
    std::cout << j.dump(4) << std::endl;
    return 0;
}

 

posted on 2024-02-02 10:30  gearslogy  阅读(23)  评论(0编辑  收藏  举报