# serdepp **Repository Path**: kj-smart/serdepp ## Basic Information - **Project Name**: serdepp - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-12-11 - **Last Updated**: 2025-12-30 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Serdepp [![Linux](https://github.com/injae/serdepp/actions/workflows/linux.yml/badge.svg)](https://github.com/injae/serdepp/actions/workflows/linux.yml) [![Windows](https://github.com/injae/serdepp/actions/workflows/window.yml/badge.svg)](https://github.com/injae/serdepp/actions/workflows/window.yml) [![MacOS](https://github.com/injae/serdepp/actions/workflows/macos.yml/badge.svg)](https://github.com/injae/serdepp/actions/workflows/macos.yml)[![codecov](https://codecov.io/gh/injae/serdepp/branch/main/graph/badge.svg?token=2L217GF77B)](https://codecov.io/gh/injae/serdepp) c++17 low cost serialize deserialize adaptor library like rust serde.rs - [Features](#Features) - [Get Started](#Get-Started) - [Dependencies](#Dependencies) - [Install With Vcpkg](#Install-With-Vcpkg) - [Install](#Install) - [CMake](#CMake) - [Examples](#Examples) - [Basic Usage](#Basic-Usage) - [Macro Version](#Macro-Version) - [Nested Class](#Nested-Class-Example) - [Custom Serializer](#Custom-Serializer) - [Reflection](#Reflection) - [Attributes](#Attributes) - [Value or Struct](#value_or_struct_se) - [Default Value](#default_se) - [Enum](#enum_toupper) - [Container](#make_optional) - [Custom Attribute](#Custom-Attribute) - [Benchmark](#Benchmark) ## Features - [x] low cost serializer, deserializer adaptor - [x] json serialize, deserialize (with [rapidjson](https://github.com/Tencent/rapidjson)) - [x] json serialize, deserialize (with [nlohmann_json](https://github.com/nlohmann/json)) - [x] toml serialize, deserialize (with [toml11](https://github.com/ToruNiina/toml11)) - [x] yaml serialize, deserialize (with [yaml-cpp](https://github.com/jbeder/yaml-cpp)) - [x] [fmt](https://github.com/fmtlib/fmt) support - [x] std::cout(ostream) support - [x] struct, class support - [x] nested struct, class support - [x] enum, enum_class support (with [magic_enum](https://github.com/Neargye/magic_enum)) - [x] optional support - [x] container support (sequence(like vector, list), map(map, unordered_map ...)) - [x] [attributes](#Attributes) and custom attribute support (value_or_struct, default, multi_key ...) - [x] variant support (std::variant, UserType, EnumType...>) [example](examples/variant.cpp) - [x] pointer support (T*, std::shared_ptr, std::unique_ptr) - [x] [reflection](#Reflection) support ## Serdepp Strcuture ![Serdepp structure](Serdepp_Structure.png) ## Get Started ```cpp #include #include enum class t_enum { A, B }; struct example { DERIVE_SERDE(example, [attributes(skip)] (&Self::number_, "number") // attribute skip (&Self::vec_, "vec") (&Self::opt_vec_, "opt_vec") (&Self::tenum_, "t_enum") //.no_remain() optional: if have unregisted data -> Exception ) int number_; std::vector vec_; std::optional> opt_vec_; t_enum tenum_; }; int main() { example ex; ex.number_ = 1024; ex.vec_ = {"a", "b", "c"}; ex.tenum_ = t_enum::B; //std::cout << ex << "\n"; nlohmann::json json_from_ex = serde::serialize(ex); example ex_from_json = serde::deserialize(json_from_ex); std::cout << "json: " << json_from_ex.dump(4) << "\n"; fmt::print("fmt:{}\n",ex_from_json); } /* Result json:{ "t_enum": "B", "vec": [ "a", "b", "c" ] } fmt:{"vec: {"a", "b", "c"}", "t_enum: B"} */ ``` ## Dependencies - [nameof](https://github.com/Neargye/nameof) (Auto Install) - [magic_enum](https://github.com/Neargye/magic_enum) (Auto Install) - [fmt](https://github.com/fmtlib/fmt) (optional) (Install CMAKE FLAG: -DSERDEPP_USE_FMT=ON) - [nlohmann_json](https://github.com/nlohmann/json) (optional) (Install CMAKE FLAG: -DSERDEPP_USE_NLOHMANN_JSON=ON) - [rapidjson](https://github.com/Tencent/rapidjson) (optional) (Install CMAKE FLAG: -DSERDEPP_USE_RAPIDJSON=ON) - [toml11](https://github.com/ToruNiina/toml11) (optional) (Install CMAKE FLAG: -DSERDEPP_USE_TOML11=ON) - [yaml-cpp](https://github.com/jbeder/yaml-cpp) (optional) (Install CMAKE FLAG: -DSERDEPP_USE_YAML-CPP=ON) ## Install With Vcpkg ```console vcpkg install serdepp # with other adaptors vcpkg install ${adaptor} ``` ## CMake With Vcpkg ```cmake find_package(serdepp CONFIG) target_link_libraries(${target name} PRIVATE serdepp::serdepp) # with adaptors # names (nlohmann_json, yaml-cpp, toml11, RapidJson, fmt) # targets (nlohmann_json::nlohmann_json, yaml-cpp, toml11::toml11, rapidjson, fmt::fmt-header-only) find_package(${adaptor name} CONFIG) target_link_libraries(${target name} PRIVATE ${adaptor cmake target}) ``` ## Install ```console cmake -Bbuild -DCMAKE_BUILD_TYPE=Release . cd build cmake --build . --config Release --target install ``` ## UnInstall ```console cd build cmake --build . --config Release --target uninstall ``` ## CMake ```cmake find_package(serdepp) target_link_libraries({target name} PUBLIC serdepp::serdepp) ``` ## Compiler - minimum compiler version clang-8, gcc-10 # [Examples](./examples/) ## Basic Usage ```cpp #include "serdepp/serde.hpp" #include "serdepp/adaptor/rapidjson.hpp" #include "serdepp/adaptor/nlohmann_json.hpp" #include "serdepp/adaptor/yaml-cpp.hpp" #include "serdepp/adaptor/toml11.hpp" int main(int argc, char *argv[]) { int num = 1; auto rjson = serde::serialize(num); auto json = serde::serialize(num); auto yaml = serde::serialize(num); auto toml = serde::serialize(num); int from_rjson = serde::deserialize(rjson); int from_json = serde::deserialize(json); int from_toml = serde::deserialize(toml); int from_yaml = serde::deserialize(yaml); auto rjson_from_file = serde::parse_file("test.json"); auto json_from_file = serde::parse_file("test.json"); auto toml_from_file = serde::parse_file("test.toml"); auto yaml_from_file = serde::parse_file("test.yaml"); return 0; } ``` ## Define Struct Serializer ```cpp #include class test { public: template constexpr static void serde(Context& context, test& value) { using Self = test; serde::serde_struct(context, value) (&Self::str, "str") // or .field(&Self::str, "str") (&Self::i, "i") // or .field(&Self::i , "i") (&Self::vec, "vec"); // or .field(&Self::vec, "vec") } private: std::string str; int i; std::vector vec; }; ``` ## Macro Version ```cpp #include class test { public: DERIVE_SERDE(test, (&Self::str, "str")(&Self::i, "i")(&Self::vec, "vec")) private: std::string str; int i; std::vector vec; }; ``` ## Custom Serializer ```cpp struct Test { int i = 0; }; template struct serde_serializer { // serialize step constexpr inline static auto from(serde_ctx& ctx, Test& data, std::string_view key) { // serialize int -> Test serde_adaptor::from(ctx.adaptor, key, data.i); } // deserialize step constexpr inline static auto into(serde_ctx& ctx, const Test& data, std::string_view key) { // deserialize Test -> int serde_adaptor::into(ctx.adaptor, key, data.i); } }; ``` ## [Reflection](./example/reflection.cpp) ```cpp #include #include #include struct A { DERIVE_SERDE(A, (&Self::a, "a") (&Self::b, "b") (&Self::c, "c") (&Self::d, "d") (&Self::e, "e")) int a; std::string b; double c; std::vector d; int e; }; int main(int argc, char* argv[]) { constexpr auto info = serde::type_info; static_assert(serde::type_info.size == 5); static_assert(serde::tuple_size_v == 5); static_assert(std::is_same_v, std::tuple, int>>); static_assert(std::is_same_v>>); constexpr std::string_view a_name = info.name; auto a = A{1, "hello", 3.}; auto to_tuple = serde::make_tuple(a); std::string& member_a = info.member<1>(a); member_a = "why"; double& member_b_info = info.member(a, "c"); member_b_info = 3.14; auto member_d_info = info.member_info<3>(a); std::string_view member_d_name = member_d_info.name(); std::vector& member_d = member_d_info.value(); auto names = info.member_names(); for(auto& name : names.members()) { std::cout << name << "\n"; } return 0; } ``` ## [Simple Example](./examples/simple_example.cpp) ```cpp #include #include #include #include #include using namespace serde::ostream; enum class tenum { INPUT, OUTPUT, }; class test { public: template constexpr static auto serde(Context& context, test& value) { using Self = test; serde::serde_struct(context, value) .field(&Self::str, "str") // or (&test::str, "str") .field(&Self::i, "i") .field(&Self::vec, "vec") .field(&Self::io, "io") .field(&Self::pri, "pri") .field(&Self::m , "m"); } std::optional str; int i; std::optional> vec; tenum io; std::map m; private: std::string pri; }; int main() { nlohmann::json v = R"({ "i": 10, "vec": [ "one", "two", "three" ], "io": "INPUT", "pri" : "pri", "m" : { "a" : "1", "b" : "2", "c" : "3" } })"_json; test t = serde::deserialize(v); auto v_to_json = serde::serialize(t); auto v_to_toml = serde::serialize(t); auto v_to_yaml = serde::serialize(t); test t_from_toml = serde::deserialize(v_to_toml); test t_from_yaml = serde::deserialize(v_to_yaml); fmt::print("{}\n", t); std::cout << t << '\n'; return 0; } ``` ### [Nested Class Example](./examples/example.cpp) ```cpp #include #include #include #include #include /// optional beta feature (for std::cout) #include using namespace serde::ostream; /// enum class tenum { INPUT = 1, OUTPUT = 2, }; struct nested { DERIVE_SERDE(nested, [attributes(value_or_struct)] (&nested::version, "version") // value_or_struct attribute (&nested::opt_desc,"opt_desc") [attributes(default_{"default value"})] (&nested::desc ,"desc") // serialize step set default value .no_remain()) std::string version; std::string desc; std::optional opt_desc = "set opt default"; }; class test { public: template constexpr static auto serde(Context& context, test& value) { serde::serde_struct(context, value) .field(&test::str, "str") .field(&test::i, "i") .field(&test::vec, "vec") .field(&test::io, "io") .field(&test::in, "in") .field(&test::pri, "pri") .field(&test::m , "m") .field(&test::nm , "nm") ; } std::optional str; int i; std::optional> vec; tenum io; std::vector in; std::map m; std::map nm; private: std::string pri; }; int main() { try { nlohmann::json v = R"({ "str":"hello", "i": 10, "vec": [ "one", "two", "three" ], "io": "INPUT", "pri" : "pri", "in" : [{ "version" : "hello" }, "single"], "m" : { "a" : "1", "b" : "2", "c" : "3" }, "nm" : { "a" : {"version" : "hello" }, "b" : "hello2" } })"_json; // nlohmann::json -> class(test) test t = serde::deserialize(v); // class(test) -> nlohmann::json auto v_to_json = serde::serialize(t); // class(test) -> toml11 auto v_to_toml = serde::serialize(t); // class(test) -> yaml-cpp auto v_to_yaml = serde::serialize(t); // nlohmann::json -> string fmt::print("json: {}\n", v_to_json.dump()); // toml11 -> string std::cout << "toml: " << v_to_toml << std::endl; // yaml-cpp -> string std::cout << "yaml: " << v_to_yaml << std::endl; // toml11 -> class(test) test t_from_toml = serde::deserialize(v_to_toml); // yaml-cpp -> class(test) test t_from_yaml = serde::deserialize(v_to_yaml); // class(test) -> string fmt::print("{}\n", t); // beta feature // need: #include // need: using namespace serdepp::ostream; // class(test) -> string std:cout << t << '\n'; // } catch(std::exception& e) { fmt::print(stderr,"{}\n",e.what()); } return 0; } ``` ## 3 Way make optional container field 1. with default_ - if empty in serialize step -> set `std::vector{}` - if empty in deserialize step -> set null, ex json: "vec" : null 2. with optional - if empty in serialize step -> set `std::nullopt` - if empty in deserialize step -> skip 3. with make_optional - if empty in serialize step -> set `std::vector{}` - if empty in deserialize step -> skip ```cpp struct attribute_example { DERIVE_SERDE(attribute_example, [attributes(default_>{{}})] // 1 (&Self::vec, "vec") . (&Self::vec_opt, "vec_opt") // 2. [attributes(make_optional)] // 3. (&Self::vec_attr_opt, "vec_attr_opt") ) std::vector ver; std::optional> vec_opt; std::vector ver_att_opt; }; ``` # Attributes ## Two Way of Attributes add ## normal ```cpp struct attribute_example { DERIVE_SERDE(attribute_example, (&nested::version, "version", value_or_struct, default_("0.0.1"))) std::string version; }; ``` ## with syntax suger (Recommanded) ```cpp struct attribute_example { DERIVE_SERDE(attribute_example, [attributes(value_or_struct, default_("0.0.1"))] (&nested::version, "version")) std::string version; }; ``` ## `value_or_struct` ```cpp struct attribute_example { template constexpr static auto serde(Context& context, nested& value) { using namespace serde::attribute; serde::serde_struct(context, value) .field(&nested::version, "version", value_or_struct); } std::string version; }; ``` ## `default_` ### support tree type default value serializer 1. Type with Attribute default_ 2. std::optional Type with default 3. std::optional Type with Attribute default_ ```cpp struct attribute_example { template constexpr static auto serde(Context& context, attribute_example& value) { using namespace serde::attribute; using Self = attribute_example; serde::serde_struct(context, value) .field(&Self::ver, "ver", default_{"0.0.1"}) // 1. .field(&Self::ver_opt, "ver_opt") // 2. .field(&Self::ver_opt_default, "ver_opt_default", default_{"0.0.1"}); // 3. } std::string version; std::optional ver_opt = "-1.0.1"; std::optional ver_opt_att_default; }; ``` ## `toupper` or `tolower` ```cpp enum class u_enum { INPUT , OUTPUT, } enum class l_enum { input , output, } struct attribute_example { template constexpr static auto serde(Context& context, attribute_example& value) { using namespace serde::attribute; using Self = attribute_example; serde::serde_struct(context, value) // serialize: input -> INPUT -> uenum::INPUT // deserialize: uenum::INPUT -> INPUT -> input .field(&Self::test_uenum, "uenum", to_upper) // serialize: INPUT -> input -> uenum::input // deserialize: uenum::input -> input -> INPUT .field(&Self::test_lenum, "lenum", to_lower); } u_enum test_uenum; l_enum test_lenum; }; ``` ## `make_optional` - c++ container make like optional type - if empty in serialize step -> set `std::vector{}` - if empty in deserialize step -> not set ```cpp struct attribute_example { template constexpr static auto serde(Context& context, attribute_example& value) { using namespace serde::attribute; using Self = attribute_example; serde::serde_struct(context, value) .field(&Self::vec, "vec", make_optional); // 3. } std::vector ver; }; ``` ## [more attribute](./include/serdepp/attribute/) - `multi_key{...str}` - description: multiple key - args: initialize_list - example: `(&Self::test, "key", mutli_key{"key2", "key3"})` - `skip` - description: skip serialize, deserialize step - example: `(&Self::test, "key", skip)` - `skip_de` - description: skip deserialize step - example: `(&Self::test, "key", skip_de)` - `skip_se` - description: skip serialize step - example: `(&Self::test, "key", skip_se)` - `to_upper` - description: enum or string -> upper, upper string -> lower enum or string - example: `(&Self::test, "key", to_upper)` Enum::test -> TEST -> Enum::test - `to_lower` - description: enum or string -> lower, lower string -> upper enum or string - example: `(&Self::test, "key", to_lower)` Enum::TEST -> test -> Enum::test - `under_to_dash` - description: enum or string -> `_` -> `-` , `-` -> `_` enum or string - example: `(&Self::test, "key", under_to_dash)` Enum::TEST_TEST -> TEST-TEST -> Enum::TEST_TEST - `defualt_` - description: parse like optional value - example: `(&Self::test, "key", default_{"default value"})` if null -> set default - `value_or_struct` - description: parse struct or single value, require other field default_ or optional - example: `(&Self::test, "key", value_or_struct)` "T": "value" or "T" : { "key" : "value" } - `flatten` - description: parse struct flatten - example: `(&Self::test, "key", flatten)` - { "obj" : {"key" : "value", "key2" : "value"} } == { "key" : "value", "key2" : "value" } ## Custom Attribute ### 1. Normal Attribute ```cpp // value_or_struct code in serde/attribute/value_or_struct.hpp namespace serde::attribute { namespace detail { struct value_or_struct { //serialize step template constexpr inline void from(serde_ctx& ctx, T& data, std::string_view key, Next&& next_attr, Attributes&&... remains) { using Helper = serde_adaptor_helper; if(Helper::is_struct(ctx.adaptor)) { next_attr.template from(ctx, data, key, remains...); } else { next_attr.template from(ctx, data, "", remains...); } } //deserialize step template constexpr inline void into(serde_ctx& ctx, const T& data, std::string_view key, Next&& next_attr, Attributes&&... remains) { next_attr.template into(ctx, data, key, remains...); } }; } constexpr static auto value_or_struct = value_or_struct{}; } ``` ### 2. Args Attribute ```cpp // default_se code in serde/attribute/default.hpp namespace serde::attribute { template struct default_ { D&& default_value_; explicit default_(D&& default_value) noexcept : default_value_(std::move(default_value)) {} template constexpr inline void from(serde_ctx& ctx, T& data, std::string_view key, Next&& next_attr, Attributes&&... remains) { using Helper = serde_adaptor_helper; if(Helper::is_null(ctx.adaptor, key)) { data = std::move(default_value_); } else { next_attr.template from(ctx, data, key, remains...); } } template constexpr inline void into(serde_ctx& ctx, T& data, std::string_view key, Next&& next_attr, Attributes&&... remains) { next_attr.template into(ctx, data, key, remains...); } }; // deduce guide template default_(D&&) -> default_; } ``` ## Serdepp Type Declare Rule ### Sequence Type - like vector , list, - require: - T.begin() - T.end() ### Map Type - like map, unordered_map - require: - T::key_type - T::mapped_type - T.operator[](T::key_type&) ### Struct Type - require: - template void serde(Format& formst, T& value); ## Benchmark ### Benchmark [Benchmark code](benchmark/benchmark.cpp) ```console 2021-08-05T21:32:23+09:00 Running ./benchmark Run on (12 X 2600 MHz CPU s) CPU Caches: L1 Data 32 KiB (x6) L1 Instruction 32 KiB (x6) L2 Unified 256 KiB (x6) L3 Unified 12288 KiB (x1) Load Average: 2.52, 3.33, 3.15 ------------------------------------------------------------------ Benchmark Time CPU Iterations ------------------------------------------------------------------ nljson_set_se_bench 475 ns 474 ns 1306580 nljson_set_nl_bench 475 ns 472 ns 1550961 nljson_get_se_bench 2536 ns 2529 ns 275437 nljson_get_nl_bench 2768 ns 2764 ns 255292 toml11_set_se_bench 470 ns 469 ns 1496340 toml11_set_tl_bench 486 ns 485 ns 1418454 toml11_get_se_bench 3582 ns 3575 ns 195280 toml11_get_tl_bench 4194 ns 4189 ns 166580 yaml_set_se_bench 2091 ns 2088 ns 332965 yaml_set_tl_bench 2439 ns 2435 ns 285903 yaml_get_se_bench 25643 ns 25584 ns 26873 yaml_get_tl_bench 30182 ns 30155 ns 23070 rapid_json_set_se_bench 398 ns 397 ns 1743184 rapid_json_get_se_bench 2099 ns 2096 ns 331971 ``` ## Projects using this library - [cppm](https://github.com/injae/cppm): cross platform c++ package manager - [cpcli](https://github.com/injae/cpcli): c++ command line parser