diff --git a/include/ylt/struct_pack.hpp b/include/ylt/struct_pack.hpp index 2f291f4e9..5afa10274 100644 --- a/include/ylt/struct_pack.hpp +++ b/include/ylt/struct_pack.hpp @@ -68,7 +68,7 @@ using unexpected = tl::unexpected; using unexpect_t = tl::unexpect_t; #endif -STRUCT_PACK_INLINE std::error_code make_error_code(struct_pack::errc err) { +inline std::error_code make_error_code(struct_pack::errc err) { return std::error_code(static_cast(err), struct_pack::detail::category()); } @@ -76,9 +76,9 @@ STRUCT_PACK_INLINE std::error_code make_error_code(struct_pack::errc err) { /*! * \defgroup struct_pack struct_pack * - * \brief yaLanTingLibs struct_pack 序列化库 + * \brief yaLanTingLibs struct_pack + * * - * coro_rpc分为服务端和客户端,服务端包括rpc函数注册API和服务器对象的API,客户端包括rpc调用API。 * */ @@ -88,21 +88,12 @@ STRUCT_PACK_INLINE std::error_code make_error_code(struct_pack::errc err) { * @param err error code. * @return error message. */ -STRUCT_PACK_INLINE std::string error_message(struct_pack::errc err) { +inline std::string error_message(struct_pack::errc err) { return struct_pack::make_error_code(err).message(); } -/*! - * \ingroup struct_pack - * Get the byte size of the packing objects. - * TODO: add doc - * @tparam Args the types of packing objects. - * @param args the packing objects. - * @return byte size. - */ - template -STRUCT_PACK_INLINE constexpr std::uint32_t get_type_code() { +constexpr std::uint32_t get_type_code() { static_assert(sizeof...(Args) > 0); std::uint32_t ret = 0; if constexpr (sizeof...(Args) == 1) { @@ -121,7 +112,7 @@ STRUCT_PACK_INLINE constexpr std::uint32_t get_type_code() { } template -STRUCT_PACK_INLINE constexpr decltype(auto) get_type_literal() { +constexpr decltype(auto) get_type_literal() { static_assert(sizeof...(Args) > 0); if constexpr (sizeof...(Args) == 1) { using Types = decltype(detail::get_types()); @@ -135,14 +126,23 @@ STRUCT_PACK_INLINE constexpr decltype(auto) get_type_literal() { } } +/*! + * \ingroup struct_pack + * Get the byte size of the packing objects. + * TODO: add doc + * @tparam Args the types of packing objects. + * @param args the packing objects. + * @return byte size. + */ + template -[[nodiscard]] STRUCT_PACK_INLINE constexpr serialize_buffer_size -get_needed_size(const Args &...args) { +[[nodiscard]] constexpr struct_pack::serialize_buffer_size get_needed_size( + const Args &...args) { return detail::get_serialize_runtime_info(args...); } template -STRUCT_PACK_INLINE void serialize_to(Writer &writer, const Args &...args) { +void serialize_to(Writer &writer, const Args &...args) { static_assert(sizeof...(args) > 0); if constexpr (struct_pack::writer_t) { auto info = detail::get_serialize_runtime_info(args...); @@ -166,8 +166,8 @@ STRUCT_PACK_INLINE void serialize_to(Writer &writer, const Args &...args) { } template -void STRUCT_PACK_INLINE serialize_to(char *buffer, serialize_buffer_size info, - const Args &...args) noexcept { +void serialize_to(char *buffer, serialize_buffer_size info, + const Args &...args) { static_assert(sizeof...(args) > 0); auto writer = struct_pack::detail::memory_writer{(char *)buffer}; struct_pack::detail::serialize_to(writer, info, args...); @@ -180,9 +180,8 @@ template -void STRUCT_PACK_INLINE serialize_to_with_offset(Buffer &buffer, - std::size_t offset, - const Args &...args) { +void serialize_to_with_offset(Buffer &buffer, std::size_t offset, + const Args &...args) { #if __cpp_concepts < 201907L static_assert(detail::struct_pack_buffer, "The buffer is not satisfied struct_pack_buffer requirement!"); @@ -203,7 +202,7 @@ template < typename Buffer = std::vector, #endif typename... Args> -[[nodiscard]] STRUCT_PACK_INLINE Buffer serialize(const Args &...args) { +[[nodiscard]] Buffer serialize(const Args &...args) { #if __cpp_concepts < 201907L static_assert(detail::struct_pack_buffer, "The buffer is not satisfied struct_pack_buffer requirement!"); @@ -221,8 +220,8 @@ template < typename Buffer = std::vector, #endif typename... Args> -[[nodiscard]] STRUCT_PACK_INLINE Buffer -serialize_with_offset(std::size_t offset, const Args &...args) { +[[nodiscard]] Buffer serialize_with_offset(std::size_t offset, + const Args &...args) { #if __cpp_concepts < 201907L static_assert(detail::struct_pack_buffer, "The buffer is not satisfied struct_pack_buffer requirement!"); @@ -240,7 +239,7 @@ template , #endif typename... Args> -[[nodiscard]] STRUCT_PACK_INLINE Buffer serialize(const Args &...args) { +[[nodiscard]] Buffer serialize(const Args &...args) { #if __cpp_concepts < 201907L static_assert(detail::struct_pack_buffer, "The buffer is not satisfied struct_pack_buffer requirement!"); @@ -258,8 +257,8 @@ template , #endif typename... Args> -[[nodiscard]] STRUCT_PACK_INLINE Buffer -serialize_with_offset(std::size_t offset, const Args &...args) { +[[nodiscard]] Buffer serialize_with_offset(std::size_t offset, + const Args &...args) { #if __cpp_concepts < 201907L static_assert(detail::struct_pack_buffer, "The buffer is not satisfied struct_pack_buffer requirement!"); @@ -272,15 +271,15 @@ serialize_with_offset(std::size_t offset, const Args &...args) { #if __cpp_concepts >= 201907L template + struct_pack::detail::deserialize_view View> #else template < uint64_t conf = sp_config::DEFAULT, typename T, typename... Args, typename View, typename = std::enable_if_t>> #endif -[[nodiscard]] STRUCT_PACK_INLINE struct_pack::errc deserialize_to( - T &t, const View &v, Args &...args) { +[[nodiscard]] struct_pack::errc deserialize_to(T &t, const View &v, + Args &...args) { detail::memory_reader reader{(const char *)v.data(), (const char *)v.data() + v.size()}; detail::unpacker in(reader); @@ -288,8 +287,8 @@ template < } template -[[nodiscard]] STRUCT_PACK_INLINE struct_pack::errc deserialize_to( - T &t, const char *data, size_t size, Args &...args) { +[[nodiscard]] struct_pack::errc deserialize_to(T &t, const char *data, + size_t size, Args &...args) { detail::memory_reader reader{data, data + size}; detail::unpacker in(reader); return in.deserialize(t, args...); @@ -303,8 +302,8 @@ template >> #endif -[[nodiscard]] STRUCT_PACK_INLINE struct_pack::errc deserialize_to( - T &t, Reader &reader, Args &...args) { +[[nodiscard]] struct_pack::errc deserialize_to(T &t, Reader &reader, + Args &...args) { detail::unpacker in(reader); std::size_t consume_len; auto old_pos = reader.tellg(); @@ -324,14 +323,16 @@ template = 201907L template + struct_pack::detail::deserialize_view View> #else -template >> +template < + uint64_t conf = sp_config::DEFAULT, typename T, typename... Args, + typename View, + typename = std::enable_if_t>> #endif -[[nodiscard]] STRUCT_PACK_INLINE struct_pack::errc deserialize_to( - T &t, const View &v, size_t &consume_len, Args &...args) { +[[nodiscard]] struct_pack::errc deserialize_to(T &t, const View &v, + size_t &consume_len, + Args &...args) { detail::memory_reader reader{(const char *)v.data(), (const char *)v.data() + v.size()}; detail::unpacker in(reader); @@ -346,8 +347,9 @@ template -[[nodiscard]] STRUCT_PACK_INLINE struct_pack::errc deserialize_to( - T &t, const char *data, size_t size, size_t &consume_len, Args &...args) { +[[nodiscard]] struct_pack::errc deserialize_to(T &t, const char *data, + size_t size, size_t &consume_len, + Args &...args) { detail::memory_reader reader{data, data + size}; detail::unpacker in(reader); auto ret = in.deserialize_with_len(consume_len, t, args...); @@ -362,13 +364,14 @@ template #if __cpp_concepts >= 201907L template + struct_pack::detail::deserialize_view View> #else template #endif -[[nodiscard]] STRUCT_PACK_INLINE struct_pack::errc deserialize_to_with_offset( - T &t, const View &v, size_t &offset, Args &...args) { +[[nodiscard]] struct_pack::errc deserialize_to_with_offset(T &t, const View &v, + size_t &offset, + Args &...args) { size_t sz; auto ret = deserialize_to(t, v.data() + offset, v.size() - offset, sz, args...); @@ -377,7 +380,7 @@ template -[[nodiscard]] STRUCT_PACK_INLINE struct_pack::errc deserialize_to_with_offset( +[[nodiscard]] struct_pack::errc deserialize_to_with_offset( T &t, const char *data, size_t size, size_t &offset, Args &...args) { size_t sz; auto ret = deserialize_to(t, data + offset, size - offset, sz, args...); @@ -386,12 +389,13 @@ template } #if __cpp_concepts >= 201907L -template +template #else -template >> +template < + typename... Args, typename View, + typename = std::enable_if_t>> #endif -[[nodiscard]] STRUCT_PACK_INLINE auto deserialize(const View &v) { +[[nodiscard]] auto deserialize(const View &v) { expected, struct_pack::errc> ret; auto errc = deserialize_to(ret.value(), v); if SP_UNLIKELY (errc != struct_pack::errc{}) { @@ -401,8 +405,7 @@ template -[[nodiscard]] STRUCT_PACK_INLINE auto deserialize(const char *data, - size_t size) { +[[nodiscard]] auto deserialize(const char *data, size_t size) { expected, struct_pack::errc> ret; if (auto errc = deserialize_to(ret.value(), data, size); errc != struct_pack::errc{}) { @@ -416,7 +419,7 @@ template template >> #endif -[[nodiscard]] STRUCT_PACK_INLINE auto deserialize(Reader &v) { +[[nodiscard]] auto deserialize(Reader &v) { expected, struct_pack::errc> ret; auto errc = deserialize_to(ret.value(), v); if SP_UNLIKELY (errc != struct_pack::errc{}) { @@ -426,12 +429,11 @@ template = 201907L -template +template #else template #endif -[[nodiscard]] STRUCT_PACK_INLINE auto deserialize(const View &v, - size_t &consume_len) { +[[nodiscard]] auto deserialize(const View &v, size_t &consume_len) { expected, struct_pack::errc> ret; auto errc = deserialize_to(ret.value(), v, consume_len); if SP_UNLIKELY (errc != struct_pack::errc{}) { @@ -441,8 +443,8 @@ template } template -[[nodiscard]] STRUCT_PACK_INLINE auto deserialize(const char *data, size_t size, - size_t &consume_len) { +[[nodiscard]] auto deserialize(const char *data, size_t size, + size_t &consume_len) { expected, struct_pack::errc> ret; auto errc = deserialize_to(ret.value(), data, size, consume_len); if SP_UNLIKELY (errc != struct_pack::errc{}) { @@ -452,12 +454,14 @@ template } #if __cpp_concepts >= 201907L -template +template #else -template >> +template < + uint64_t conf, typename... Args, typename View, + typename = std::enable_if_t>> #endif -[[nodiscard]] STRUCT_PACK_INLINE auto deserialize(const View &v) { +[[nodiscard]] auto deserialize(const View &v) { expected, struct_pack::errc> ret; auto errc = deserialize_to(ret.value(), v); if SP_UNLIKELY (errc != struct_pack::errc{}) { @@ -467,8 +471,7 @@ template -[[nodiscard]] STRUCT_PACK_INLINE auto deserialize(const char *data, - size_t size) { +[[nodiscard]] auto deserialize(const char *data, size_t size) { expected, struct_pack::errc> ret; if (auto errc = deserialize_to(ret.value(), data, size); errc != struct_pack::errc{}) { @@ -483,7 +486,7 @@ template template >> #endif -[[nodiscard]] STRUCT_PACK_INLINE auto deserialize(Reader &v) { +[[nodiscard]] auto deserialize(Reader &v) { expected, struct_pack::errc> ret; auto errc = deserialize_to(ret.value(), v); if SP_UNLIKELY (errc != struct_pack::errc{}) { @@ -493,12 +496,12 @@ template = 201907L -template +template #else template #endif -[[nodiscard]] STRUCT_PACK_INLINE auto deserialize(const View &v, - size_t &consume_len) { +[[nodiscard]] auto deserialize(const View &v, size_t &consume_len) { expected, struct_pack::errc> ret; auto errc = deserialize_to(ret.value(), v, consume_len); if SP_UNLIKELY (errc != struct_pack::errc{}) { @@ -508,8 +511,8 @@ template } template -[[nodiscard]] STRUCT_PACK_INLINE auto deserialize(const char *data, size_t size, - size_t &consume_len) { +[[nodiscard]] auto deserialize(const char *data, size_t size, + size_t &consume_len) { expected, struct_pack::errc> ret; auto errc = deserialize_to(ret.value(), data, size, consume_len); if SP_UNLIKELY (errc != struct_pack::errc{}) { @@ -519,12 +522,11 @@ template } #if __cpp_concepts >= 201907L -template +template #else template #endif -[[nodiscard]] STRUCT_PACK_INLINE auto deserialize_with_offset(const View &v, - size_t &offset) { +[[nodiscard]] auto deserialize_with_offset(const View &v, size_t &offset) { expected, struct_pack::errc> ret; auto errc = deserialize_to_with_offset(ret.value(), v, offset); if SP_UNLIKELY (errc != struct_pack::errc{}) { @@ -534,9 +536,8 @@ template } template -[[nodiscard]] STRUCT_PACK_INLINE auto deserialize_with_offset(const char *data, - size_t size, - size_t &offset) { +[[nodiscard]] auto deserialize_with_offset(const char *data, size_t size, + size_t &offset) { expected, struct_pack::errc> ret; auto errc = deserialize_to_with_offset(ret.value(), data, size, offset); if SP_UNLIKELY (errc != struct_pack::errc{}) { @@ -547,14 +548,14 @@ template #if __cpp_concepts >= 201907L template + typename Field, struct_pack::detail::deserialize_view View> #else -template >> +template < + typename T, size_t I, uint64_t conf = sp_config::DEFAULT, typename Field, + typename View, + typename = std::enable_if_t>> #endif -[[nodiscard]] STRUCT_PACK_INLINE struct_pack::errc get_field_to(Field &dst, - const View &v) { +[[nodiscard]] struct_pack::errc get_field_to(Field &dst, const View &v) { using T_Field = std::tuple_element_t())>; static_assert(std::is_same_v, "The dst's type is not correct. It should be as same as the " @@ -567,8 +568,8 @@ template -[[nodiscard]] STRUCT_PACK_INLINE struct_pack::errc get_field_to( - Field &dst, const char *data, size_t size) { +[[nodiscard]] struct_pack::errc get_field_to(Field &dst, const char *data, + size_t size) { using T_Field = std::tuple_element_t())>; static_assert(std::is_same_v, "The dst's type is not correct. It should be as same as the " @@ -586,8 +587,7 @@ template >> #endif -[[nodiscard]] STRUCT_PACK_INLINE struct_pack::errc get_field_to( - Field &dst, Reader &reader) { +[[nodiscard]] struct_pack::errc get_field_to(Field &dst, Reader &reader) { using T_Field = std::tuple_element_t())>; static_assert(std::is_same_v, "The dst's type is not correct. It should be as same as the " @@ -598,13 +598,13 @@ template = 201907L template + struct_pack::detail::deserialize_view View> #else -template >> +template < + typename T, size_t I, uint64_t conf = sp_config::DEFAULT, typename View, + typename = std::enable_if_t>> #endif -[[nodiscard]] STRUCT_PACK_INLINE auto get_field(const View &v) { +[[nodiscard]] auto get_field(const View &v) { using T_Field = std::tuple_element_t())>; expected ret; auto ec = get_field_to(ret.value(), v); @@ -615,7 +615,7 @@ template -[[nodiscard]] STRUCT_PACK_INLINE auto get_field(const char *data, size_t size) { +[[nodiscard]] auto get_field(const char *data, size_t size) { using T_Field = std::tuple_element_t())>; expected ret; auto ec = get_field_to(ret.value(), data, size); @@ -632,7 +632,7 @@ template >> #endif -[[nodiscard]] STRUCT_PACK_INLINE auto get_field(Reader &reader) { +[[nodiscard]] auto get_field(Reader &reader) { using T_Field = std::tuple_element_t())>; expected ret; auto ec = get_field_to(ret.value(), reader); @@ -648,9 +648,9 @@ template >> #endif -[[nodiscard]] STRUCT_PACK_INLINE - struct_pack::expected, struct_pack::errc> - deserialize_derived_class(Reader &reader) { +[[nodiscard]] struct_pack::expected, + struct_pack::errc> +deserialize_derived_class(Reader &reader) { static_assert(sizeof...(DerivedClasses) > 0, "There must have a least one derived class"); static_assert( @@ -678,15 +678,15 @@ template = 201907L template + struct_pack::detail::deserialize_view View> #else template < typename BaseClass, typename... DerivedClasses, typename View, typename = std::enable_if_t>> #endif -[[nodiscard]] STRUCT_PACK_INLINE - struct_pack::expected, struct_pack::errc> - deserialize_derived_class(const View &v) { +[[nodiscard]] struct_pack::expected, + struct_pack::errc> +deserialize_derived_class(const View &v) { detail::memory_reader reader{v.data(), v.data() + v.size()}; if constexpr (std::is_abstract_v) { return deserialize_derived_class(reader); @@ -697,9 +697,9 @@ template < } } template -[[nodiscard]] STRUCT_PACK_INLINE - struct_pack::expected, struct_pack::errc> - deserialize_derived_class(const char *data, size_t size) { +[[nodiscard]] struct_pack::expected, + struct_pack::errc> +deserialize_derived_class(const char *data, size_t size) { detail::memory_reader reader{data, data + size}; if constexpr (std::is_abstract_v) { return deserialize_derived_class(reader); diff --git a/include/ylt/struct_pack/compatible.hpp b/include/ylt/struct_pack/compatible.hpp index 15b378841..0522ec37e 100644 --- a/include/ylt/struct_pack/compatible.hpp +++ b/include/ylt/struct_pack/compatible.hpp @@ -140,8 +140,7 @@ struct compatible : public std::optional { : std::optional(other){}; constexpr compatible &operator=(const compatible &other) = default; constexpr compatible &operator=(compatible &&other) = default; - using base = std::optional; - using base::base; + using std::optional::optional; friend bool operator==(const compatible &self, const compatible &other) { return static_cast(self) == static_cast(other) && diff --git a/include/ylt/struct_pack/error_code.hpp b/include/ylt/struct_pack/error_code.hpp index 20fe62f34..88f4ec5de 100644 --- a/include/ylt/struct_pack/error_code.hpp +++ b/include/ylt/struct_pack/error_code.hpp @@ -27,7 +27,6 @@ enum class errc { }; namespace detail { - class struct_pack_category : public std::error_category { public: virtual const char *name() const noexcept override { @@ -44,7 +43,8 @@ class struct_pack_category : public std::error_category { return "invalid argument"; case errc::hash_conflict: return "hash conflict"; - + case errc::invalid_width_of_container_length: + return "invalid width of container length"; default: return "(unrecognized error)"; } diff --git a/include/ylt/struct_pack/md5_constexpr.hpp b/include/ylt/struct_pack/md5_constexpr.hpp index cf353f825..bc21e4988 100644 --- a/include/ylt/struct_pack/md5_constexpr.hpp +++ b/include/ylt/struct_pack/md5_constexpr.hpp @@ -27,7 +27,7 @@ namespace struct_pack { template struct string_literal { constexpr string_literal() = default; - constexpr string_literal(std::string_view str) : ar{} { + constexpr string_literal(std::basic_string_view str) : ar{} { for (size_t i = 0; i < Size; ++i) { ar[i] = str[i]; } @@ -39,6 +39,40 @@ struct string_literal { } } + template + constexpr bool operator!=( + const string_literal &other) const { + if constexpr (Size == Size2) { + for (int i = 0; i < Size; ++i) { + if ((*this)[i] != other[i]) + return true; + } + return false; + } + else { + return true; + } + } + + template + constexpr bool operator==( + const string_literal &other) const { + return !(*this != other); + } + + template + string_literal constexpr operator+( + string_literal other) const { + string_literal ret{}; + for (size_t i = 0; i < Size; ++i) { + ret[i] = (*this)[i]; + } + for (size_t i = 0; i < Size2; ++i) { + ret[i + Size] = other[i]; + } + return ret; + } + constexpr std::size_t size() const { return Size; } constexpr bool empty() const { return !Size; } @@ -53,44 +87,10 @@ struct string_literal { CharType ar[Size + 1]; }; -template -constexpr bool operator!=(const string_literal &s1, - const string_literal &s2) { - if constexpr (Size1 == Size2) { - for (int i = 0; i < Size1; ++i) { - if (s1[i] != s2[i]) - return true; - } - return false; - } - else { - return true; - } -} - -template -constexpr bool operator==(const string_literal &s1, - const string_literal &s2) { - return !(s1 != s2); -} - template string_literal(const CharType (&value)[Size]) -> string_literal; -template -decltype(auto) constexpr operator+(string_literal str1, - string_literal str2) { - string_literal ret{}; - for (size_t i = 0; i < Len1; ++i) { - ret[i] = str1[i]; - } - for (size_t i = 0; i < Len2; ++i) { - ret[i + Len1] = str2[i]; - } - return ret; -} - namespace MD5 { // The implementation here is based on the pseudocode provided by Wikipedia: // https://en.wikipedia.org/wiki/MD5#Pseudocode diff --git a/include/ylt/struct_pack/trivial_view.hpp b/include/ylt/struct_pack/trivial_view.hpp index 7fee242ac..35ff46d4c 100644 --- a/include/ylt/struct_pack/trivial_view.hpp +++ b/include/ylt/struct_pack/trivial_view.hpp @@ -19,9 +19,11 @@ #include "reflection.hpp" +namespace struct_pack { /*! * \ingroup struct_pack * \struct trivial_view + * \tparam T trivial_view指向的类型 * \brief * trivial_view is a view for trivial struct. It's equals T in type system. * It can decrease memory copy in proto initialization/deserialization @@ -74,8 +76,6 @@ * ``` * */ - -namespace struct_pack { template struct trivial_view { private: @@ -85,14 +85,12 @@ struct trivial_view { trivial_view(const T* t) : ref(t){}; trivial_view(const T& t) : ref(&t){}; trivial_view(const trivial_view&) = default; - trivial_view(trivial_view&&) = default; trivial_view() : ref(nullptr){}; - trivial_view& operator=(const trivial_view&) = default; - trivial_view& operator=(trivial_view&&) = default; using value_type = T; + void set(const T& obj) { ref = &obj; } const T& get() const { assert(ref != nullptr); return *ref; diff --git a/website/Doxyfile b/website/Doxyfile index 5ebbca208..ca2b1e5fc 100644 --- a/website/Doxyfile +++ b/website/Doxyfile @@ -2,12 +2,12 @@ PROJECT_NAME=yaLanTingLibs GENERATE_HTML=yes GENERATE_LATEX=no INPUT=../include/ylt/struct_pack.hpp \ - ../include/ylt/coro_rpc/coro_rpc_server.hpp \ ../include/ylt/coro_rpc/coro_rpc_client.hpp \ docs/en/coro_rpc/coro_rpc_introduction.md \ docs/en/struct_pack/struct_pack_intro.md - + +MARKDOWN_SUPPORT = YES GENERATE_TREEVIEW = YES # required! DISABLE_INDEX = NO FULL_SIDEBAR = NO diff --git a/website/Doxyfile_cn b/website/Doxyfile_cn index 4eea4f404..d8c709133 100644 --- a/website/Doxyfile_cn +++ b/website/Doxyfile_cn @@ -5,7 +5,8 @@ INPUT=docs/zh/coro_rpc/coro_rpc_doc.hpp \ docs/zh/coro_rpc/coro_rpc_intro.md \ docs/zh/struct_pack/struct_pack_doc.hpp \ docs/zh/struct_pack/struct_pack_intro.md - + +MARKDOWN_SUPPORT = YES GENERATE_TREEVIEW = YES # required! DISABLE_INDEX = NO FULL_SIDEBAR = NO diff --git a/website/docs/en/struct_pack/struct_pack_intro.md b/website/docs/en/struct_pack/struct_pack_intro.md index edf784f80..f3dbe0473 100644 --- a/website/docs/en/struct_pack/struct_pack_intro.md +++ b/website/docs/en/struct_pack/struct_pack_intro.md @@ -507,7 +507,7 @@ user can also serialize type `base` then deserialize it to the `std::unique(buffer); + struct_pack::deserialize_derived_class(ret); assert(result.has_value()); // check deserialize ok std::unique_ptr ptr = std::move(result.value()); assert(ptr != nullptr); diff --git a/website/docs/zh/struct_pack/struct_pack_doc.hpp b/website/docs/zh/struct_pack/struct_pack_doc.hpp index ad4a407c3..557f32d0f 100644 --- a/website/docs/zh/struct_pack/struct_pack_doc.hpp +++ b/website/docs/zh/struct_pack/struct_pack_doc.hpp @@ -19,675 +19,1321 @@ namespace struct_pack { /*! - * \defgroup struct_pack struct_pack + * \defgroup struct_pack struct_pack * - * \brief yaLanTingLibs struct_pack 序列化库 + * \brief 高性能,head-only,直接解析C++结构体的二进制序列化库。 * */ /*! * \ingroup struct_pack * \struct expected - * \brief deserialize的返回值 + * \brief `deserialize`函数的返回值类型 + * + * 当启用C++23标准且标准库已实现`std::expected`时,该类是`std::expected`的别名。否则该类将使用内部的实现代码来模拟。 + * + * 该类型的详细文档与介绍请见[cpprefence](https://en.cppreference.com/w/cpp/utility/expected) * - * 当启用C++23标准且标准库已实现std::expected时,该类是std::expected的别名。否则该类将使用内部的实现代码。 * 例如: - * ```cpp - * person p{20, "tom"}; - * auto buffer = struct_pack::serialize(p); - * auto p2 = struct_pack::deserialize(buffer.data(), buffer.size()); - * assert(p2); - * assert(p == p2.value()); - * ``` + @code{.cpp} + int test() { + person p{20, "tom"}; + auto buffer = struct_pack::serialize(p); + struct_pack::expected p2 = + struct_pack::deserialize(buffer.data(), buffer.size()); + assert(p2); + assert(p == p2.value()); + } + @endcode + * */ /*! - * \ingroup struct_pack - * \struct trivial_view - * \brief - * trivial_view 是一个平凡结构体的视图,在类型系统上等价于T。 - * 其作用是减少赋值/反序列化过程中的内存拷贝。 - * - * 例如,假如有一个巨大的结构体Data - * - * ```cpp - * struct Data { - * int x[10000],y[10000],z[10000]; - * }; + * @ingroup struct_pack + * @struct trivial_view + * @brief `trivial_view` 是一个平凡结构体的视图,在类型系统上等价于`T`。 * + * 其作用是减少赋值/反序列化过程中的内存拷贝。 + * 例如,假如有一个巨大的结构体`Data` + @code{.cpp} + struct Data { + int x[10000],y[10000],z[10000]; + }; + @endcode * 假设有协议: - * ```cpp - * struct Proto { - * std::string name; - * Data data; - * }; - * void serialzie(std::string_view name, Data& data) { - * Proto proto={.name = name, .data = data}; - * //从已有的数据构造对象时,需要花费大量时间拷贝 - * auto buffer = struct_pack::serialize(proto); - * auto result = struct_pack::deserialize(data); - * //反序列化时,需要花费大量时间拷贝 - * assert(result->name == name && result->data == data); - * } - * ``` + @code{.cpp} + struct Proto { + std::string name; + Data data; + }; + void serialzie(std::string_view name, Data& data) { + Proto proto={.name = name, .data = data}; + //从已有的数据构造对象时,需要花费大量时间拷贝 + auto buffer = struct_pack::serialize(proto); + auto result = struct_pack::deserialize(data); + //反序列化时,需要花费大量时间拷贝 + assert(result->name == name && result->data == data); + } + @endcode * * 可以发现,构造/反序列化时拷贝代价很大。 * 如何解决?我们可以改用视图: - * ```cpp - * struct ProtoView { - * std::string_view name; - * struct_pack::trivial_view data; - * }; - * void serialzie(std::string_view name, Data& data) { - * ProtoView proto={.name = name, .data = data}; - * //从已有的数据构造对象时,可以做到zero-copy - * auto buffer = struct_pack::serialize(proto); - * auto result = struct_pack::deserialize(data); - * //反序列化是zero-copy的。 - * assert(result->name == name && result->data.get() == data); - * } - * ``` - * 由于在类型系统上trivial_view等价于T,故两者在内存布局上等价。 - * 因此,序列化其中一者再反序列化到另外一者是合法的。 - * ```cpp - * void serialzie(Proto& proto) { - * auto buffer = struct_pack::serialize(proto); - * auto result = struct_pack::deserialize(data); - * //反序列化是zero-copy的。 - * assert(result->name == name && result->data.get() == data); - * } - * ``` - * + @code{.cpp} + struct ProtoView { + std::string_view name; + struct_pack::trivial_view data; + }; + void serialzie(std::string_view name, Data& data) { + ProtoView proto={.name = name, .data = data}; + //从已有的数据构造对象时,可以做到zero-copy + auto buffer = struct_pack::serialize(proto); + auto result = struct_pack::deserialize(data); + //反序列化是zero-copy的。 + assert(result->name == name && result->data.get() == data); + } + @endcode + * `trivial_view`在类型系统中等价于`T`,因此,序列化其中一者再反序列化到另外一者是合法的。 + @code{.cpp} + void serialzie(Proto& proto) { + auto buffer = struct_pack::serialize(proto); + auto result = struct_pack::deserialize(data); + //反序列化是zero-copy的。 + assert(result->name == name && result->data.get() == data); + } + @endcode + * + * @tparam T `trivial_view`指向的类型,`T`必须是一个平凡的结构体 */ +template +struct trivial_view { + private: + const T *ref; + + public: + /** + * @brief 构造一个指向对象`t`的`trivial_view` + * + * @param t + */ + trivial_view(const T *t); + /** + * @brief 构造一个指向对象`t`的`trivial_view` + * + * @param t + */ + trivial_view(const T &t); + trivial_view(const trivial_view &) = default; + /** + * @brief 构造一个指向空对象的`trivial_view` + * + */ + trivial_view() : ref(nullptr){}; + trivial_view &operator=(const trivial_view &) = default; + /** + * @brief `trivial_view`指向的对象的类型,`value_type`必须是一个平凡的结构体 + + * + */ + using value_type = T; + + /** + * @brief 设置`trivial_view`指向的对象 + * + * @param obj + */ + void set(const T &obj); + /** + * @brief 返回`trivial_vie`w指向的对象`T`的常引用 + * + * @return const T& + */ + const T &get() const; + /** + * @brief 允许`trivial_view`通过运算符`->`访问指向对象`T` + * + * @return const T* + */ + const T *operator->() const; +}; /*! * \ingroup struct_pack * \struct compatible - * \brief - * 这个类使用上类似于std::optional,但其语义是添加一个能够保持向前兼容的字段。 + * \brief 兼容字段类型 + * + * @tparam T 兼容字段的类型 + * @tparam version 兼容字段的版本号 + * + * 这个类使用上类似于`std::optional`,但其语义是添加一个能够保持向前兼容的字段。 * * 例如: * - * ```cpp - * struct person_v1 { - * int age; - * std::string name; - * }; - * - * struct person_v2 { - * int age; - * std::string name; - * struct_pack::compatible id; //20210101为版本号 - * }; - * ``` - * - * `struct_pack::compatible` - * 可以为空值,从而保证向前和向后的兼容性。 - * 例如,序列化person_v2,然后将其按照person_v1来反序列化,多余的字段在反序列化时将会被直接忽略。 - * 反过来说,序列化person_v1,再将其按照person_v2来反序列化,则解析出的person_v2中的compatibale字段均为空值。 - * - * compatible字段的版本号可以省略,默认版本号为0。 - * - * ```cpp - * person_v2 p2{20, "tom", 114.1, "tom"}; - * auto buf = struct_pack::serialize(p2); - * - * person_v1 p1; - * // deserialize person_v2 as person_v1 - * auto ec = struct_pack::deserialize_to(p1, buf.data(), buf.size()); - * CHECK(ec == std::errc{}); - * - * auto buf1 = struct_pack::serialize(p1); - * person_v2 p3; - * // deserialize person_v1 as person_v2 - * auto ec = struct_pack::deserialize_to(p3, buf1.data(), buf1.size()); - * CHECK(ec == std::errc{}); - * ``` - * - * 升级接口时应注意保持 **版本号递增** 原则: - * 1. - * 新增加的compatible字段的版本号,应该大于上一次增加的compatible字段的版本号。 - * 2. - * 不得移除原来的任何一个compatible字段(移除非compatible字段也会出错,但这种错误是可以被类型校验检查出来的) + @code{.cpp} + struct person_v1 { + int age; + std::string name; + }; + + struct person_v2 { + int age; + std::string name; + struct_pack::compatible id; //20210101为版本号 + }; + @endcode + * + * `struct_pack::compatible`可以为空值,从而保证向前和向后的兼容性。 + * 例如,序列化`person_v2`,然后将其按照`person_v1`来反序列化,多余的字段在反序列化时将会被直接忽略。 + * 反过来说,序列化`person_v1`,再将其按照`person_v2`来反序列化,则解析出的`person_v2`中的`compatibale`字段均为空值。 + * + * `compatible`字段的版本号可以省略,默认版本号为0。 + * + @code{.cpp} + person_v2 p2{20, "tom", 114.1, "tom"}; + auto buf = struct_pack::serialize(p2); + + person_v1 p1; + // deserialize person_v2 as person_v1 + auto ec = struct_pack::deserialize_to(p1, buf.data(), buf.size()); + CHECK(ec == std::errc{}); + + auto buf1 = struct_pack::serialize(p1); + person_v2 p3; + // deserialize person_v1 as person_v2 + auto ec = struct_pack::deserialize_to(p3, buf1.data(), buf1.size()); + CHECK(ec == std::errc{}); + @endcode + * + * 升级接口时应注意保持版本号递增 原则: + * + * 1.新增加的`compatible`字段的版本号,应该大于上一次增加的`compatible`字段的版本号。 + * + * 2.不得移除原来的任何一个`compatible`字段(移除普通字段也会出错,但这种错误是可以被类型校验检查出来的) * * 假如违反这一原则,那么可能会引发未定义行为。 * 例如: * - * ```cpp - * struct loginRequest_V1 - * { - * string user_name; - * string pass_word; - * struct_pack::compatible verification_code; - * }; - * - * struct loginRequest_V2 - * { - * string user_name; - * string pass_word; - * struct_pack::compatible ip_address; - * }; - * - * auto data=struct_pack::serialize(loginRequest_V1{}); - * loginRequest_V2 req; - * //该行为是未定义的! - * struct_pack::deserialize_to(req, data); - * - * ``` - * - * ```cpp - * struct loginRequest_V1 - * { - * string user_name; - * string pass_word; - * struct_pack::compatible verification_code; - * }; - * - * struct loginRequest_V2 - * { - * string user_name; - * string pass_word; - * struct_pack::compatible verification_code; - * struct_pack::compatible ip_address; - * }; - * - * auto data=struct_pack::serialize(loginRequest_V1{}); - * loginRequest_V2 req; - * //该行为是未定义的! - * struct_pack::deserialize_to(req, data); - * - * ``` - * + @code{.cpp} + struct loginRequest_V1 + { + string user_name; + string pass_word; + struct_pack::compatible verification_code; + }; + + struct loginRequest_V2 + { + string user_name; + string pass_word; + struct_pack::compatible ip_address; + }; + + auto data=struct_pack::serialize(loginRequest_V1{}); + loginRequest_V2 req; + //该行为是未定义的! + struct_pack::deserialize_to(req, data); + @endcode + * + @code{.cpp} + struct loginRequest_V1 + { + string user_name; + string pass_word; + struct_pack::compatible verification_code; + }; + + struct loginRequest_V2 + { + string user_name; + string pass_word; + struct_pack::compatible verification_code; + struct_pack::compatible ip_address; + }; + + auto data=struct_pack::serialize(loginRequest_V1{}); + loginRequest_V2 req; + //该行为是未定义的! + struct_pack::deserialize_to(req, data); + @endcode */ +template +struct compatible : public std::optional { + constexpr compatible() = default; + constexpr compatible(const compatible &other) = default; + constexpr compatible(compatible &&other) = default; + constexpr compatible(std::optional &&other) + : std::optional(std::move(other)){}; + constexpr compatible(const std::optional &other) + : std::optional(other){}; + constexpr compatible &operator=(const compatible &other) = default; + constexpr compatible &operator=(compatible &&other) = default; + using std::optional::optional; + friend bool operator==(const compatible &self, + const compatible &other); + static constexpr uint64_t version_number = version; +}; + /*! * \ingroup struct_pack * \struct string_literal - * \brief - * 该类用于表示一个编译期的字符串类型,是函数`struct_pack::get_type_literal`的返回值。 + * \brief 编译期字符串类型 * + * \tparam CharType 字符类型 + * \tparam Size 字符串长度 + * 该类用于表示一个编译期的字符串类型,是函数`struct_pack::get_type_literal`的返回值,该字符串以'\0'结尾 * * 样例代码: * - * ```cpp - * auto str = get_type_literal(); - * CHECK(str.size() == 5); - * string_literal val{{(char)-2, 1, 1, 7, (char)-1}}; - * //{struct_begin,int32_t,int32_t,int16_t,struct_end}; CHECK(str == val); - * ``` + @code{.cpp} + auto str = struct_pack::get_type_literal(); + CHECK(str.size() == 5); + string_literal val{{(char)-2, 1, 1, 7, (char)-1}}; + // {struct_begin,int32_t,int32_t,int16_t,struct_end}; + CHECK(str == val); + @endcode */ -/*! +template +struct string_literal { + constexpr string_literal() = default; + /** + * @brief 从`string_view`构造`string_literal`类型 + * + */ + constexpr string_literal(std::basic_string_view str); + /** + * @brief 从数组构造`string_literal`类型 + * + */ + constexpr string_literal(const CharType (&value)[Size + 1]); + /** + * @brief 返回字符串的长度 + * + * @return constexpr std::size_t + */ + constexpr std::size_t size() const; + /** + * @brief 判断字符串是否为空字符串 + * + * @return true + * @return false + */ + constexpr bool empty() const; + /** + * @brief 获取下标对应的字符 + * + * @param sz + * @return constexpr CharType& + */ + constexpr CharType &operator[](std::size_t sz); + /** + * @brief 获取下标对应的字符 + * + * @param sz + * @return constexpr const char& + */ + constexpr const char &operator[](std::size_t sz) const; + + /** + * @brief 返回一个C-style(以'\0'结尾)的字符串指针 + * + * @return constexpr const CharType* + */ + constexpr const CharType *data() const; + + /** + * @brief 判断两个字符串是否不相等 + * + * @tparam Size2 + * @param other + * @return true + * @return false + */ + template + constexpr bool operator!=(const string_literal &other) const; + + /** + * @brief 判断两个字符串是否相等 + * + * @tparam Size2 + * @param other + * @return true + * @return false + */ + template + constexpr bool operator==(const string_literal &other) const; + + /** + * @brief 拼接两个字符串 + * + * @tparam Size2 + * @param other + * @return string_literal constexpr 返回拼接后的字符串 + */ + template + string_literal constexpr operator+( + string_literal other) const; + + private: + CharType ar[Size + 1]; +}; + +/** * \ingroup struct_pack - * \struct members_count - * \brief - * 某些特殊情况下,struct_pack可能无法正确计算结构体内的元素个数并导致编译期错误。 - * 此时请特化模板`struct_pack::members_count`,并手动标明结构体内元素的个数。 - * - * 样例代码: - * ```cpp - * struct bug_member_count_struct1 { - * int i; - * std::string j; - * double k = 3; - * }; + * @brief struct_pack的错误码 + * + * struct_pack的错误码枚举,各枚举值解释如下: + * 1. ok,代表无错误。 + * 2. no_buffer_space,缓冲区耗尽,未能完成所有字段的反序列化。 + * 3. invalid_buffer,读取到非法数据,无法将数据反序列化到指定类型。 + * 4. hash_conflict, + * 侦测到哈希冲突,该错误仅在序列化数据中存在完整类型信息时才可能出现。代表尽管类型校验哈希码相同,但反序列化的目的类型和数据的实际类型依然不同,两类型的校验值之间发生了哈希冲突。 + * 5. + * invalid_width_of_container_length,容器长度的位数不合法,例如,当字符串/容器长度大于2的32次方时,序列化数据的二进制格式使用64位的容器长度来存储数据,将该数据保存到文件中,并在32位系统上反序列化,即会返回该错误,因为32位系统不支持如此之大的数据。 + */ +enum class errc { + ok = 0, + no_buffer_space, + invalid_buffer, + hash_conflict, + invalid_width_of_container_length, +}; + +/** + * \ingroup struct_pack + * @brief struct_pack的配置 * - * struct bug_member_count_struct2 : bug_member_count_struct1 {}; + * struct_pack的编译期配置,用户可通过或运算组合各枚举值,以决定struct_pack的序列化行为。 * - * template <> - * constexpr size_t struct_pack::members_count = 3; - * ``` + * 具体说明详见:[序列化设置](https://alibaba.github.io/yalantinglibs/zh/struct_pack/struct_pack_tips.html#%E5%BA%8F%E5%88%97%E5%8C%96%E8%AE%BE%E7%BD%AE) * */ +enum sp_config : uint64_t { + DEFAULT = 0, + + DISABLE_TYPE_INFO = 0b1, + ENABLE_TYPE_INFO = 0b10, + DISABLE_ALL_META_INFO = 0b11, + + ENCODING_WITH_VARINT = 0b100, + USE_FAST_VARINT = 0b1000 +}; /*! * \ingroup struct_pack - * \struct members_count - * \brief + * \brief 手动标注结构体成员个数 + * + * @tparam T 待纠正的结构体 + * * 某些特殊情况下,struct_pack可能无法正确计算结构体内的元素个数并导致编译期错误。 * 此时请特化模板`struct_pack::members_count`,并手动标明结构体内元素的个数。 * * 样例代码: - * ```cpp - * struct bug_member_count_struct1 { - * int i; - * std::string j; - * double k = 3; - * }; - * - * struct bug_member_count_struct2 : bug_member_count_struct1 {}; - * - * template <> - * constexpr size_t struct_pack::members_count = 3; - * ``` + @code{.cpp} + struct bug_member_count_struct1 { + int i; + std::string j; + double k = 3; + }; + + struct bug_member_count_struct2 : bug_member_count_struct1 {}; + + template <> + constexpr size_t struct_pack::members_count = 3; + @endcode + */ +template +constexpr std::size_t members_count; + +/*! + * \ingroup struct_pack + * @brief 构造`std::error_code` + * @param err error code. + * @return std::error_code * + * 通过`struct_pack::errc`构造`std::error_code` */ +inline std::error_code make_error_code(struct_pack::errc err); /*! * \ingroup struct_pack - * 本函数返回std::errc对应的错误消息。 + * @brief 获取错误消息 * @param err - * @return + * @return std::string 错误消息 + * + * 本函数获取struct_pack::errc对应的错误消息。 */ -STRUCT_PACK_INLINE std::string error_message(struct_pack::errc err); + +inline std::string error_message(struct_pack::errc err); /*! * \ingroup struct_pack - * 用于预先分配好合适长度的内存,通常配合`struct_pack::serialize_to`函数使用。 - * 如果类型允许,该计算可能在编译期完成。 + * @brief 获取类型校验码 + * @tparam Args + * @return 编译期计算出的类型名 + * + * +返回Args的31位类型校验码。当传入的参数多于一个时,返回类型`std::tuple`的校验码 * * 样例代码: * - * ```cpp - * std::map obj{{"hello", "struct pack"}}; - * auto size=struct_pack::get_needed_size(obj); - * auto array=std::make_unique(size); - * struct_pack::serialize_to(array.get(),size); - * ``` + @code{.cpp} + static_assert( + get_type_code>() == get_type_code>(), + "different class accord with container concept should get same MD5"); + static_assert( + get_type_code>() != get_type_code>(), + "different class accord with different concept should get different MD5"); + @endcode * * @tparam Args - * @param args - * @return 序列化所需的buffer的长度。 + * @return 编译期计算出的类型哈希校验码。 */ template -[[nodiscard]] STRUCT_PACK_INLINE constexpr auto get_needed_size( - const Args &...args); +constexpr std::uint32_t get_type_code(); /*! * \ingroup struct_pack - * 返回一个31位的类型T的MD5校验码。当传入的参数多于一个时,返回类型`std::tuple`的校验码 + * 本函数返回编译期计算出的类型名,并按`struct_pack::string_literal`类型返回。 + * 当传入的参数多于一个时,返回类型`std::tuple`的类型名。 * * 样例代码: * - * ```cpp - * static_assert( - * get_type_code>() == - * get_type_code>(), "different class accord with container - * concept should get same MD5"); - * ``` - * - * @tparam Args - * @return 编译期计算出的类型的哈希校验码。 + @code{.cpp} + auto str = get_type_literal>(); + CHECK(str.size() == 5); + string_literal val{{(char)-2, 1, 1, 7, (char)-1}}; + //{struct_begin,int32_t,int32_t,int16_t,struct_end}; + CHECK(str == val); + @endcode */ template -STRUCT_PACK_INLINE consteval std::uint32_t get_type_code(); +constexpr decltype(auto) get_type_literal(); /*! * \ingroup struct_pack - * 本函数返回编译期计算出的类型名,并按`struct_pack::string_literal`类型返回。 - * 当传入的参数多于一个时,返回类型`std::tuple`的类型名。 * - * 样例代码: + * @brief 获取序列化长度 * - * ```cpp - * auto str = get_type_literal>(); - * CHECK(str.size() == 5); - * string_literal val{{(char)-2, 1, 1, 7, (char)-1}}; - * //{struct_begin,int32_t,int32_t,int16_t,struct_end}; CHECK(str == val); - * ``` * @tparam Args - * @return 编译期计算出的类型名 + * @param args + * @return serialize_buffer_size 序列化所需的buffer的长度。 + * + * 用于预先分配好合适长度的内存,通常配合`struct_pack::serialize_to`函数使用。 + * 如果类型允许,该计算可能在编译期完成。 + * + * 样例代码: + * + @code{.cpp} + std::map obj{{"hello", "struct pack"}}; + auto size=struct_pack::get_needed_size(obj); + auto array=std::make_unique(size); + struct_pack::serialize_to(array.get(),size); + @endcode */ template -STRUCT_PACK_INLINE consteval decltype(auto) get_type_literal(); +[[nodiscard]] constexpr struct_pack::serialize_buffer_size get_needed_size( + const Args &...args); -/*! +/** * \ingroup struct_pack - * 序列化 * - * 样例代码 - * ``` - * std::map obj{{"hello", "struct pack"}}; - * // 序列化对象 - * { - * auto buffer=struct_pack::serialize(obj); - * } - * // 保存到std::string - * { - * auto buffer=struct_pack::serialize(obj); - * } - * // 打包序列化多个对象 - * { - * auto buffer=struct_pack::serialize(obj,42,"Hello world"); - * } - * ``` + * @brief 将对象序列化并写入到给定的流/缓冲区。 * - * @tparam Buffer 传入的自定义`Buffer`类型需要满足以下约束条件: - * 1. 是顺序容器,具有成员函数begin(),end(),size(),data(),resize()等函数; - * 2. 容器的value_type为char,unsigned char 或者 std::byte - * 3. 容器的内存布局是连续的 + * @tparam conf 序列化配置,详见`struct_pack::sp_config` + * @tparam Writer + 输出流/缓冲区容器类型,对于缓冲区容器需要满足`detail::struct_pack_buffer`约束,可以为内存连续的字节容器(如`std::vector`,`std::string`)。 + ,对于流需要满足`struct_pack::writer_t`约束的类型(如`std::ostream`) * @tparam Args - * @param args args为需要序列化的对象。 - * 当传入多个序列化对象时,函数会将其打包合并,按std::tuple类型的格式序列化。 - * @return - * 返回保存在容器中的序列化结果,容器类型为模板参数中的buffer,默认为std::vector。 - * 也支持std::string或者自定义容器 + * 需要序列化的对象类型。当传入多个序列化对象时,函数会将其打包合并,按`std::tuple`类型的格式序列化。 + * @param writer 输出缓冲区 + * @param args 待序列化的对象 + * + * 需要注意的是,该函数不会清空写入的容器/流,而是会将数据附加到缓冲区尾部。 + * + * 样例代码: + * @code{.cpp} + std::string buffer = "The next line is struct_pack data.\n"; + struct_pack::serialize_to(buffer, p); + @endcode + * @code{.cpp} + std::ofstream writer("struct_pack_demo.data", + std::ofstream::out | std::ofstream::binary); + struct_pack::serialize_to(writer, p); + @endcode + * + * */ -template , - typename... Args> -[[nodiscard]] STRUCT_PACK_INLINE Buffer serialize(const Args &...args); +template +void serialize_to(Writer &writer, const Args &...args); /*! * \ingroup struct_pack - * 该函数在 struct_pack::serialize_with_offset 的基础上,增加了一个offset参数。 * - * 异常:struct_pack不主动抛出异常。 + * @brief 将对象序列化到给定的内存地址 + * + * @tparam conf 序列化配置,详见`struct_pack::sp_config` + * @tparam Args + * 需要序列化的对象类型。当传入多个序列化对象时,函数会将其打包合并,按`std::tuple`类型的格式序列化。 + * @param buffer 内存首地址 + * @param len 序列化所需长度,该长度可通过`struct_pack::get_needed_size`计算得到 + * @param args 待序列化的对象 * * 样例代码: - * ```cpp - * std::map obj{{"hello", "struct pack"}}; - * // 保存到std::string, 并且在string的头部预留一个int的空间。 - * { - * auto - * buffer=struct_pack::serialize_with_offset(sizeof(int),obj); - * } - * ``` + * @code{.cpp} + auto size = struct_pack::get_needed_size(p); + auto array = std::make_unique(size); + struct_pack::serialize_to(array.get(), size, p); + @endcode + */ +template +void serialize_to(char *buffer, serialize_buffer_size len, const Args &...args); + +/** + * \ingroup struct_pack + * + * @brief 将序列化结果保存到给定缓冲区尾部,并在序列化结果的头部预留一段字节。 + * + * @tparam conf 序列化配置,详见`struct_pack::sp_config` + * @tparam Buffer + * 序列化缓冲区类型,需要满足`detail::struct_pack_buffer`约束,缓冲区可以为内存连续的字节容器(如`std::vector`,`std::string`)。 + * @tparam Args + * 需要序列化的对象类型。当传入多个序列化对象时,函数会将其打包合并,按`std::tuple`类型的格式序列化。 + * @param buffer 输出缓冲区 + * @param offset 头部偏移的空闲字节长度 + * @param args 待序列化的对象 + * + * 需要注意的是,出于性能优化考虑,预留的字节可能未被初始化,不应假设其被初始化为0。 + */ +template +void serialize_to_with_offset(Buffer &buffer, std::size_t offset, + const Args &...args); + +/** + * \ingroup struct_pack * - * @tparam Buffer 传入的自定义`Buffer`类型需要满足以下约束条件: - * 1. 是顺序容器,具有成员函数begin(),end(),size(),data(),resize()等函数; - * 2. 容器的value_type为char,unsigned char 或者 std::byte - * 3. 容器的内存布局是连续的 + * @brief 序列化对象并返回结果 + * + * @tparam Buffer + * 需要序列化的对象类型,默认为`std::vector`,需要满足`detail::struct_pack_buffer`约束,可以为内存连续的字节容器(如`std::vector`,`std::string`)。 * @tparam Args - * @param offset 序列化结果的头部预留的空字节数。 - * @param args args为需要序列化的对象。 - * 当传入多个序列化对象时,函数会将其打包合并,按std::tuple类型的格式序列化。 - * @return - * 返回保存在容器中的序列化结果,容器类型为模板参数中的buffer,默认为std::vector。 - * 也支持std::string或者自定义容器。 + * 需要序列化的对象类型。当传入多个序列化对象时,函数会将其打包合并,按`std::tuple`类型的格式序列化。 + * @param args 待序列化的对象 + * @return Buffer 返回序列化的结果 + * + * 序列化对象并返回结果 + @code{.cpp} + person p{20, "tom"}; + auto buffer = struct_pack::serialize(p); + @endcode + * */ template , typename... Args> -[[nodiscard]] STRUCT_PACK_INLINE Buffer -serialize_with_offset(std::size_t offset, const Args &...args); +[[nodiscard]] Buffer serialize(const Args &...args); -/*! +/** * \ingroup struct_pack - * @tparam Byte - * @tparam Args args为需要序列化的对象。 - * 当传入多个序列化对象时,函数会将其打包合并,按std::tuple类型的格式序列化。 - * @param buffer 内存首地址 - * @param len 该段内存的长度 + * + * @brief 将序列化结果保存到容器并返回,同时在序列化结果的头部预留一段字节。 + * + * @tparam Buffer + * 序列化的容器(缓冲区)类型,需要满足`detail::struct_pack_buffer`约束,可以为内存连续的字节容器(如`std::vector`,`std::string`)。 + * @tparam Args + * 需要序列化的对象类型。当传入多个序列化对象时,函数会将其打包合并,按`std::tuple`类型的格式序列化。 + * @param offset 头部偏移的空闲字节长度 * @param args 待序列化的对象 - * @return - * 返回序列化结果所占的长度。当序列化结果所需的内存大于len时,不进行序列化,并返回0。 + * @return Buffer 将结果保存为容器类型,按值返回。 */ -template -std::size_t STRUCT_PACK_INLINE serialize_to(Byte *buffer, std::size_t len, - const Args &...args) noexcept; +template < + detail::struct_pack_buffer Buffer = std::vector typename... Args> +[[nodiscard]] Buffer serialize_with_offset(std::size_t offset, + const Args &...args); -/*! +/** * \ingroup struct_pack - * 将序列化结果添加到已有的容器尾部,或者保存到一块连续的内存中。 - * 样例代码 - * ```cpp - * std::map obj{{"hello", "struct pack"}}; - * // 序列化到已有的容器末尾 - * { - * auto buffer=serialize(42); - * struct_pack::serialize_to(buffer, obj); - * } - * // 序列化到某段内存中 - * { - * auto size=struct_pack::get_needed_size(obj); - * auto array=std::make_unique(size); - * struct_pack::serialize_to(array.get(),size); - * } - * // 内存长度不足 - * { - * std::array ar; - * auto len=struct_pack::serialize_to(ar,obj); - * assert(len==0); //数组长度不足! - * } - * ``` + * + * @brief 按给定配置序列化对象并返回结果 + * + * @tparam conf 显式指定的序列化配置,详见`struct_pack::sp_config` * @tparam Buffer - * @tparam Args args为需要序列化的对象。 - * 当传入多个序列化对象时,函数会将其打包合并,按std::tuple类型的格式序列化。 - * @param buffer 保存序列化结果的容器 + * 需要序列化的对象类型,默认为std::vector,需要满足`detail::struct_pack_buffer`约束,可以为内存连续的字节容器(如`std::vector`,`std::string`)。 + * @tparam Args + * 需要序列化的对象类型。当传入多个序列化对象时,函数会将其打包合并,按`std::tuple`类型的格式序列化。 * @param args 待序列化的对象 + * @return Buffer 返回序列化的结果 */ -template -void STRUCT_PACK_INLINE serialize_to(Buffer &buffer, const Args &...args); +template , + typename... Args> +[[nodiscard]] Buffer serialize(const Args &...args); -/*! +/** * \ingroup struct_pack - * 将序列化结果添加到已有的容器尾部,并在序列化结果的头部添加一段空白字节。 * + * @brief + * 按给定配置将序列化结果保存到容器并返回,同时在序列化结果的头部预留一段字节。 + * + * @tparam conf 显式指定的序列化配置,详见`struct_pack::sp_config` * @tparam Buffer + * 序列化的容器(缓冲区)类型,需要满足`detail::struct_pack_buffer`约束,可以为内存连续的字节容器(如`std::vector`,`std::string`)。 * @tparam Args - * @param buffer 保存序列化结果的容器 - * @param offset 序列化结果的头部预留的空字节数。 + * 需要序列化的对象类型。当传入多个序列化对象时,函数会将其打包合并,按`std::tuple`类型的格式序列化。 + * @param offset 头部偏移的空闲字节长度 * @param args 待序列化的对象 + * @return Buffer 将结果保存为容器类型,按值返回。 */ -template -void STRUCT_PACK_INLINE serialize_to_with_offset(Buffer &buffer, - std::size_t offset, - const Args &...args); +template , + typename... Args> +[[nodiscard]] Buffer serialize_with_offset(std::size_t offset, + const Args &...args); -/*! +/** + * \ingroup struct_pack + * @brief 从视图中反序列化目的对象 * + * @tparam conf 序列化配置,详见`struct_pack::sp_config` + * @tparam T 对象的类型T + * @tparam Args + * 对象的类型Args,当Args不为空时,会将数据按`std::tuple`格式将数据反序列化保存到参数中。 + * @tparam View 视图类型,需满足`struct_pack::detail::deserialize_view`约束 + * 约束,如std::string_view + * @param t 待反序列化的对象t + * @param v 存有struct_pack序列化数据的视图 + * @param args 待反序列化的对象args + * @return struct_pack::errc + * 返回错误码,如果返回值不等于`struct_pack::errc::ok`,说明反序列化失败 + * + * 当反序列化失败时,t的值可能被部分修改。 * 样例代码: + @code{.cpp} + person p{20, "tom"}; + auto buffer = struct_pack::serialize(p); + person p2; + auto ec = struct_pack::deserialize_to(p2, buffer); + assert(ec == struct_pack::errc{}); + assert(p == p2); + @endcode + */ +template +[[nodiscard]] struct_pack::errc deserialize_to(T &t, const View &v, + Args &...args); + +/** + * \ingroup struct_pack + * @brief 将内存中的数据反序列化到目的对象 * - * ```cpp - * person p{20, "tom"}; - * std::string buffer; - * auto - * buffer=struct_pack::serialize_to_with_offset(buffer,sizeof(std::size_t),p); - * std::size_t sz=buffer.size()-sizeof(std::size_t); - * memcpy(buffer.data(),&sz,sizeof(std::size_t)); - * //then send it to tcp data flow - * ``` - * - * @tparam T 反序列化的类型,需要用户手动填写 - * @tparam Args 函数允许填入多个类型参数,然后按std::tuple类型反序列化 - * @tparam Byte - * @param data 指向保存序列化结果的内存首地址。 - * @param size 内存的长度 - * @return 当用户只填入一个类型参数时,返回struct_pack::expected类型,该expected类型包含反序列化结果T或std::errc类型的错误码。 - * 否则当用户填入多个类型参数时,返回struct_pack::expected,std::errc>类型 - */ -template -[[nodiscard]] STRUCT_PACK_INLINE auto deserialize(const Byte *data, - size_t size); + * @tparam conf 序列化配置,详见`struct_pack::sp_config` + * @tparam T 对象的类型T + * @tparam Args + * 对象的类型Args,当Args不为空时,会将数据按`std::tuple`格式将数据反序列化保存到参数中。 + * @param t 待反序列化的对象t + * @param data 起始地址 + * @param size 数据的长度 + * @param args 待反序列化的对象args + * @return struct_pack::errc + * 返回错误码,如果返回值不等于`struct_pack::errc::ok`,说明反序列化失败 + * + * 当反序列化失败时,t的值可能被部分修改。 + * 样例代码: + @code{.cpp} + person p{20, "tom"}; + auto buffer = struct_pack::serialize(p); + person p2; + auto ec = struct_pack::deserialize_to(p2, buffer.data(), buffer.size()); + assert(ec == struct_pack::errc{}); + assert(p == p2); + @endcode + */ +template +[[nodiscard]] struct_pack::errc deserialize_to(T &t, const char *data, + size_t size, Args &...args); +/** + * \ingroup struct_pack + * @brief 将输入流中的数据反序列化到目的对象 + * + * @tparam conf 序列化配置,详见`struct_pack::sp_config` + * @tparam T 对象的类型T + * @tparam Args + * 对象的类型Args,当Args不为空时,会将数据按`std::tuple`格式将数据反序列化保存到参数中。 + * @tparam Reader 输入流类型Reader,该类型需要满足约束`struct_pack::reader_t` + * @param t 待反序列化的对象t + * @param reader 输入流 + * @param args + * @return struct_pack::errc + * 返回错误码,如果返回值不等于`struct_pack::errc::ok`,说明反序列化失败。 + * + * 当反序列化失败时,t的值可能被部分修改。 + */ +template +[[nodiscard]] struct_pack::errc deserialize_to(T &t, Reader &reader, + Args &...args); -/*! +/** * \ingroup struct_pack - * @tparam T 反序列化的类型,需要用户手动填写 - * @tparam Args 函数允许填入多个类型参数,然后按std::tuple类型反序列化 - * @tparam View - * @param v 待反序列化的数据,其包含成员函数data()和size()。 - * @return 当用户只填入一个类型参数时,返回struct_pack::expected类型,该expected类型包含反序列化结果T或std::errc类型的错误码。 - * 否则当用户填入多个类型参数时,返回struct_pack::expected,std::errc>类型 + * @brief 从视图中反序列化目的对象,反序列化时跳过开头的若干字节 + * + * @tparam conf 序列化配置,详见`struct_pack::sp_config` + * @tparam T 对象的类型T + * @tparam Args + * 对象的类型Args,当Args不为空时,会将数据按`std::tuple`格式将数据反序列化保存到参数中。 + * @tparam View 视图类型,需满足`struct_pack::detail::deserialize_view`约束 + * @param t 待反序列化的对象t + * @param v 存有struct_pack序列化数据的视图 + * @param offset 跳过开头字节的长度 + * @param args 待反序列化的对象args + * @return struct_pack::errc + * 返回错误码,如果返回值不等于`struct_pack::errc::ok`,说明反序列化失败 + * + * 当反序列化失败时,t的值可能被部分修改。 */ -template -[[nodiscard]] STRUCT_PACK_INLINE auto deserialize(const View &v); +template +[[nodiscard]] struct_pack::errc deserialize_to_with_offset(T &t, const View &v, + size_t &offset, + Args &...args); -/*! +/** * \ingroup struct_pack + * @brief 将内存中的数据反序列化到目的对象,反序列化时跳过开头的若干字节 + * + * @tparam conf 序列化配置,详见`struct_pack::sp_config` + * @tparam T 对象的类型T + * @tparam Args + * 对象的类型Args,当Args不为空时,会将数据按`std::tuple`格式将数据反序列化保存到参数中。 + * @param t 待反序列化的对象t + * @param data 起始地址 + * @param size 数据长度 + * @param offset 跳过开头字节的长度 + * @param args 待反序列化的对象args + * @return struct_pack::errc + * 返回错误码,如果返回值不等于`struct_pack::errc::ok`,说明反序列化失败 + * + * 当反序列化失败时,t的值可能被部分修改。 + */ +template +[[nodiscard]] struct_pack::errc deserialize_to_with_offset( + T &t, const char *data, size_t size, size_t &offset, Args &...args); + +/** + * \ingroup struct_pack + * @brief 反序列化视图中的数据,并按值返回 + * + * @tparam Args + * 反序列化对象的类型,至少应填写一个,当填写多个时,按`std::tuple`类型反序列化 + * @tparam View 视图类型,需满足`struct_pack::detail::deserialize_view`约束 + * @param v 存有struct_pack序列化数据的视图 + * @return struct_pack::expected + * 类型,当Args有多个参数时,返回值类型为`struct_pack::expected, + * struct_pack::errc>`。该类型存储了反序列化结果或`struct_pack::errc`类型的错误码。详见`struct_pack::expected` + * * 样例代码: + @code{.cpp} + person p; + auto p2 = struct_pack::deserialize(buffer); + assert(p2); // check if no error + assert(p2.value() == p2); // check if value equal. + @endcode * - * ```cpp - * std::string hi = "Hello struct_pack"; - * std::vector ho; - * for (int i = 0; i < 100; ++i) { - * struct_pack::serialize_to(ho, hi + std::to_string(i)); - * } - * for (size_t i = 0, offset = 0; i < 100; ++i) { - * auto ret = struct_pack::deserialize_with_offset(ho, offset); - * CHECK(ret.errc == std::errc{}); - * CHECK(ret.value == hi + std::to_string(i)); - * } - * ``` + */ +template +[[nodiscard]] auto deserialize(const View &v); + +/** + * \ingroup struct_pack + * + * @brief 反序列化内存中的数据,并按值返回 + * + * @tparam Args + * 反序列化对象的类型,至少应填写一个,当填写多个时,按`std::tuple`类型反序列化 + * @param data 起始地址 + * @param size 数据长度 + * @return struct_pack::expected + * 类型,当Args有多个参数时,返回值类型为`struct_pack::expected, + * struct_pack::errc>`。该类型存储了反序列化结果或`struct_pack::errc`类型的错误码。详见`struct_pack::expected` + * + * 样例代码: + @code{.cpp} + person p; + auto p2 = struct_pack::deserialize(buffer.data(), buffer.size()); + assert(p2); // check if no error + assert(p2.value() == p2); // check if value equal. + @endcode * - * @tparam T 反序列化的类型,需要用户手动填写 - * @tparam Args 函数允许填入多个类型参数,然后按std::tuple类型反序列化 - * @tparam Byte - * @param data 指向保存序列化结果的内存首地址。 - * @param size 该段内存的长度 - * @param offset 反序列化起始位置的偏移量 - * @return 当用户只填入一个类型参数时,返回struct_pack::expected类型,该expected类型包含反序列化结果T或std::errc类型的错误码。 - * 否则当用户填入多个类型参数时,返回struct_pack::expected,std::errc>类型 */ -template -[[nodiscard]] STRUCT_PACK_INLINE auto deserialize_with_offset(const Byte *data, - size_t size, - size_t &offset); +template +[[nodiscard]] auto deserialize(const char *data, size_t size); -/*! +/** * \ingroup struct_pack - * @tparam T 反序列化的类型,需要用户手动填写 - * @tparam Args 函数允许填入多个类型参数,然后按std::tuple类型反序列化 - * @tparam View - * @param v 待反序列化的数据,其包含成员函数data()和size() - * @param offset 反序列化起始位置的偏移量。 - * @return 当用户只填入一个类型参数时,返回struct_pack::expected类型,该expected类型包含反序列化结果T或std::errc类型的错误码。 - * 否则,当用户填入多个类型参数时,返回struct_pack::expected,std::errc>类型 + * @brief 反序列化输入流中的数据,并按值返回 + * + * @tparam Args + 反序列化对象的类型,至少应填写一个,当填写多个时,按`std::tuple`类型反序列化 + * @tparam Reader 输入流类型Reader,该类型需要满足约束`struct_pack::reader_t` + * @param v 输入流 + * @return struct_pack::expected + * 类型,当Args有多个参数时,返回值类型为`struct_pack::expected, + * struct_pack::errc>`。该类型存储了反序列化结果或`struct_pack::errc`类型的错误码。详见`struct_pack::expected` + * + * 样例代码: + @code{.cpp} + person p; + std::ifstream ifs("struct_pack_demo.data", + std::ofstream::in | std::ofstream::binary); + auto p2 = struct_pack::deserialize(ifs); + assert(p2); // check if no error + assert(p2.value() == p2); // check if value equal. + @endcode */ -template -[[nodiscard]] STRUCT_PACK_INLINE auto deserialize_with_offset(const View &v, - size_t &offset); +template +[[nodiscard]] auto deserialize(Reader &v); -/*! +/** + * \ingroup struct_pack + * @brief 反序列化视图中的数据,并按值返回,并在参数中返回消耗的数据长度。 + * + * @tparam Args + 反序列化对象的类型,至少应填写一个,当填写多个时,按`std::tuple`类型反序列化 + * @tparam View 视图类型,需满足`struct_pack::detail::deserialize_view`约束 + * @param v 存有struct_pack序列化数据的视图 + * @param consume_len 出参,保存消耗的数据长度。 + * @return struct_pack::expected + * 类型,当Args有多个参数时,返回值类型为`struct_pack::expected, + * struct_pack::errc>`。该类型存储了反序列化结果或`struct_pack::errc`类型的错误码。详见`struct_pack::expected` + * + * 当错误发生时,consume_len会被设为0。 + */ +template +[[nodiscard]] auto deserialize(const View &v, size_t &consume_len); + +/** + * \ingroup struct_pack + * @brief 反序列化内存中的数据,并按值返回,并在参数中返回消耗的数据长度。 + * + * @tparam Args + * 反序列化对象的类型,至少应填写一个,当填写多个时,按`std::tuple`类型反序列化 + * @param data 起始地址 + * @param size 数据长度 + * @param consume_len 出参,保存消耗的数据长度。 + * @return struct_pack::expected + * 类型,当Args有多个参数时,返回值类型为`struct_pack::expected, + * struct_pack::errc>`。该类型存储了反序列化结果或`struct_pack::errc`类型的错误码。详见`struct_pack::expected` + * + * 当错误发生时,consume_len会被设为0。 + */ +template +[[nodiscard]] auto deserialize(const char *data, size_t size, + size_t &consume_len); + +/** * \ingroup struct_pack + * @brief 按给定配置反序列化视图中的数据,并按值返回 + * + * @tparam conf 显式指定的序列化配置,详见`struct_pack::sp_config` + * @tparam Args + * 反序列化对象的类型,至少应填写一个,当填写多个时,按`std::tuple`类型反序列化 + * @tparam View 视图类型,需满足`struct_pack::detail::deserialize_view`约束 + * @param v 存有struct_pack序列化数据的视图 + * @return struct_pack::expected + * 类型,当Args有多个参数时,返回值类型为`struct_pack::expected, + * struct_pack::errc>`。该类型存储了反序列化结果或`struct_pack::errc`类型的错误码。详见`struct_pack::expected` + * * 样例代码: + @code{.cpp} + person p; + auto p2 = struct_pack::deserialize(buffer); + assert(p2); // check if no error + assert(p2.value() == p2); // check if value equal. + @endcode * - * ```cpp - * person p{20, "tom"}; - * auto buffer = struct_pack::serialize(p); - * person p2; - * auto ec = struct_pack::deserialize_to(p2, buffer); - * assert(ec == std::errc{}); - * assert(p == p2); - * ``` - * @tparam T + */ +template +[[nodiscard]] auto deserialize(const View &v); + +/** + * \ingroup struct_pack + * + * @brief 按给定配置反序列化内存中的数据,并按值返回 + * + * @tparam conf 显式指定的序列化配置,详见`struct_pack::sp_config` * @tparam Args - * @tparam Byte - * @param t 保存反序列化的结果。 - * @param data 指向序列化buffer的指针 - * @param size buffer的长度 - * @param args - * 函数允许填入多个引用,然后按std::tuple类型解析数据并反序列化。 - * 受C++推导规则限制,args置于参数末尾。 - * @return - * std::errc类型的错误码。另外,t作为出参保存序列化结果。当有错误发生时,t的值是未定义的。 - */ -template -[[nodiscard]] STRUCT_PACK_INLINE std::errc deserialize_to(T &t, - const Byte *data, - size_t size, - Args... args); -/*! + * 反序列化对象的类型,至少应填写一个,当填写多个时,按`std::tuple`类型反序列化 + * @param data 起始地址 + * @param size 数据长度 + * @return struct_pack::expected + * 类型,当Args有多个参数时,返回值类型为`struct_pack::expected, + * struct_pack::errc>`。该类型存储了反序列化结果或`struct_pack::errc`类型的错误码。详见`struct_pack::expected` + * + * 样例代码: + @code{.cpp} + person p; + auto p2 = struct_pack::deserialize(buffer.data(), buffer.size()); + assert(p2); // check if no error + assert(p2.value() == p2); // check if value equal. + @endcode + * + */ +template +[[nodiscard]] auto deserialize(const char *data, size_t size); + +/** * \ingroup struct_pack - * @tparam T + * @brief 按给定配置反序列化输入流中的数据,并按值返回 + * + * @tparam conf 显式指定的序列化配置,详见`struct_pack::sp_config` * @tparam Args - * @tparam View - * @param t 保存反序列化的结果。 - * @param v 待反序列化的数据,其包含成员函数data()和size() - * @param args - * 函数允许填入多个引用,然后按std::tuple类型解析数据并反序列化。 - * 受C++推导规则限制,args置于参数末尾。 - * @return - * std::errc类型的错误码。另外,t作为出参保存序列化结果。当有错误发生时,t的值是未定义的。 + 反序列化对象的类型,至少应填写一个,当填写多个时,按`std::tuple`类型反序列化 + * @tparam Reader 输入流类型Reader,该类型需要满足约束`struct_pack::reader_t` + * @param v 输入流 + * @return struct_pack::expected + * 类型,当Args有多个参数时,返回值类型为`struct_pack::expected, + * struct_pack::errc>`。该类型存储了反序列化结果或`struct_pack::errc`类型的错误码。详见`struct_pack::expected` + * + * 样例代码: + @code{.cpp} + person p; + std::ifstream ifs("struct_pack_demo.data", + std::ofstream::in | std::ofstream::binary); + auto p2 = struct_pack::deserialize(ifs); + assert(p2); // check if no error + assert(p2.value() == p2); // check if value equal. + @endcode */ -template -[[nodiscard]] STRUCT_PACK_INLINE std::errc deserialize_to(T &t, const View &v, - Args... args); +template +[[nodiscard]] auto deserialize(Reader &v); -/*! +/** + * \ingroup struct_pack + * @brief + 按给定配置反序列化视图中的数据,并按值返回,并在参数中返回消耗的数据长度。 + * + * @tparam conf 显式指定的序列化配置,详见`struct_pack::sp_config` + * @tparam Args + 反序列化对象的类型,至少应填写一个,当填写多个时,按`std::tuple`类型反序列化 + * @tparam View 视图类型,需满足`struct_pack::detail::deserialize_view`约束 + * @param v 存有struct_pack序列化数据的视图 + * @param consume_len 出参,保存消耗的数据长度。 + * @return struct_pack::expected + * 类型,当Args有多个参数时,返回值类型为`struct_pack::expected, + * struct_pack::errc>`。该类型存储了反序列化结果或`struct_pack::errc`类型的错误码。详见`struct_pack::expected` + * + * 当错误发生时,consume_len会被设为0。 + */ +template +[[nodiscard]] auto deserialize(const View &v, size_t &consume_len); + +/** + * \ingroup struct_pack + * @brief + * 按给定配置反序列化内存中的数据,并按值返回,并在参数中返回消耗的数据长度。 + * + * @tparam conf 显式指定的序列化配置,详见`struct_pack::sp_config` + * @tparam Args + * 反序列化对象的类型,至少应填写一个,当填写多个时,按`std::tuple`类型反序列化 + * @param data 起始地址 + * @param size 数据长度 + * @param consume_len 出参,保存消耗的数据长度。 + * @return struct_pack::expected + * 类型,当Args有多个参数时,返回值类型为`struct_pack::expected, + * struct_pack::errc>`。该类型存储了反序列化结果或`struct_pack::errc`类型的错误码。详见`struct_pack::expected` + * + * 当错误发生时,consume_len会被设为0。 + */ +template +[[nodiscard]] auto deserialize(const char *data, size_t size, + size_t &consume_len); + +/** + * \ingroup struct_pack + * + * @brief 从视图中反序列化目的对象并保存到返回值,反序列化时跳过开头的若干字节 + * + * @tparam Args + * 反序列化对象的类型,至少应填写一个,当填写多个时,按`std::tuple`类型反序列化 + * @tparam View 视图类型,需满足`struct_pack::detail::deserialize_view`约束 + * @param v 存有struct_pack序列化数据的视图 + * @param offset 反序列化起始位置的偏移量 + * @return struct_pack::expected + * 类型,当Args有多个参数时,返回值类型为`struct_pack::expected, + * struct_pack::errc>`。该类型存储了反序列化结果或`struct_pack::errc`类型的错误码。详见`struct_pack::expected` + */ +template +[[nodiscard]] auto deserialize_with_offset(const View &v, size_t &offset); + +/** * \ingroup struct_pack + * @brief + * 从内存中反序列化目的对象并保存到返回值,反序列化时跳过开头的若干字节 + * + * @tparam Args + * 反序列化对象的类型,至少应填写一个,当填写多个时,按`std::tuple`类型反序列化 + * @param data 起始地址 + * @param size 数据长度 + * @param offset 反序列化起始位置的偏移量 + * @return struct_pack::expected + * 类型,当Args有多个参数时,返回值类型为`struct_pack::expected, + * struct_pack::errc>`。该类型存储了反序列化结果或`struct_pack::errc`类型的错误码。详见`struct_pack::expected` + * * 样例代码: * - * ```cpp - * std::string hi = "Hello struct_pack"; + @code{.cpp} + * std::string hi = "Hello struct_pack"; * std::vector ho; * for (int i = 0; i < 100; ++i) { * struct_pack::serialize_to(ho, hi + std::to_string(i)); * } * for (size_t i = 0, offset = 0; i < 100; ++i) { - * std::string value; - * auto ec = struct_pack::deserialize_to_with_offset(value, ho, offset); - * CHECK(ec == std::errc{}); - * CHECK(value == hi + std::to_string(i)); + * auto ret = struct_pack::deserialize_with_offset(ho, offset); + * CHECK(ret.errc == std::errc{}); + * CHECK(ret.value == hi + std::to_string(i)); * } - * ``` - * @tparam T - * @tparam Args - * @tparam Byte - * @param t 保存反序列化的结果。 - * @param data 指向保存序列化结果的内存首地址。 - * @param size 内存的长度。 - * @param offset 反序列化起始位置的偏移量。 - * @param args - * 函数允许填入多个引用,然后按std::tuple类型解析数据并反序列化。 - * 受C++推导规则限制,args置于参数末尾。 - * @return - * std::errc类型的错误码。另外,t作为出参,保存序列化结果。offset作为出参,会被更新为反序列化对象尾部相对于data的offset。 - * 当有错误发生时,t的值是未定义的,而offset则不会被更新。 + @endcode + * */ -template -[[nodiscard]] STRUCT_PACK_INLINE std::errc deserialize_to_with_offset( - T &t, const Byte *data, size_t size, size_t &offset, Args... args); +template +[[nodiscard]] auto deserialize_with_offset(const char *data, size_t size, + size_t &offset); -/*! +/** + * * \ingroup struct_pack - * 样例代码: + * @brief 从视图中反序列化一个字段并保存到目的对象 + * + * @tparam T 反序列化对象的类型 + * @tparam I 反序列化对象的第I个字段(从0开始计数) + * @tparam conf 序列化配置,详见`struct_pack::sp_config` + * @tparam Field 字段类型 + * @tparam View 视图类型,需满足`struct_pack::detail::deserialize_view`约束 + * @param dst 目的对象 + * @param v 数据视图 + * @return struct_pack::errc + * 返回错误码,如果返回值不等于`struct_pack::errc::ok`,说明反序列化失败 + * + * 当反序列化失败时,t的值可能被部分修改。 * - * ```cpp - * person p{20, "tom"}; - * auto buffer = serialize(p); + * 样例代码 + @code{.cpp} + person p{20, "tom"}; + auto buffer = serialize(p); + int age; + auto ec = get_field_to(age, buffer); + CHECK(ec == struct_pack::errc{}); + CHECK(age == 20); + @endcode * - * auto [ec, age] = get_field(buffer); - * CHECK(ec == std::errc{}); - * CHECK(age == 20); + */ +template +[[nodiscard]] struct_pack::errc get_field_to(Field &dst, const View &v); +/** + * \ingroup struct_pack + * @brief 从内存中反序列化一个字段并保存到目的对象 + * + * @tparam T 反序列化对象的类型 + * @tparam I 反序列化对象的第I个字段(从0开始计数) + * @tparam conf 序列化配置,详见`struct_pack::sp_config` + * @tparam Field 字段类型 + * @param dst 目的对象 + * @param v 数据视图 + * @param data 起始地址 + * @param size 数据长度 + * @return struct_pack::errc + * 返回错误码,如果返回值不等于`struct_pack::errc::ok`,说明反序列化失败 + * + * 当反序列化失败时,t的值可能被部分修改。 + * 样例代码 + @code{.cpp} + person p{20, "tom"}; + auto buffer = serialize(p); + std::string name; + auto ec = get_field_to(name, buffer.data(), buffer.size()); + CHECK(ec == struct_pack::errc{}); + CHECK(name == "tom"); + @endcode + */ +template +[[nodiscard]] struct_pack::errc get_field_to(Field &dst, const char *data, + size_t size); + +/** + * \ingroup struct_pack + * @brief 从输入流中反序列化一个字段并保存到目的对象 + * + * @tparam T 反序列化对象的类型 + * @tparam I 反序列化对象的第I个字段(从0开始计数) + * @tparam conf 序列化配置,详见`struct_pack::sp_config` + * @tparam Field 字段类型 + * @tparam Reader 输入流类型,需要满足`struct_pack::reader_t`约束 + * @param dst 目的对象 + * @param reader 输入流 + * @return struct_pack::errc + * 返回错误码,如果返回值不等于`struct_pack::errc::ok`,说明反序列化失败 + * + * 当反序列化失败时,t的值可能被部分修改。 + */ +template +[[nodiscard]] struct_pack::errc get_field_to(Field &dst, Reader &reader); + +/** + * \ingroup struct_pack + * @brief 从视图中反序列化一个字段并返回 * - * auto [ec, name] = get_field(buffer.data(), buffer.size()); - * CHECK(ec == std::errc{}); - * CHECK(name == "tom"); - * ``` + * @tparam T 反序列化对象的类型 + * @tparam I 反序列化对象的第I个字段(从0开始计数) + * @tparam conf 序列化配置,详见`struct_pack::sp_config` + * @tparam View 视图类型,需满足`struct_pack::detail::deserialize_view`约束 + * @param v 视图 + * @return struct_pack::expected + * 类型,其中TI代表T的第I个字段。该类型存储了反序列化结果或`struct_pack::errc`类型的错误码。详见`struct_pack::expected` * - * @tparam T - * @tparam I - * @tparam Byte - * @param data 指向保存序列化结果的内存首地址。 - * @param size 内存的长度 - * @return - * expected类型,包含反序列化的结果(T的第I个字段)或std::errc类型的错误码。 + * 样例代码 + @code{.cpp} + person p{20, "tom"}; + auto buffer = serialize(p); + std::string name; + auto result = get_field_to(name, buffer); + CHECK(result); + CHECK(result.value() == "tom"); + @endcode */ -template -[[nodiscard]] STRUCT_PACK_INLINE auto get_field(const Byte *data, size_t size); +template +[[nodiscard]] auto get_field(const View &v); -/*! +/** * \ingroup struct_pack - * 样例代码: + * @brief 从内存中反序列化一个字段并返回 + * + * @tparam T 反序列化对象的类型 + * @tparam I 反序列化对象的第I个字段(从0开始计数) + * @tparam conf 序列化配置,详见`struct_pack::sp_config` + * @param data 起始地址 + * @param size 数据长度 + * @return struct_pack::expected + * 类型,其中TI代表T的第I个字段。该类型存储了反序列化结果或`struct_pack::errc`类型的错误码。详见`struct_pack::expected` + * + * 样例代码 + @code{.cpp} + person p{20, "tom"}; + auto buffer = serialize(p); + std::string name; + auto result = get_field_to(name, buffer.data(), buffer.size()); + CHECK(result); + CHECK(result.value() == "tom"); + @endcode + */ +template +[[nodiscard]] auto get_field(const char *data, size_t size); + +/** + * \ingroup struct_pack + * @brief 从输入流中反序列化一个字段并返回 + * + * @tparam T 反序列化对象的类型 + * @tparam I 反序列化对象的第I个字段(从0开始计数) + * @tparam conf 序列化配置,详见`struct_pack::sp_config` + * @tparam Reader 输入流类型,需要满足`struct_pack::reader_t`约束 + * @param reader 输入流 + * @return struct_pack::expected + * 类型,其中TI代表T的第I个字段。该类型存储了反序列化结果或`struct_pack::errc`类型的错误码。详见`struct_pack::expected` + */ +template +[[nodiscard]] auto get_field(Reader &reader); + +/** + * \ingroup struct_pack + * @brief 从视图中反序列化派生类到基类的指针 + * + * @tparam BaseClass 基类类型 + * @tparam DerivedClasses 所有可能的派生类类型 + * @tparam View 视图类型,需满足`struct_pack::detail::deserialize_view`约束 + * @param v 视图 + * @return struct_pack::expected, + struct_pack::errc> + * 类型,该类型存储了反序列化结果(`std::unique_ptr`)或`struct_pack::errc`类型的错误码。详见`struct_pack::expected` + * + * 本函数用于在不知道派生类具体类型的情况下,将其反序列化到基类的指针。 + * + * 样例代码 + @code{.cpp} + // base + // / | + // obj1 obj2 + // | + // obj3 + auto ret = struct_pack::serialize(obj3{}); + auto result = + struct_pack::deserialize_derived_class(ret); + assert(result.has_value()); // check deserialize ok + std::unique_ptr ptr = std::move(result.value()); + assert(ptr != nullptr); + @endcode + */ +template +[[nodiscard]] struct_pack::expected, + struct_pack::errc> +deserialize_derived_class(const View &v); + +/** + * \ingroup struct_pack + * @brief 从内存中反序列化派生类到基类的指针 + * + * @tparam BaseClass 基类类型 + * @tparam DerivedClasses 所有可能的派生类类型 + * @param data 起始地址 + * @param size 数据长度 + * @return struct_pack::expected, + struct_pack::errc> + * 类型,该类型存储了反序列化结果(`std::unique_ptr`)或`struct_pack::errc`类型的错误码。详见`struct_pack::expected` * - * ```cpp - * person p{20, "tom"}; - * auto buffer = serialize(p); - * - * auto [ec, age] = get_field(buffer); - * CHECK(ec == std::errc{}); - * CHECK(age == 20); - * - * auto [ec, name] = get_field(buffer.data(), buffer.size()); - * CHECK(ec == std::errc{}); - * CHECK(name == "tom"); - * ``` - * - * @tparam T - * @tparam I - * @tparam Field - * @tparam Byte - * @param data 指向保存序列化结果的内存首地址。 - * @param size 内存的长度 - * @return - * std::errc类型的错误码。另外,t作为出参,返回反序列化的结果。当有错误发生时,t的值是未定义的。 - */ -template -[[nodiscard]] STRUCT_PACK_INLINE std::errc get_field_to(Field &t, - const Byte *data, - size_t size); + * 本函数用于在不知道派生类具体类型的情况下,将其反序列化到基类的指针。 + * + * 样例代码 + @code{.cpp} + // base + // / | + // obj1 obj2 + // | + // obj3 + auto ret = struct_pack::serialize(obj3{}); + auto result = + struct_pack::deserialize_derived_class(ret.data(), + ret.size()); + assert(result.has_value()); // check deserialize ok + std::unique_ptr ptr = std::move(result.value()); + assert(ptr != nullptr); + @endcode + */ +template +[[nodiscard]] struct_pack::expected, + struct_pack::errc> +deserialize_derived_class(const char *data, size_t size); + +/** + * \ingroup struct_pack + * @brief 从输入流中反序列化派生类到基类的指针 + * + * @tparam BaseClass 基类类型 + * @tparam DerivedClasses 所有可能的派生类类型 + * @tparam Reader 输入流类型,需要满足`struct_pack::reader_t`约束 + * @param reader + * @return struct_pack::expected, + struct_pack::errc> + * 类型,该类型存储了反序列化结果(`std::unique_ptr`)或`struct_pack::errc`类型的错误码。详见`struct_pack::expected` + */ +template +[[nodiscard]] struct_pack::expected, + struct_pack::errc> +deserialize_derived_class(Reader &reader); } // namespace struct_pack diff --git a/website/docs/zh/struct_pack/struct_pack_intro.md b/website/docs/zh/struct_pack/struct_pack_intro.md index 6e09efbd6..1ba0f2a8e 100644 --- a/website/docs/zh/struct_pack/struct_pack_intro.md +++ b/website/docs/zh/struct_pack/struct_pack_intro.md @@ -532,7 +532,7 @@ STRUCT_PACK_DERIVED_IMPL(base, obj1, obj2, obj3); ```cpp auto ret = struct_pack::serialize(obj3{}); auto result = - struct_pack::deserialize_derived_class(buffer); + struct_pack::deserialize_derived_class(ret); assert(result.has_value()); // check deserialize ok std::unique_ptr ptr = std::move(result.value()); assert(ptr != nullptr); diff --git a/website/docs/zh/struct_pack/struct_pack_tips.md b/website/docs/zh/struct_pack/struct_pack_tips.md index af37085ac..3192632bd 100644 --- a/website/docs/zh/struct_pack/struct_pack_tips.md +++ b/website/docs/zh/struct_pack/struct_pack_tips.md @@ -43,6 +43,8 @@ struct_pack允许通过`struct_pack::sp_config`来配置序列化生成的元数 | DISABLE_META_INFO| 禁用元信息和4字节的类型校验码,从而减小二进制数据的体积| | ENCODING_WITH_VARINT| 当前结构体的整数(int32_t,int64_t,uint32_t,uint64_t)将启用变长编码| | USE_FAST_VARINT| 对整数启用快速变长编码,该编码具有更好的反序列化性能,小字段下体积也更小| + + 需要注意的是,当序列化配置了DISABLE_META_INFO选项时,必须保证反序列化也使用了该选项,否则行为未定义,大概率反序列化会失败。 此外,需要注意的是,如果结构体A嵌套了结构体B,则对A的配置不会对B生效。