{"id":13730486,"url":"https://github.com/xyzdev/reflect","last_synced_at":"2025-05-08T03:30:42.360Z","repository":{"id":216133627,"uuid":"56623289","full_name":"xyzdev/reflect","owner":"xyzdev","description":"Reflection and serialization in C++","archived":false,"fork":false,"pushed_at":"2016-07-01T22:00:24.000Z","size":79,"stargazers_count":11,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-11-14T21:38:04.693Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xyzdev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2016-04-19T18:54:58.000Z","updated_at":"2023-08-04T07:23:00.000Z","dependencies_parsed_at":null,"dependency_job_id":"5f9c46f2-e958-4751-853f-f4966ab8864f","html_url":"https://github.com/xyzdev/reflect","commit_stats":null,"previous_names":["xyzdev/reflect"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xyzdev%2Freflect","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xyzdev%2Freflect/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xyzdev%2Freflect/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xyzdev%2Freflect/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xyzdev","download_url":"https://codeload.github.com/xyzdev/reflect/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252992834,"owners_count":21837181,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T02:01:15.540Z","updated_at":"2025-05-08T03:30:42.078Z","avatar_url":"https://github.com/xyzdev.png","language":"C++","readme":"# C++ Reflection and Serialization\n\nA simple header based library for reflection and serialization in C++.\n\n### Goals\n\n- Read and write objects to inspectable and serializable elements\n- List and call methods\n- Zero per-instance overhead (no virtual methods, etc)\n- DRY\n- No preprocessor magic\n\n### Case\n\nThis library was created primarily for use with an [ECS](https://en.wikipedia.org/wiki/Entity_component_system) architecture,\nwhere it can provide serialization of components, scripting language bindings to get and set components\nand call methods on systems, as well as a unified way to access data, e.g. for undo/redo in an editor or manual manipulation in a development console.\nSince reflection of every component and system class is required to fully enable this, there must be very little effort required per class.\nWhile performance is a minor concern for most of these cases, it is important that performance and memory use/alignment remain unaffected in \"ordinary\" use.\nWhen working with components, a name or id is normally available which can be mapped to a store or management class,\nwhile it may be acceptable to rely on virtual methods for reflection of systems.\n\n### Solution\n\nUse visitors and template specialization for double dispatch on action (read, write, call) and member type.\nAn optional macro removes the need to repeat the name of each field, eliminating a potential source of bugs.\nReading, writing and passing arguments is facilitated by an intermediate JSON element on which inspection,\nmanipulation and serialization can be performed.\n\n## JSON Element and Serialization / Deserialization\n\nThe provided `json::Element` class is utilized by the reflection system, however it should be fairly\nstraight-forward to replace it with your own intermediate data model. It can of course also be used\non its own, without the reflection system.\n\n## Examples\n\n### Reading and writing data\n\n```cpp\nstruct Component {\n    std::string field1;\n    std::vector\u003cint\u003e field2;\n\n    // Make reflectable:\n    void reflect(xyz::core::Reflection \u0026r) {\n        XYZ_REFLECT(r, field1);\n        XYZ_REFLECT(r, field2);\n    }\n};\n```\n\n```cpp\n// Read:\nxyz::core::ReflectionSink sink;\ncomponent.reflect(sink);\nstd::cout \u003c\u003c sink.sink.object()[\"field1\"].str();\n\n// Write:\nxyz::core::ReflectionSource source;\nsource.source.object()[\"field1\"] = \"new value\";\ncomponent.reflect(source);\n```\n\n### Type id\n\nThe `type_id` method template will produce an integer identifying a type for the duration of the\nprogram's execution. Note that it is NOT guaranteed to be stable across runs.\n\n```cpp\nxyz::core::type_id\u003cComponent\u003e() == xyz::core::type_id\u003cComponent\u003e();\nxyz::core::type_id\u003cComponent\u003e() != xyz::core::type_id\u003cint\u003e();\n```\n\nThis is useful for safe downcasts without RTTI and dynamic_cast, as well as for looking up manager\nclasses, etc.\n\n```cpp\nclass StoreBase {\npublic:\n    virtual ~StoreBase() {}\n    virtual int create() = 0;\n    virtual xyz::json::Element get(int) = 0;\n    virtual void set(int, xyz::json::Element) = 0;\n};\n\ntemplate\u003ctypename C\u003e\nclass Store: public StoreBase {\n    std::vector\u003cC\u003e components;\n\n    virtual int create();\n    virtual xyz::json::Element get(int);\n    virtual void set(int, xyz::json::Element);\n};\n\nstd::map\u003cxyz::core::TypeId, StoreBase*\u003e stores;\n\nauto COMPONENT_ID = xyz::core::type_id\u003cComponent\u003e();\nstores[COMPONENT_ID] = new Store\u003cComponent\u003e();\n```\n\n```cpp\nint myComponent = stores[COMPONENT_ID]-\u003ecreate();\nstores[COMPONENT_ID]-\u003eset(myComponent, xyz::json::Element());\n```\n\n### Calling methods\n\n```cpp\nclass System {\npublic:\n    std::vector\u003cComponent\u003e foo(int x, int y) { ... }\n    void bar() { ... }\n\n    // Make reflectable:\n    virtual void reflect(xyz::core::Reflection \u0026r) {\n        XYZ_REFLECT_METHOD(r, System, foo);\n        XYZ_REFLECT_METHOD(r, System, bar);\n    }\n};\n```\n\n```cpp\n// Call method:\nxyz::json::Array args(2);\nargs[0] = xyz::json::Number(42);\nargs[1] = xyz::json::Number(123);\n\nxyz::core::ReflectionCaller caller(\"foo\", args);\nsystem-\u003ereflect(caller);\n\nif(caller.found)\n    std::cout \u003c\u003c caller.result.array().size();\n```\n\n### Reflectors\n\nThe `Reflector` class template can be specialized to enable reflection of types which can't be\nmodified to add a `reflect` method.\n\n```cpp\ntemplate\u003c\u003e\nclass xyz::core::Reflector\u003cVector3, void\u003e: public xyz::core::AbstractReflector {\npublic:\n    typedef Vector3 field_type;\n\n    Reflector(field_type \u0026field)\n        :field(field) {}\n\n    json::Element read() {\n        json::Array array(3);\n        array[0] = field.x;\n        array[1] = field.y;\n        array[2] = field.z;\n        return json::Element(array);\n    }\n\n    void write(const json::Element \u0026data) {\n        json::Array array = data.array();\n        if(array.size() != 3) {\n            throw json::TypeError(\"Vector3 requires array with three Number elements.\");\n        }\n        field.x = array[0].number();\n        field.y = array[1].number();\n        field.z = array[2].number();\n    }\n\nprotected:\n    field_type \u0026field;\n};\n```\n\nOr simply:\n\n```cpp\ntemplate\u003c\u003e\nxyz::json::Element xyz::core::Reflector\u003cVector3\u003e::read() {\n    json::Array array(3);\n    array[0] = field.x;\n    array[1] = field.y;\n    array[2] = field.z;\n    return json::Element(array);\n}\ntemplate\u003c\u003e\nvoid xyz::core::Reflector\u003cVector3\u003e::write(const json::Element \u0026data) {\n    json::Array array = data.array();\n    if(array.size() != 3) {\n        throw json::TypeError(\"Vector3 requires array with three Number elements.\");\n    }\n    field.x = array[0].number();\n    field.y = array[1].number();\n    field.z = array[2].number();\n}\n```\n\n### TODO\n\n- Documentation and examples\n- Improve readability of code\n- Reflector should offer type description, i.e. `{id: \u003ctype_id\u003e, type: string/array/etc, [member: {id:...}] }`\n- UTF-8 support in JSON serialization\n- Function call on member's methods\n- Possibly add some syntactic sugar over `Reflection`, e.g. `result = Sink::get(component)`, `Source::set(component, data)`, `result = Caller::call(component, method, args...)`, etc.\n\n## License\n\nDistributed under the [MIT License](LICENSE.md).\n","funding_links":[],"categories":["C++"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxyzdev%2Freflect","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxyzdev%2Freflect","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxyzdev%2Freflect/lists"}