diff --git a/benchmark/pb_benchmark.cpp b/benchmark/pb_benchmark.cpp index c73a34b0..8787ad69 100644 --- a/benchmark/pb_benchmark.cpp +++ b/benchmark/pb_benchmark.cpp @@ -1,6 +1,7 @@ #include #define SEQUENTIAL_PARSE #include "../test/proto/unittest_proto3.h" +#include "iguana/iguana.hpp" class ScopedTimer { public: diff --git a/iguana/detail/pb_type.hpp b/iguana/detail/pb_type.hpp index 2ce5f60b..37db25f7 100644 --- a/iguana/detail/pb_type.hpp +++ b/iguana/detail/pb_type.hpp @@ -1,4 +1,5 @@ #pragma once +#include namespace iguana { @@ -87,6 +88,10 @@ inline bool operator<(const sfixed64_t& lhs, const sfixed64_t& rhs) { return lhs.val < rhs.val; } +template +constexpr bool is_pb_type_v = std::is_same_v||std::is_same_v||std::is_same_v|| +std::is_same_v || std::is_same_v|| std::is_same_v; + } // namespace iguana // for key in std::unordered_map diff --git a/iguana/dynamic.hpp b/iguana/dynamic.hpp new file mode 100644 index 00000000..86f0a177 --- /dev/null +++ b/iguana/dynamic.hpp @@ -0,0 +1,89 @@ +#pragma once +#include "reflection.hpp" + +namespace iguana { +using base = detail::base; + +template +IGUANA_INLINE constexpr size_t member_offset(T* t, U T::*member) { + return (char*)&(t->*member) - (char*)t; +} + +template +struct base_impl : public base { + void to_pb(std::string& str) override { + to_pb_adl((iguana_adl_t*)nullptr, *(static_cast(this)), str); + } + + void from_pb(std::string_view str) override { + from_pb_adl((iguana_adl_t*)nullptr, *(static_cast(this)), str); + } + + void to_json(std::string& str) override { + to_json_adl((iguana_adl_t*)nullptr, *(static_cast(this)), str); + } + + void from_json(std::string_view str) override { + from_json_adl((iguana_adl_t*)nullptr, *(static_cast(this)), str); + } + + void to_xml(std::string& str) override { + to_xml_adl((iguana_adl_t*)nullptr, *(static_cast(this)), str); + } + + void from_xml(std::string_view str) override { + from_xml_adl((iguana_adl_t*)nullptr, *(static_cast(this)), str); + } + + iguana::detail::field_info get_field_info(std::string_view name) override { + static constexpr auto map = iguana::get_members(); + iguana::detail::field_info info{}; + for (auto& [no, field] : map) { + if (info.offset > 0) { + break; + } + std::visit( + [&](auto val) { + if (val.field_name == name) { + info.offset = member_offset((T*)this, val.member_ptr); + using value_type = typename decltype(val)::value_type; +#if defined(__clang__) || defined(_MSC_VER) || \ + (defined(__GNUC__) && __GNUC__ > 8) + info.type_name = type_string(); +#endif + } + }, + field); + } + + return info; + } + + std::vector get_fields_name() override { + static constexpr auto map = iguana::get_members(); + std::vector vec; + for (auto [no, val] : map) { + std::visit( + [&](auto& field) { + vec.push_back(std::string_view(field.field_name.data(), + field.field_name.size())); + }, + val); + } + return vec; + } + + virtual ~base_impl() {} + + size_t cache_size = 0; +}; + +IGUANA_INLINE std::shared_ptr create_instance(std::string_view name) { + auto it = iguana::detail::g_pb_map.find(name); + if (it == iguana::detail::g_pb_map.end()) { + throw std::invalid_argument(std::string(name) + + "not inheried from iguana::base_impl"); + } + return it->second(); +} +} // namespace iguana \ No newline at end of file diff --git a/iguana/iguana.hpp b/iguana/iguana.hpp new file mode 100644 index 00000000..77dc2bc5 --- /dev/null +++ b/iguana/iguana.hpp @@ -0,0 +1,10 @@ +#pragma once +#include "iguana/dynamic.hpp" +#include "iguana/json_reader.hpp" +#include "iguana/json_writer.hpp" +#include "iguana/pb_reader.hpp" +#include "iguana/pb_writer.hpp" +#include "iguana/xml_reader.hpp" +#include "iguana/xml_writer.hpp" +#include "iguana/yaml_reader.hpp" +#include "iguana/yaml_writer.hpp" diff --git a/iguana/json_reader.hpp b/iguana/json_reader.hpp index 05018349..d433f017 100644 --- a/iguana/json_reader.hpp +++ b/iguana/json_reader.hpp @@ -86,6 +86,11 @@ IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) { } } +template , int> = 0> +IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) { + from_json_impl(value.val, it, end); +} + template , int> = 0> IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) { skip_ws(it, end); @@ -899,4 +904,10 @@ IGUANA_INLINE void from_json_file(T &value, const std::string &filename, } } +template +IGUANA_INLINE void from_json_adl(iguana_adl_t *p, T &t, + std::string_view pb_str) { + iguana::from_json(t, pb_str); +} + } // namespace iguana diff --git a/iguana/json_util.hpp b/iguana/json_util.hpp index f1bf3a33..5887599f 100644 --- a/iguana/json_util.hpp +++ b/iguana/json_util.hpp @@ -3,6 +3,7 @@ #pragma once +#include "detail/pb_type.hpp" #include "util.hpp" #include "value.hpp" diff --git a/iguana/json_writer.hpp b/iguana/json_writer.hpp index eeaeaa39..498e0a2d 100644 --- a/iguana/json_writer.hpp +++ b/iguana/json_writer.hpp @@ -13,7 +13,7 @@ template -IGUANA_INLINE void to_json_impl(Stream &ss, std::optional &val); +IGUANA_INLINE void to_json_impl(Stream &ss, const std::optional &val); template , int> = 0> @@ -76,6 +76,12 @@ IGUANA_INLINE void to_json_impl(Stream &ss, T value) { ss.append(temp, p - temp); } +template , int> = 0> +IGUANA_INLINE void to_json_impl(Stream &ss, T value) { + to_json_impl(ss, value.val); +} + template , int> = 0> IGUANA_INLINE void to_json_impl(Stream &ss, T v) { @@ -97,12 +103,18 @@ IGUANA_INLINE void to_json_impl(Stream &ss, T &&t) { template , int> = 0> -IGUANA_INLINE void render_key(Stream &ss, T &t) { +IGUANA_INLINE void render_key(Stream &ss, const T &t) { ss.push_back('"'); to_json_impl(ss, t); ss.push_back('"'); } +template , int> = 0> +IGUANA_INLINE void render_key(Stream &ss, const T &t) { + render_key(ss, t.val); +} + template , int> = 0> IGUANA_INLINE void render_key(Stream &ss, T &&t) { @@ -140,7 +152,7 @@ IGUANA_INLINE void to_json_impl(Stream &ss, T val) { } template -IGUANA_INLINE void to_json_impl(Stream &ss, std::optional &val) { +IGUANA_INLINE void to_json_impl(Stream &ss, const std::optional &val) { if (!val) { ss.append("null"); } @@ -282,5 +294,10 @@ IGUANA_INLINE void to_json(T &&t, Stream &s) { to_json_impl(s, t); } +template +IGUANA_INLINE void to_json_adl(iguana_adl_t *p, T &t, std::string &pb_str) { + to_json(t, pb_str); +} + } // namespace iguana #endif // SERIALIZE_JSON_HPP diff --git a/iguana/pb_reader.hpp b/iguana/pb_reader.hpp index 6a0a92d9..83d2425d 100644 --- a/iguana/pb_reader.hpp +++ b/iguana/pb_reader.hpp @@ -3,7 +3,8 @@ #include "pb_util.hpp" namespace iguana { - +template +IGUANA_INLINE void from_pb(T& t, std::string_view pb_str); namespace detail { template @@ -254,4 +255,9 @@ IGUANA_INLINE void from_pb(T& t, std::string_view pb_str) { } } } + +template +IGUANA_INLINE void from_pb_adl(iguana_adl_t* p, T& t, std::string_view pb_str) { + iguana::from_pb(t, pb_str); +} } // namespace iguana diff --git a/iguana/pb_util.hpp b/iguana/pb_util.hpp index b2e46772..5fa7365c 100644 --- a/iguana/pb_util.hpp +++ b/iguana/pb_util.hpp @@ -29,83 +29,8 @@ enum class WireType : uint32_t { Unknown }; -template -IGUANA_INLINE void to_pb(T& t, Stream& out); - template -IGUANA_INLINE void from_pb(T& t, std::string_view pb_str); - -using base = detail::base; - -template -IGUANA_INLINE constexpr size_t member_offset(T* t, U T::*member) { - return (char*)&(t->*member) - (char*)t; -} - -template -struct base_impl : public base { - void to_pb(std::string& str) override { - iguana::to_pb(*(static_cast(this)), str); - } - - void from_pb(std::string_view str) override { - iguana::from_pb(*(static_cast(this)), str); - } - - iguana::detail::field_info get_field_info(std::string_view name) override { - static constexpr auto map = iguana::get_members(); - iguana::detail::field_info info{}; - for (auto [no, field] : map) { - if (info.offset > 0) { - break; - } - std::visit( - [&](auto val) { - if (val.field_name == name) { - info.offset = member_offset((T*)this, val.member_ptr); - using value_type = typename decltype(val)::value_type; -#if defined(__clang__) || defined(_MSC_VER) || \ - (defined(__GNUC__) && __GNUC__ > 8) - info.type_name = type_string(); -#endif - } - }, - field); - } - - return info; - } - - std::vector get_fields_name() override { - static constexpr auto map = iguana::get_members(); - std::vector vec; - for (auto [no, val] : map) { - std::visit( - [&](auto& field) { - vec.push_back(std::string_view(field.field_name.data(), - field.field_name.size())); - }, - val); - } - return vec; - } - - virtual ~base_impl() {} - - size_t cache_size = 0; -}; - -template -constexpr bool inherits_from_base_v = std::is_base_of_v; - -IGUANA_INLINE std::shared_ptr create_instance(std::string_view name) { - auto it = iguana::detail::g_pb_map.find(name); - if (it == iguana::detail::g_pb_map.end()) { - throw std::invalid_argument(std::string(name) + - "not inheried from iguana::base_impl"); - } - return it->second(); -} +constexpr bool inherits_from_base_v = std::is_base_of_v; namespace detail { template diff --git a/iguana/pb_writer.hpp b/iguana/pb_writer.hpp index db2662ab..9e7bbe5e 100644 --- a/iguana/pb_writer.hpp +++ b/iguana/pb_writer.hpp @@ -216,4 +216,9 @@ IGUANA_INLINE void to_pb(T& t, Stream& out) { detail::to_pb_impl<0>(t, &out[0], sz_ptr); } +template +IGUANA_INLINE void to_pb_adl(iguana_adl_t* p, T& t, Stream& out) { + to_pb(t, out); +} + } // namespace iguana diff --git a/iguana/reflection.hpp b/iguana/reflection.hpp index fdefa2bc..e1524e60 100644 --- a/iguana/reflection.hpp +++ b/iguana/reflection.hpp @@ -563,6 +563,10 @@ struct field_info { struct base { virtual void to_pb(std::string &str) {} virtual void from_pb(std::string_view str) {} + virtual void to_xml(std::string &str) {} + virtual void from_xml(std::string_view str) {} + virtual void to_json(std::string &str) {} + virtual void from_json(std::string_view str) {} virtual std::vector get_fields_name() { return {}; } virtual iguana::detail::field_info get_field_info(std::string_view name) { return {}; @@ -756,6 +760,8 @@ namespace iguana { MAKE_META_DATA(STRUCT_NAME, TABLE_NAME, GET_ARG_COUNT(__VA_ARGS__), \ __VA_ARGS__) +struct iguana_adl_t {}; + template inline auto iguana_reflect_type(const T &t); diff --git a/iguana/xml_reader.hpp b/iguana/xml_reader.hpp index 209b4f89..2047eadc 100644 --- a/iguana/xml_reader.hpp +++ b/iguana/xml_reader.hpp @@ -25,7 +25,7 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, std::string_view name); template , int> = 0> -IGUANA_INLINE void parse_value(U &&value, It &&begin, It &&end) { +IGUANA_INLINE void xml_parse_value(U &&value, It &&begin, It &&end) { using T = std::decay_t; if constexpr (string_container_v) { if constexpr (string_view_v) { @@ -70,8 +70,8 @@ IGUANA_INLINE void parse_value(U &&value, It &&begin, It &&end) { static constexpr auto str_to_enum = get_enum_map(); if constexpr (bool_v) { // not defined a specialization template - parse_value(reinterpret_cast &>(value), begin, - end); + xml_parse_value(reinterpret_cast &>(value), + begin, end); } else { auto enum_names = std::string_view( @@ -86,6 +86,12 @@ IGUANA_INLINE void parse_value(U &&value, It &&begin, It &&end) { } } } + +template , int> = 0> +IGUANA_INLINE void xml_parse_value(U &&value, It &&begin, It &&end) { + xml_parse_value(value.val, begin, end); +} + template , int> = 0> IGUANA_INLINE void parse_attr(U &&value, It &&it, It &&end) { @@ -98,7 +104,7 @@ IGUANA_INLINE void parse_attr(U &&value, It &&it, It &&end) { auto key_begin = it; auto key_end = skip_pass<'='>(it, end); key_type key; - parse_value(key, key_begin, key_end); + xml_parse_value(key, key_begin, key_end); skip_sapces_and_newline(it, end); auto value_begin = it + 1; @@ -115,7 +121,7 @@ IGUANA_INLINE void parse_attr(U &&value, It &&it, It &&end) { else IGUANA_UNLIKELY { throw std::runtime_error("expected quote or apos"); } value_type v; - parse_value(v, value_begin, value_end); + xml_parse_value(v, value_begin, value_end); value.emplace(std::move(key), std::move(v)); } } @@ -128,10 +134,16 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, skip_sapces_and_newline(it, end); auto value_begin = it; auto value_end = skip_pass<'<'>(it, end); - parse_value(value, value_begin, value_end); + xml_parse_value(value, value_begin, value_end); match_close_tag(it, end, name); } +template , int> = 0> +IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, + std::string_view name) { + parse_item(value.val, it, end, name); +} + template , int> = 0> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, @@ -159,6 +171,19 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, } } +template , int> = 0> +IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, + std::string_view name) { + throw std::bad_function_call(); +} + +template , int> = 0> +IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, + std::string_view name) { + throw std::bad_function_call(); +} + template , int>> IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, std::string_view name) { @@ -182,7 +207,7 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, match_close_tag(it, end, name); return; } - parse_value(value.emplace(), value_begin, value_end); + xml_parse_value(value.emplace(), value_begin, value_end); match_close_tag(it, end, name); } else { @@ -494,8 +519,14 @@ IGUANA_INLINE void from_xml(U &value, const View &view) { template , int> = 0> IGUANA_INLINE Num get_number(std::string_view str) { Num num; - detail::parse_value(num, str.begin(), str.end()); + detail::xml_parse_value(num, str.begin(), str.end()); return num; } +template +IGUANA_INLINE void from_xml_adl(iguana_adl_t *p, T &t, + std::string_view pb_str) { + iguana::from_xml(t, pb_str); +} + } // namespace iguana \ No newline at end of file diff --git a/iguana/xml_util.hpp b/iguana/xml_util.hpp index dabd47a3..8259e176 100644 --- a/iguana/xml_util.hpp +++ b/iguana/xml_util.hpp @@ -1,4 +1,5 @@ #pragma once +#include "detail/pb_type.hpp" #include "util.hpp" namespace iguana { diff --git a/iguana/xml_writer.hpp b/iguana/xml_writer.hpp index 94e35f37..f9d5009d 100644 --- a/iguana/xml_writer.hpp +++ b/iguana/xml_writer.hpp @@ -63,6 +63,11 @@ template , int> = 0> +IGUANA_INLINE void render_xml_value(Stream &ss, const T &value, + std::string_view name); + template , int> = 0> IGUANA_INLINE void render_xml_value(Stream &ss, T &&t, std::string_view name); @@ -133,6 +138,12 @@ IGUANA_INLINE void render_value(Stream &ss, const T &value) { } } +template , int> = 0> +IGUANA_INLINE void render_value(Stream &ss, const T &value) { + render_value(ss, value); +} + template , int> = 0> inline void render_xml_attr(Stream &ss, const T &value, std::string_view name) { @@ -160,6 +171,20 @@ IGUANA_INLINE void render_xml_value(Stream &ss, const T &value, render_tail(ss, name); } +template >, int> = 0> +IGUANA_INLINE void render_xml_value(Stream &ss, const T &value, + std::string_view name) { + render_xml_value(ss, value.val, name); +} + +template >, int> = 0> +IGUANA_INLINE void render_xml_value(Stream &ss, const T &value, + std::string_view name) { + throw std::bad_function_call(); +} + template , int> = 0> IGUANA_INLINE void render_xml_value(Stream &ss, const T &value, @@ -209,6 +234,20 @@ IGUANA_INLINE void render_xml_value(Stream &ss, const T &value, } } +template , int>> +IGUANA_INLINE void render_xml_value(Stream &ss, const T &value, + std::string_view name) { + throw std::bad_function_call(); +} + +template , int>> +IGUANA_INLINE void render_xml_value(Stream &ss, const T &value, + std::string_view name) { + throw std::bad_function_call(); +} + template , int>> IGUANA_INLINE void render_xml_value(Stream &ss, T &&t, std::string_view name) { @@ -267,4 +306,9 @@ IGUANA_INLINE void to_xml(T &&t, Stream &s) { render_xml_value(s, std::forward(t), root_name); } +template +IGUANA_INLINE void to_xml_adl(iguana_adl_t *p, T &t, std::string &pb_str) { + to_xml(t, pb_str); +} + } // namespace iguana diff --git a/iguana/yaml_reader.hpp b/iguana/yaml_reader.hpp index 0a0d47fa..49a31953 100644 --- a/iguana/yaml_reader.hpp +++ b/iguana/yaml_reader.hpp @@ -259,7 +259,7 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces) { } if constexpr (plain_v) { auto start = it; - auto value_end = skip_till<',', ']'>(it, end); + auto value_end = yaml_skip_till<',', ']'>(it, end); parse_value(value.emplace_back(), start, value_end); if (*(it - 1) == ']') IGUANA_UNLIKELY { return; } @@ -316,7 +316,7 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces) { skip_space_and_lines(it, end, min_spaces); if constexpr (plain_v) { auto start = it; - auto value_end = skip_till<',', ']'>(it, end); + auto value_end = yaml_skip_till<',', ']'>(it, end); parse_value(v, start, value_end); } else { @@ -370,13 +370,13 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces) { return; } auto start = it; - auto value_end = skip_till<':'>(it, end); + auto value_end = yaml_skip_till<':'>(it, end); key_type key; parse_value(key, start, value_end); subspaces = skip_space_and_lines(it, end, min_spaces); if constexpr (plain_v) { start = it; - value_end = skip_till<',', '}'>(it, end); + value_end = yaml_skip_till<',', '}'>(it, end); parse_value(value[key], start, value_end); if (*(it - 1) == '}') IGUANA_UNLIKELY { return; } @@ -399,7 +399,7 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, size_t min_spaces) { return; } auto start = it; - auto value_end = skip_till<':'>(it, end); + auto value_end = yaml_skip_till<':'>(it, end); key_type key; parse_value(key, start, value_end); subspaces = skip_space_and_lines(it, end, min_spaces); @@ -494,7 +494,7 @@ IGUANA_INLINE void from_yaml(T &value, It &&it, It &&end, size_t min_spaces) { auto spaces = skip_space_and_lines(it, end, min_spaces); while (it != end) { auto start = it; - auto keyend = skip_till<':'>(it, end); + auto keyend = yaml_skip_till<':'>(it, end); std::string_view key = std::string_view{ &*start, static_cast(std::distance(start, keyend))}; static constexpr auto frozen_map = get_iguana_struct_map(); diff --git a/iguana/yaml_util.hpp b/iguana/yaml_util.hpp index 0f2be370..b1b93a6a 100644 --- a/iguana/yaml_util.hpp +++ b/iguana/yaml_util.hpp @@ -1,5 +1,6 @@ #pragma once +#include "detail/pb_type.hpp" #include "util.hpp" namespace iguana { @@ -42,7 +43,7 @@ IGUANA_INLINE auto skip_till_newline(It &&it, It &&end) { } template -IGUANA_INLINE auto skip_till(It &&it, It &&end) { +IGUANA_INLINE auto yaml_skip_till(It &&it, It &&end) { if (it == end) IGUANA_UNLIKELY { return it; } std::decay_t res = it; @@ -93,7 +94,7 @@ IGUANA_INLINE size_t skip_space_and_lines(It &&it, It &&end, size_t minspaces) { // skip the --- line if ((it != end) && (*it == '-')) IGUANA_UNLIKELY { - auto line_end = skip_till(it, end); + auto line_end = yaml_skip_till(it, end); auto line = std::string_view( &*start, static_cast(std::distance(start, line_end))); if (line != "---") { diff --git a/test/proto/unittest_proto3.h b/test/proto/unittest_proto3.h index c1123b7f..4311e986 100644 --- a/test/proto/unittest_proto3.h +++ b/test/proto/unittest_proto3.h @@ -1,4 +1,5 @@ #pragma once +#include "iguana/dynamic.hpp" #include "iguana/pb_reader.hpp" #include "iguana/pb_writer.hpp" #if defined(STRUCT_PB_WITH_PROTO) diff --git a/test/test_pb.cpp b/test/test_pb.cpp index ae5d999b..1e06a492 100644 --- a/test/test_pb.cpp +++ b/test/test_pb.cpp @@ -3,9 +3,7 @@ #define DOCTEST_CONFIG_IMPLEMENT #include "doctest.h" -#include "iguana/pb_reader.hpp" -#include "iguana/pb_writer.hpp" - +#include "iguana/iguana.hpp" #if defined(__clang__) || defined(_MSC_VER) || \ (defined(__GNUC__) && __GNUC__ > 8) void print_hex_str(const std::string &str) { @@ -279,6 +277,24 @@ TEST_CASE("test reflection") { pair_t *st = dynamic_cast(t.get()); CHECK(st->x == t1.x); CHECK(st->y == t1.y); + std::string xml; + st->to_xml(xml); + std::cout << xml << "\n"; + pair_t s; + s.from_xml(xml); + std::cout << s.x << " " << s.y << "\n"; + CHECK(st->x == s.x); + CHECK(st->y == s.y); + + std::string json; + t->to_json(json); + std::cout << json << "\n"; + + s = {}; + s.from_json(json); + std::cout << s.x << " " << s.y << "\n"; + CHECK(st->x == s.x); + CHECK(st->y == s.y); } auto t = iguana::create_instance("numer_st"); t->set_field_value("a", true); diff --git a/test/test_proto3.cpp b/test/test_proto3.cpp index 27da52e7..c5227686 100644 --- a/test/test_proto3.cpp +++ b/test/test_proto3.cpp @@ -1,5 +1,6 @@ #define DOCTEST_CONFIG_IMPLEMENT #include "doctest.h" +#include "iguana/iguana.hpp" #include "proto/unittest_proto3.h" // msg reflection #if defined(STRUCT_PB_WITH_PROTO)