{"id":18869905,"url":"https://github.com/eyalz800/serializer","last_synced_at":"2025-08-21T08:31:21.321Z","repository":{"id":52066984,"uuid":"114425610","full_name":"eyalz800/serializer","owner":"eyalz800","description":"A single header standard C++ serialization framework.","archived":false,"fork":false,"pushed_at":"2021-12-17T02:29:19.000Z","size":134,"stargazers_count":189,"open_issues_count":2,"forks_count":24,"subscribers_count":10,"default_branch":"master","last_synced_at":"2024-12-11T06:10:23.312Z","etag":null,"topics":["cpp11","cpp14","cpp17","cross-platform","header-only","lightweight","serialization","simple","single-file","standalone","standard"],"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/eyalz800.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-12-16T01:50:35.000Z","updated_at":"2024-07-22T13:37:40.000Z","dependencies_parsed_at":"2022-09-06T05:22:05.650Z","dependency_job_id":null,"html_url":"https://github.com/eyalz800/serializer","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eyalz800%2Fserializer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eyalz800%2Fserializer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eyalz800%2Fserializer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eyalz800%2Fserializer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eyalz800","download_url":"https://codeload.github.com/eyalz800/serializer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230501172,"owners_count":18236061,"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":["cpp11","cpp14","cpp17","cross-platform","header-only","lightweight","serialization","simple","single-file","standalone","standard"],"created_at":"2024-11-08T05:18:05.695Z","updated_at":"2024-12-19T21:10:10.225Z","avatar_url":"https://github.com/eyalz800.png","language":"C++","funding_links":[],"categories":["C++"],"sub_categories":[],"readme":"zpp serializer\n==============\nA single header only standard C++ serialization framework.\nBefore you continue - if you are using C++20, you should probably use [zpp_bits](https://github.com/eyalz800/zpp_bits) instead.\n\nAbstract\n--------\nIn C++ there is no standard way of taking an object as-is and transforming it into a language\nindependent representation, that is, serialize it.\n\nSerialization frameworks are really common in C++, and they all come with difference promises and have their advantages and disadvantages.\nI've had the pleasure to seek a serialization framework that would turn my classes into data\nin the most effortless manner, without caring much about the format, and without doing unnecessary logic.\n\nSome frameworks support many common formats such as json, xml, and such; Some frameworks provide\nyou with the highest level of performance using zero copy techniques, thus supporting only binary format;\nSome frameworks require you to run a script that generates the C++ code that serializes your classes;\n\nWhile there are many excellent serialization frameworks, the diversity of the features and complexity often\nmake them hard to adopt, some of them require you to change existing code to integrate them, or even set up\nyour build environment differently.\n\nI finally reached the conclusion that I do not need all those features.\nI definitly do not want to either pay unnecessary price in performance to serialize my classes into a textual format, change the code of\nmy already existing classes, modify my build systems, write my classes in another format and compile it into C++.\n\nWhat I needed was to have my classes serialized in a zero overhead manner into binary, with the ability to serialize\nobjects by their dynamic type, allowing easy dispatch logic between a server and client side, with little to no\nchange to my already existing classes.\n\nMotivation\n----------\nProvide a single, simple header file, that would enable one to:\n* Enable save \u0026 load any STL container / string / utility into and from a binary form, in a zero overhead approach.\n* Enable save \u0026 load any object, by adding only a few lines to any class, without breaking existing code.\n* Enable save \u0026 load the dynamic type of any object, by a simple one-liner.\n\nContents\n--------\n* To enable save \u0026 load of any object, add the following lines to your class, `object_1, object_2, ...` being the non-static\ndata members of the class.\n```cpp\n    friend zpp::serializer::access;\n    template \u003ctypename Archive, typename Self\u003e\n    static void serialize(Archive \u0026 archive, Self \u0026 self)\n    {\n        archive(self.object_1, self.object_2, ...);\n    }\n```\nIf your class does not have a default constructor, define one as private.\n* To enable save \u0026 load of the dynamic type of any object, you have to register it, and have the base type derive from `zpp::serializer::polymorphic`.\nGiven the classes `v1::protocol::client_hello`, `v1::protocol::server_hello`, and `v1::protocol::sleep`, all derive from `protocol::command`.\n```cpp\n// protocol.h\nclass protocol::command : public zpp::serializer::polymorphic\n{\npublic:\n    virtual void operator()(protocol::context \u0026) = 0;\n    virtual ~command() = default;\n};\n\n// protocol.cpp\nnamespace\n{\nzpp::serializer::register_types\u003c\n   zpp::serializer::make_type\u003cv1::protocol::client_hello, zpp::serializer::make_id(\"v1::protocol::client_hello\")\u003e,\n   zpp::serializer::make_type\u003cv1::protocol::server_hello, zpp::serializer::make_id(\"v1::protocol::server_hello\")\u003e,\n   zpp::serializer::make_type\u003cv1::protocol::sleep, zpp::serializer::make_id(\"v1::protocol::sleep\")\u003e,\n   // ...\n\u003e _;\n}\n```\n* Save and load objects into a vector of data, in this example we show polymorphic serialization which\nhas an overhead of 8 bytes serialization id, per polymorphic object being serialized.\n```cpp\n// The data of the objects we serialize, the vector will grow and shrink as we serialize\n// data to/from the vector.\nstd::vector\u003cunsigned char\u003e data;\n\n// Turns an object into data.\nzpp::serializer::memory_output_archive out(data);\n\n// Turns data into objects.\nzpp::serializer::memory_input_archive in(data);\n\n// Create a sleep command.\nstd::unique_ptr\u003cprotocol::command\u003e command = std::make_unique\u003cv1::protocol::sleep\u003e(60s);\n\n// Serialize a unique pointer of an object whose zpp::serializer::polymorphic is a base class,\n// prepends 8 bytes of the serialization id, then the derived class is serialized.\nout(command);\n\n// ...\n// Deserializes a unique pointer of an object whose zpp::serializer::polymorphic is a base class,\n// loads 8 bytes of the serialization id, constructs a `v1::protocol::sleep` then deseializes into it.\nin(command);\n\n// Run the command, any command has its own logic.\n(*command)(protocol_context);\n```\n\n* You can serialize multiple objects in the same line:\n```cpp\nout(object_1, object_2, ...);\nin(object_1, object_2, ...);\n```\n\n* You can request serializtion without the polymorphic overhead, thus the static type\nis serialized and only this type can be loaded in the other end.\n```\nout(*command);\nin(*command);\n```\n\n* Serializing STL containers and strings, first stores a 4 byte size, then the elements:\n```\nstd::vector\u003cint\u003e v = { 1, 2, 3, 4 };\nout(v);\nin(v);\n```\nThe reason why the default size type is of 4 bytes (i.e `std::uint32_t`) is that most programs\nalmost never reach a case of a container being more than ~4 billion items, and it may be unjust to\npay the price of 8 bytes size by default.\n\n* For specific size types that are not 4 bytes, use `zpp::serializer::size_is\u003cSizeType\u003e()`:\n```\nstd::vector\u003cint\u003e v = { 1, 2, 3, 4 };\nout(zpp::serializer::size_is\u003cstd::uint16_t\u003e(v));\nin(zpp::serializer::size_is\u003cstd::uint16_t\u003e(v));\n```\nMake sure that the size type is large enough for the serialized object, otherwise less items\nwill be serialized, according to conversion rules of unsigned types. Uncareful use may lead to\nerroneuos code.\n\n* You may use `memory_view_input_archive`/`memory_view_output_archive` that receives a view type or pointer and size rather than\na vector which requires ownership and memory allocation. In contrary to the owning archives, the view types are not altered, and\nyou should use the `offset()` function to determine the position of the processed input and output.\n\n* Serialization using argument dependent lookup is also possible:\n```cpp\nnamespace my_namespace\n{\nstruct adl\n{\n    int x;\n    int y;\n};\n\ntemplate \u003ctypename Archive\u003e\nvoid serialize(Archive \u0026 archive, adl \u0026 adl)\n{\n    archive(adl.x, adl.y);\n}\n\ntemplate \u003ctypename Archive\u003e\nvoid serialize(Archive \u0026 archive, const adl \u0026 adl)\n{\n    archive(adl.x, adl.y);\n}\n}\n```\n\n* Objects that do not derive from `zpp::serializer::polymorphic` do not need registration\nand have no overhead at all.\n\nExample\n-------\n```cpp\n#include \"serializer.h\"\n#include \u003cvector\u003e\n#include \u003ciostream\u003e\n\nclass point\n{\npublic:\n    point() = default;\n    point(int x, int y) noexcept :\n        m_x(x),\n        m_y(y)\n    {\n    }\n\n    friend zpp::serializer::access;\n    template \u003ctypename Archive, typename Self\u003e\n    static void serialize(Archive \u0026 archive, Self \u0026 self)\n    {\n        archive(self.m_x, self.m_y);\n    }\n\n    int get_x() const noexcept\n    {\n        return m_x;\n    }\n\n    int get_y() const noexcept\n    {\n        return m_y;\n    }\n\nprivate:\n    int m_x = 0;\n    int m_y = 0;\n};\n\nclass person : public zpp::serializer::polymorphic\n{\npublic:\n    person() = default;\n    explicit person(std::string name) noexcept :\n        m_name(std::move(name))\n    {\n    }\n\n    friend zpp::serializer::access;\n    template \u003ctypename Archive, typename Self\u003e\n    static void serialize(Archive \u0026 archive, Self \u0026 self)\n    {\n        archive(self.m_name);\n    }\n\n    const std::string \u0026 get_name() const noexcept\n    {\n        return m_name;\n    }\n\n    virtual void print() const\n    {\n        std::cout \u003c\u003c \"person: \" \u003c\u003c m_name;\n    }\n\nprivate:\n    std::string m_name;\n};\n\nclass student : public person\n{\npublic:\n    student() = default;\n    student(std::string name, std::string university) noexcept :\n        person(std::move(name)),\n        m_university(std::move(university))\n    {\n    }\n\n    friend zpp::serializer::access;\n    template \u003ctypename Archive, typename Self\u003e\n    static void serialize(Archive \u0026 archive, Self \u0026 self)\n    {\n        person::serialize(archive, self);\n        archive(self.m_university);\n    }\n\n    virtual void print() const\n    {\n        std::cout \u003c\u003c \"student: \" \u003c\u003c person::get_name() \u003c\u003c ' ' \u003c\u003c m_university \u003c\u003c '\\n';\n    }\n\nprivate:\n    std::string m_university;\n};\n\nnamespace\n{\nzpp::serializer::register_types\u003c\n   zpp::serializer::make_type\u003cperson, zpp::serializer::make_id(\"v1::person\")\u003e,\n   zpp::serializer::make_type\u003cstudent, zpp::serializer::make_id(\"v1::student\")\u003e\n\u003e _;\n} // \u003canynymous namespace\u003e\n\nstatic void foo()\n{\n    std::vector\u003cunsigned char\u003e data;\n    zpp::serializer::memory_input_archive in(data);\n    zpp::serializer::memory_output_archive out(data);\n\n    out(point(1337, 1338));\n\n    point my_point;\n    in(my_point);\n\n    std::cout \u003c\u003c my_point.get_x() \u003c\u003c ' ' \u003c\u003c my_point.get_y() \u003c\u003c '\\n';\n}\n\nstatic void bar()\n{\n    std::vector\u003cunsigned char\u003e data;\n    zpp::serializer::memory_input_archive in(data);\n    zpp::serializer::memory_output_archive out(data);\n\n    std::unique_ptr\u003cperson\u003e my_person = std::make_unique\u003cstudent\u003e(\"1337\", \"1337University\");\n    out(my_person);\n\n    my_person = nullptr;\n    in(my_person);\n\n    my_person-\u003eprint();\n}\n\nstatic void foobar()\n{\n    std::vector\u003cunsigned char\u003e data;\n    zpp::serializer::memory_input_archive in(data);\n    zpp::serializer::memory_output_archive out(data);\n\n    out(zpp::serializer::as_polymorphic(student(\"1337\", \"1337University\")));\n\n    std::unique_ptr\u003cperson\u003e my_person;\n    in(my_person);\n\n    my_person-\u003eprint();\n}\n```\n\nFreestanding Implementation\n--------------------------\nThe library also supports experimental freestanding mode, to allow running in an environment\nwithout exceptions and rtti.\n\nTo enable freestanding mode, define `ZPP_SERIALIZER_FREESTANDING` preprocessing macro.\n\nIn this mode polymorphic serialization is not supported, and error checking\nis done via return values.\n\nThe returned error type is `zpp::serializer::freestanding::error`. The numeric value of the error is of\nthe values in the enum class `zpp::serializer::error` and is accessible by `code()` member function.\nThe error message is accessible by calling the `message()` member function, as a `std::string_view`.\n\nIn this mode serialization functions should be declared with `auto` as the return type, and return the result\nfrom the `archive`, like so:\n```cpp\n    template \u003ctypename Archive, typename Self\u003e\n    static auto serialize(Archive \u0026 archive, Self \u0026 self)\n    {\n        return archive(self.m_x, self.m_y);\n    }\n```\n\nError checking is done like so:\n```cpp\n    std::vector\u003cunsigned char\u003e data;\n    zpp::serializer::memory_input_archive in(data);\n    zpp::serializer::memory_output_archive out(data);\n\n    if (auto result = out(point(1337, 1338)); !result) {\n        std::cout \u003c\u003c \"Error: \" \u003c\u003c result.code() \u003c\u003c \" message: \" \u003c\u003c result.message() \u003c\u003c '\\n';\n        // return failure / throw\n    }\n\n    point my_point;\n    if (auto result = in(my_point); !result) {\n        std::cout \u003c\u003c \"Error: \" \u003c\u003c result.code() \u003c\u003c \" message: \" \u003c\u003c result.message() \u003c\u003c '\\n';\n        // return failure / throw\n    }\n\n    std::cout \u003c\u003c my_point.get_x() \u003c\u003c ' ' \u003c\u003c my_point.get_y() \u003c\u003c '\\n';\n```\n\nA Python Version\n----------------\nA compact python version of the library can be found here: https://github.com/eyalz800/zpp_serializer_py.\nYou can use this library to intercommunicate with this one. The python version does not support variant/optional.\n\nRequirements\n------------\nThis framework requires a fully compliant C++14 compiler, including RTTI and exceptions enabled.\nOne can easily overcome the RTTI requirement by using the following project: https://github.com/eyalz800/type_info.\nDisclaimer: registering polymorphic types can be slower in C++14 compared to C++17 due to the use of `shared_timed_mutex` instead of `shared_mutex`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feyalz800%2Fserializer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feyalz800%2Fserializer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feyalz800%2Fserializer/lists"}