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; }