diff --git a/include/struct_pack/struct_pack/reflection.h b/include/struct_pack/struct_pack/reflection.h index f19e1db76..ebae1a06b 100644 --- a/include/struct_pack/struct_pack/reflection.h +++ b/include/struct_pack/struct_pack/reflection.h @@ -16,6 +16,7 @@ #pragma once #include #include +#include #include #include #include @@ -120,13 +121,28 @@ namespace detail { container.resize(std::size_t{}); }; + + template + concept span = container && requires(Type t) { + t=Type{(typename Type::value_type*)nullptr ,std::size_t{} }; + t.subspan(std::size_t{},std::size_t{}); + }; + + + + template + concept dynamic_span = span && std::is_same_v,std::integral_constant>; + + template + concept static_span = span && !dynamic_span; + + #if __cpp_lib_span >= 202002L template concept continuous_container = - string ||(container && requires(Type container) { + string || (container && requires(Type container) { std::span{container}; - container.resize(std::size_t{}); }); #else diff --git a/include/struct_pack/struct_pack/struct_pack_impl.hpp b/include/struct_pack/struct_pack/struct_pack_impl.hpp index e89f9f7af..6104d7bf3 100644 --- a/include/struct_pack/struct_pack/struct_pack_impl.hpp +++ b/include/struct_pack/struct_pack/struct_pack_impl.hpp @@ -240,6 +240,11 @@ constexpr inline bool is_trivial_tuple = false; template constexpr inline bool is_trivial_tuple> = true; +template +[[noreturn]] constexpr T declval() { + unreachable(); +} + template constexpr auto get_types() { using T = std::remove_cvref_t; @@ -247,23 +252,24 @@ constexpr auto get_types() { std::is_same_v || container || optional || unique_ptr || variant || expected || array || c_array || std::is_same_v) { - return std::tuple{}; + return declval>(); } else if constexpr (tuple) { - return T{}; + return declval(); } else if constexpr (is_trivial_tuple) { - return T{}; + return declval(); } else if constexpr (pair) { - return std::tuple{}; + return declval< + std::tuple>(); } else if constexpr (std::is_aggregate_v) { // clang-format off return visit_members( - std::forward(T{}), [&](Args && - ...) CONSTEXPR_INLINE_LAMBDA { - return std::tuple...>{}; + declval(), [](Args && + ...) constexpr { + return declval...>>(); }); // clang-format on } @@ -567,7 +573,7 @@ consteval type_id get_type_id() { else if constexpr (string) { return type_id::string_t; } - else if constexpr (array || c_array) { + else if constexpr (array || c_array || static_span) { return type_id::array_t; } else if constexpr (map_container) { @@ -695,6 +701,26 @@ consteval std::size_t check_circle() { } } +template +struct get_array_element { + using type = typename T::value_type; +}; + +template +struct get_array_element { + using type = T; +}; + +template +std::size_t consteval get_array_size() { + if constexpr (array || c_array) { + return sizeof(T) / sizeof(typename get_array_element::type); + } + else { + return T::extent; + } +} + // This help function is just to improve unit test coverage. :) // The original lambada in `get_type_literal` is a compile-time expression. // Currently, the unit test coverage tools like @@ -750,10 +776,8 @@ consteval decltype(auto) get_type_literal() { return ret + body + end; } else if constexpr (id == type_id::array_t) { - constexpr auto sz = - sizeof(Arg) / - sizeof(decltype(std::declval< - Arg>()[0])); // std::size(std::declval()); + constexpr auto sz = get_array_size(); + static_assert(sz > 0, "The array's size must greater than zero!"); return ret + get_type_literal< std::remove_cvref_t()[0])>, Arg, @@ -910,7 +934,8 @@ constexpr bool check_if_compatible_element_exist_impl_helper() { } else if constexpr (id == type_id::array_t) { return check_if_compatible_element_exist_impl_helper< - version, std::remove_cvref_t, T, ParentArgs...>(); + version, std::remove_cvref_t::type>, T, + ParentArgs...>(); } else if constexpr (id == type_id::map_container_t) { return check_if_compatible_element_exist_impl_helper< @@ -978,7 +1003,7 @@ constexpr size_info inline calculate_one_size(const T &item) { using type = std::remove_cvref_t; static_assert(!std::is_pointer_v); size_info ret{.total = 0, .size_cnt = 0, .max_size = 0}; - if constexpr (std::is_same_v) { + if constexpr (id == type_id::monostate_t) { } else if constexpr (std::is_fundamental_v || std::is_enum_v) { ret.total = sizeof(type); @@ -986,7 +1011,7 @@ constexpr size_info inline calculate_one_size(const T &item) { else if constexpr (detail::varint_t) { ret.total = detail::calculate_varint_size(item); } - else if constexpr (c_array || array) { + else if constexpr (id == type_id::array_t) { if constexpr (is_trivial_serializable::value) { ret.total = sizeof(type); } @@ -1141,7 +1166,8 @@ constexpr std::size_t calculate_compatible_version_size() { } else if constexpr (id == type_id::array_t) { return calculate_compatible_version_size< - std::remove_cvref_t, T, ParentArgs...>(); + std::remove_cvref_t::type>, T, + ParentArgs...>(); } else if constexpr (id == type_id::map_container_t) { return calculate_compatible_version_size, T, - ParentArgs...>(buffer, sz); + get_compatible_version_numbers< + std::remove_cvref_t::type>, T, + ParentArgs...>(buffer, sz); } else if constexpr (id == type_id::map_container_t) { get_compatible_version_numbers( @@ -1495,7 +1522,7 @@ class packer { else if constexpr (detail::varint_t) { detail::serialize_varint(writer_, item); } - else if constexpr (c_array || array) { + else if constexpr (id == type_id::array_t) { if constexpr (is_trivial_serializable::value) { writer_.write((char *)&item, sizeof(type)); } @@ -1612,7 +1639,7 @@ class packer { } } } - else if constexpr (c_array || array) { + else if constexpr (id == type_id::array_t) { for (const auto &i : item) { serialize_one(i); } @@ -2215,7 +2242,7 @@ class unpacker { else if constexpr (detail::varint_t) { code = detail::deserialize_varint(reader_, item); } - else if constexpr (array || c_array) { + else if constexpr (id == type_id::array_t) { if constexpr (is_trivial_serializable::value) { if constexpr (NotSkip) { if (!reader_.read((char *)&item, sizeof(type))) [[unlikely]] { @@ -2308,11 +2335,11 @@ class unpacker { if constexpr (trivially_copyable_container) { size_t mem_sz = size * sizeof(value_type); if constexpr (NotSkip) { - if constexpr (string_view) { + if constexpr (string_view || dynamic_span) { static_assert( view_reader_t, "The Reader isn't a view_reader, can't deserialize " - "a string_view"); + "a string_view/span"); const char *view = reader_.read_view(mem_sz); if (view == nullptr) [[unlikely]] { return struct_pack::errc::no_buffer_space; @@ -2336,11 +2363,18 @@ class unpacker { } else { if constexpr (NotSkip) { - item.resize(size); - for (auto &i : item) { - code = deserialize_one(i); - if (code != struct_pack::errc{}) [[unlikely]] { - return code; + if constexpr (dynamic_span) { + static_assert(!dynamic_span, + "It's illegal to deserialize a span which T " + "is a non-trival-serializable type."); + } + else { + item.resize(size); + for (auto &i : item) { + code = deserialize_one(i); + if (code != struct_pack::errc{}) [[unlikely]] { + return code; + } } } } @@ -2457,7 +2491,7 @@ class unpacker { } } } - else if constexpr (array || c_array) { + else if constexpr (id == type_id::array_t) { for (auto &i : item) { code = deserialize_one(i); if (code != struct_pack::errc{}) [[unlikely]] { diff --git a/src/struct_pack/tests/test_serialize.cpp b/src/struct_pack/tests/test_serialize.cpp index f109cadd1..14c4484a3 100644 --- a/src/struct_pack/tests/test_serialize.cpp +++ b/src/struct_pack/tests/test_serialize.cpp @@ -549,17 +549,17 @@ TEST_CASE("test get type code") { SUBCASE("test get_types") { using namespace struct_pack::detail; - auto t = get_types(); - static_assert(std::is_same_v, decltype(t)>); + static_assert(std::is_same_v, + decltype(get_types())>); - auto t1 = get_types>(); - static_assert(std::is_same_v, decltype(t1)>); + static_assert( + std::is_same_v, + decltype(get_types>())>); - auto t2 = get_types(); - static_assert(std::is_same_v, decltype(t2)>); + static_assert(std::is_same_v, decltype(get_types())>); - auto t3 = get_types>(); - static_assert(std::is_same_v>, decltype(t3)>); + static_assert(std::is_same_v>, + decltype(get_types>())>); static_assert(std::is_same_v()), std::tuple>); @@ -1031,3 +1031,232 @@ TEST_CASE("compatible convert to optional") { CHECK(b.value() == "hello world"); CHECK(a.value() == "hello world"); } + +#if __cpp_lib_span >= 202002L + +struct span_test { + std::string hello; + std::span sp; +}; +template <> +constexpr std::size_t struct_pack::members_count = 2; +struct span_test2 { + std::string hello; + std::array ar; +}; + +struct span_test3 { + std::string hello; + std::span sp; +}; +template <> +constexpr std::size_t struct_pack::members_count = 2; +struct span_test4 { + std::string hello; + std::array ar; +}; + +struct span_test5 { + std::string hello; + std::span sp; +}; +template <> +constexpr std::size_t struct_pack::members_count = 2; +struct span_test6 { + std::string hello; + std::array ar; +}; + +TEST_CASE("test static span") { + SUBCASE("test static span") { + { + std::array ar = {1, 4, 5, 3}; + span_test s = {.hello = "Hello", .sp = ar}; + auto buffer = struct_pack::serialize(s); + span_test2 s2; + auto res = struct_pack::deserialize(buffer); + CHECK(res.has_value()); + CHECK(res.value().hello == s.hello); + CHECK(res.value().ar == ar); + } + { + std::array ar = {1, 4, 5, 32}, ar2; + span_test s = {.hello = "Hello", .sp = ar}; + auto buffer = struct_pack::serialize(s); + span_test s2 = {.sp = ar2}; + auto ec = struct_pack::deserialize_to(s2, buffer); + CHECK(ec == struct_pack::errc{}); + CHECK(s.hello == s2.hello); + CHECK(ar == ar2); + } + { + std::array ar2; + span_test2 s = {.hello = "Hello", .ar = {1, 4, 5, 3}}; + auto buffer = struct_pack::serialize(s); + span_test s2 = {.sp = ar2}; + auto ec = struct_pack::deserialize_to(s2, buffer); + CHECK(ec == struct_pack::errc{}); + CHECK(s.hello == s2.hello); + CHECK(s.ar == ar2); + } + } + SUBCASE("test static span") { + { + std::array ar = {"1", "14", "145", "1453"}; + span_test3 s = {.hello = "Hello", .sp = ar}; + auto buffer = struct_pack::serialize(s); + auto res = struct_pack::deserialize(buffer); + CHECK(res.has_value()); + CHECK(res.value().hello == s.hello); + CHECK(res.value().ar == ar); + } + { + std::array ar = {"1", "14", "145", "1453"}, ar2; + span_test3 s = {.hello = "Hello", .sp = ar}; + auto buffer = struct_pack::serialize(s); + span_test3 s2 = {.sp = ar2}; + auto ec = struct_pack::deserialize_to(s2, buffer); + CHECK(ec == struct_pack::errc{}); + CHECK(s.hello == s2.hello); + CHECK(ar == ar2); + } + { + std::array ar2; + span_test4 s = {.hello = "Hello", .ar = {"1", "14", "145", "1453"}}; + auto buffer = struct_pack::serialize(s); + span_test3 s2 = {.sp = ar2}; + auto ec = struct_pack::deserialize_to(s2, buffer); + CHECK(ec == struct_pack::errc{}); + CHECK(s.hello == s2.hello); + CHECK(s.ar == ar2); + } + } + SUBCASE("test static span") { + { + std::array ar = {person{.age = 24, .name = "Betty"}, + person{.age = 241, .name = "Betty2"}, + person{.age = 2414, .name = "Betty3"}, + person{.age = 24141, .name = "Betty4"}}; + span_test5 s = {.hello = "Hello", .sp = ar}; + auto buffer = struct_pack::serialize(s); + auto res = struct_pack::deserialize(buffer); + CHECK(res.has_value()); + CHECK(res.value().hello == s.hello); + CHECK(res.value().ar == ar); + } + { + std::array ar = {person{.age = 24, .name = "Betty"}, + person{.age = 241, .name = "Betty2"}, + person{.age = 2414, .name = "Betty3"}, + person{.age = 24141, .name = "Betty4"}}, + ar2; + span_test5 s = {.hello = "Hello", .sp = ar}; + auto buffer = struct_pack::serialize(s); + span_test5 s2 = {.sp = ar2}; + auto ec = struct_pack::deserialize_to(s2, buffer); + CHECK(ec == struct_pack::errc{}); + CHECK(s.hello == s2.hello); + CHECK(ar == ar2); + } + { + std::array ar2; + span_test6 s = {.hello = "Hello", + .ar = {person{.age = 24, .name = "Betty"}, + person{.age = 241, .name = "Betty2"}, + person{.age = 2414, .name = "Betty3"}, + person{.age = 24141, .name = "Betty4"}}}; + auto buffer = struct_pack::serialize(s); + span_test5 s2 = {.sp = ar2}; + auto ec = struct_pack::deserialize_to(s2, buffer); + CHECK(ec == struct_pack::errc{}); + CHECK(s.hello == s2.hello); + CHECK(s.ar == ar2); + } + } +} + +struct dspan_test { + std::string hello; + std::span sp; +}; +struct dspan_test2 { + std::string hello; + std::vector ar; +}; +struct dspan_test3 { + std::string hello; + std::span sp; +}; +struct dspan_test4 { + std::string hello; + std::vector ar; +}; +struct dspan_test5 { + std::string hello; + std::span sp; +}; +struct dspan_test6 { + std::string hello; + std::vector ar; +}; + +TEST_CASE("test dynamic span") { + SUBCASE("test dynamic span") { + { + std::vector ar = {1, 4, 5, 3}; + dspan_test s = {.hello = "Hello", .sp = ar}; + auto buffer = struct_pack::serialize(s); + dspan_test2 s2; + auto res = struct_pack::deserialize(buffer); + CHECK(res.has_value()); + CHECK(res.value().hello == s.hello); + CHECK(res.value().ar == ar); + } + { + std::vector ar = {1, 4, 5, 32}; + dspan_test s = {.hello = "Hello", .sp = ar}; + auto buffer = struct_pack::serialize(s); + dspan_test s2; + auto ec = struct_pack::deserialize_to(s2, buffer); + CHECK(ec == struct_pack::errc{}); + CHECK(s.hello == s2.hello); + CHECK(std::equal(ar.begin(), ar.end(), s2.sp.begin())); + } + { + dspan_test2 s = {.hello = "Hello", .ar = {1, 4, 5, 3}}; + auto buffer = struct_pack::serialize(s); + dspan_test s2; + auto ec = struct_pack::deserialize_to(s2, buffer); + CHECK(ec == struct_pack::errc{}); + CHECK(s.hello == s2.hello); + CHECK(std::equal(s.ar.begin(), s.ar.end(), s2.sp.begin())); + } + } + SUBCASE("test dynamic span") { + { + std::vector ar = {"1", "14", "145", "1453"}; + dspan_test3 s = {.hello = "Hello", .sp = ar}; + auto buffer = struct_pack::serialize(s); + auto res = struct_pack::deserialize(buffer); + CHECK(res.has_value()); + CHECK(res.value().hello == s.hello); + CHECK(res.value().ar == ar); + } + } + SUBCASE("test dynamic span") { + { + std::vector ar = {person{.age = 24, .name = "Betty"}, + person{.age = 241, .name = "Betty2"}, + person{.age = 2414, .name = "Betty3"}, + person{.age = 24141, .name = "Betty4"}}; + dspan_test5 s = {.hello = "Hello", .sp = ar}; + auto buffer = struct_pack::serialize(s); + auto res = struct_pack::deserialize(buffer); + CHECK(res.has_value()); + CHECK(res.value().hello == s.hello); + CHECK(res.value().ar == ar); + } + } +} + +#endif \ No newline at end of file