diff --git a/include/ylt/struct_pack/md5_constexpr.hpp b/include/ylt/struct_pack/md5_constexpr.hpp index 027a94bc7..40e328b97 100644 --- a/include/ylt/struct_pack/md5_constexpr.hpp +++ b/include/ylt/struct_pack/md5_constexpr.hpp @@ -38,11 +38,11 @@ struct string_literal { constexpr bool empty() const { return !Size; } - constexpr char &operator[](std::size_t sz) { return ar[sz]; } + constexpr CharType &operator[](std::size_t sz) { return ar[sz]; } constexpr const char &operator[](std::size_t sz) const { return ar[sz]; } - constexpr const char *data() const { return &ar[0]; } + constexpr const CharType *data() const { return &ar[0]; } private: CharType ar[Size + 1]; diff --git a/include/ylt/struct_pack/reflection.hpp b/include/ylt/struct_pack/reflection.hpp index 4ab56e98b..ff70612aa 100644 --- a/include/ylt/struct_pack/reflection.hpp +++ b/include/ylt/struct_pack/reflection.hpp @@ -49,6 +49,11 @@ using remove_cvref_t = std::remove_cv_t>; #endif } +template +[[noreturn]] constexpr T declval() { + unreachable(); +} + template constexpr auto get_types(); @@ -84,6 +89,28 @@ constexpr std::size_t alignment_v = 0; #if __cpp_concepts >= 201907L +template +concept has_user_defined_id = requires { + typename std::integral_constant; +}; + +#else + +template +struct has_user_defined_id_impl : std::false_type {}; + +template +struct has_user_defined_id_impl< + T, std::void_t>> + : std::true_type {}; + +template +constexpr bool has_user_defined_id = has_user_defined_id_impl::value; + +#endif + +#if __cpp_concepts >= 201907L + template concept writer_t = requires(T t) { t.write((const char *)nullptr, std::size_t{}); diff --git a/include/ylt/struct_pack/struct_pack_impl.hpp b/include/ylt/struct_pack/struct_pack_impl.hpp index 8e29923c1..7e452801f 100644 --- a/include/ylt/struct_pack/struct_pack_impl.hpp +++ b/include/ylt/struct_pack/struct_pack_impl.hpp @@ -222,11 +222,6 @@ template constexpr inline bool is_trivial_tuple> = true; #endif -template -[[noreturn]] constexpr T declval() { - unreachable(); -} - template constexpr auto get_types() { using T = remove_cvref_t; @@ -347,6 +342,8 @@ enum class type_id { monostate_t = 250, // circle_flag circle_flag = 251, + // end helper with user defined type ID + type_end_flag_with_id = 252, trivial_class_t = 253, // struct type non_trivial_class_t = 254, @@ -915,11 +912,18 @@ constexpr void calculate_trival_obj_size::operator()( } // namespace align -// 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 -// [Coverage](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html) -// can not detect code that is run at compile time. +template +constexpr decltype(auto) get_type_end_flag() { + if constexpr (has_user_defined_id) { + return string_literal{ + {static_cast(type_id::type_end_flag_with_id)}} + + get_size_literal(); + } + else { + return string_literal{{static_cast(type_id::type_end_flag)}}; + } +} + template constexpr decltype(auto) get_type_literal(std::index_sequence); @@ -941,10 +945,11 @@ constexpr decltype(auto) get_type_literal() { } else { constexpr auto id = get_type_id(); - constexpr auto ret = string_literal{{static_cast(id)}}; + constexpr auto begin = string_literal{{static_cast(id)}}; if constexpr (id == type_id::non_trivial_class_t || id == type_id::trivial_class_t) { using Args = decltype(get_types()); + constexpr auto end = get_type_end_flag(); constexpr auto body = get_type_literal( std::make_index_sequence>()); if constexpr (is_trivial_serializable::value) { @@ -952,15 +957,12 @@ constexpr decltype(auto) get_type_literal() { align::pack_alignment_v <= align::alignment_v, "If you add #pragma_pack to a struct, please specify the " "struct_pack::pack_alignment_v."); - constexpr auto end = string_literal{ - {static_cast(type_id::type_end_flag)}}; - return ret + body + get_size_literal>() + + return begin + body + + get_size_literal>() + get_size_literal>() + end; } else { - constexpr auto end = string_literal{ - {static_cast(type_id::type_end_flag)}}; - return ret + body + end; + return begin + body + end; } } else if constexpr (id == type_id::variant_t) { @@ -971,12 +973,12 @@ constexpr decltype(auto) get_type_literal() { std::make_index_sequence>()); constexpr auto end = string_literal{ {static_cast(type_id::type_end_flag)}}; - return ret + body + end; + return begin + body + end; } else if constexpr (id == type_id::array_t) { constexpr auto sz = get_array_size(); static_assert(sz > 0, "The array's size must greater than zero!"); - return ret + + return begin + get_type_literal< remove_cvref_t()[0])>, Arg, ParentArgs...>() + @@ -985,38 +987,39 @@ constexpr decltype(auto) get_type_literal() { else if constexpr (id == type_id::bitset_t) { constexpr auto sz = get_array_size(); static_assert(sz > 0, "The array's size must greater than zero!"); - return ret + get_size_literal(); + return begin + get_size_literal(); } else if constexpr (unique_ptr) { - return ret + + return begin + get_type_literal, Arg, ParentArgs...>(); } else if constexpr (id == type_id::container_t || id == type_id::optional_t || id == type_id::string_t) { - return ret + get_type_literal, - Arg, ParentArgs...>(); + return begin + + get_type_literal, Arg, + ParentArgs...>(); } else if constexpr (id == type_id::set_container_t) { - return ret + get_type_literal, - Arg, ParentArgs...>(); + return begin + get_type_literal, + Arg, ParentArgs...>(); } else if constexpr (id == type_id::map_container_t) { - return ret + + return begin + get_type_literal, Arg, ParentArgs...>() + get_type_literal, Arg, ParentArgs...>(); } else if constexpr (id == type_id::expected_t) { - return ret + + return begin + get_type_literal, Arg, ParentArgs...>() + get_type_literal, Arg, ParentArgs...>(); } else if constexpr (id != type_id::compatible_t) { - return ret; + return begin; } else { return string_literal{}; @@ -1054,10 +1057,9 @@ constexpr decltype(auto) get_types_literal() { } else { constexpr auto root_id = get_type_id>(); - constexpr auto end = - string_literal{{static_cast(type_id::type_end_flag)}}; if constexpr (root_id == type_id::non_trivial_class_t || root_id == type_id::trivial_class_t) { + constexpr auto end = get_type_end_flag>(); constexpr auto begin = string_literal{{static_cast(root_id)}}; constexpr auto body = get_types_literal_impl(); diff --git a/src/struct_pack/tests/test_compile_time_calculate.cpp b/src/struct_pack/tests/test_compile_time_calculate.cpp index 25a365900..d90864773 100644 --- a/src/struct_pack/tests/test_compile_time_calculate.cpp +++ b/src/struct_pack/tests/test_compile_time_calculate.cpp @@ -1,4 +1,6 @@ +#include #include +#include #include #include "doctest.h" @@ -347,3 +349,83 @@ TEST_CASE("type calculate") { serialize(std::tuple>{}))); } } + +struct foo_trivial { + int a; + double b; +}; + +struct foo_trivial_with_ID { + int a; + double b; + constexpr static std::size_t struct_pack_id = 1; +}; +struct foo_trivial_with_ID2 { + int a; + double b; + constexpr static int struct_pack_id = 2; +}; +struct bar_trivial_with_ID2 { + uint32_t a; + double b; + constexpr static int struct_pack_id = 2; +}; + +struct foo { + std::vector a; + std::list b; +}; +struct bar; +struct bar { + std::vector a; + std::vector b; +}; + +struct foo_with_ID { + std::vector a; + std::list b; + constexpr static std::size_t struct_pack_id = 0; +}; + +struct bar_with_ID { + std::vector a; + std::map b; + constexpr static std::size_t struct_pack_id = 0; +}; + +struct foo_with_ID1 { + std::vector a; + std::list b; + constexpr static std::size_t struct_pack_id = 1; +}; + +struct bar_with_ID1 { + std::vector a; + std::vector b; + constexpr static std::size_t struct_pack_id = 1; +}; + +TEST_CASE("test user defined ID") { + { + static_assert(has_user_defined_id); + static_assert(struct_pack::get_type_literal() != + struct_pack::get_type_literal()); + static_assert(struct_pack::get_type_literal() != + struct_pack::get_type_literal()); + static_assert(struct_pack::get_type_literal() != + struct_pack::get_type_literal()); + } + { + static_assert(has_user_defined_id); + static_assert(struct_pack::get_type_literal() != + struct_pack::get_type_literal()); + static_assert(struct_pack::get_type_literal() != + struct_pack::get_type_literal()); + auto a = struct_pack::get_type_literal(); + auto b = struct_pack::get_type_literal(); + static_assert(struct_pack::get_type_literal() == + struct_pack::get_type_literal()); + static_assert(struct_pack::get_type_literal() != + struct_pack::get_type_literal()); + } +}