├── .gitignore ├── LICENCE ├── test ├── CMakeLists.txt ├── equal_test.cpp ├── print_test.cpp ├── enum_reflection_test.cpp ├── bit_fields_test.cpp ├── struct_reflection_test.cpp └── serialization_test.cpp ├── mozi ├── enum_reflection.hpp ├── bit_fields.hpp ├── struct_reflection.hpp ├── net_pack.hpp ├── copy.hpp ├── enum_reflection_print.hpp ├── net_pack_core.hpp ├── struct_reflection_print.hpp ├── span.hpp ├── net_pack_struct_reflection.hpp ├── equal.hpp ├── struct_reflection_equal.hpp ├── net_pack_array.hpp ├── compile_time_string.hpp ├── net_pack_bit_fields.hpp ├── compare.hpp ├── struct_reflection_copy.hpp ├── struct_reflection_compare.hpp ├── net_pack_basic.hpp ├── type_traits.hpp ├── bit_fields_core.hpp ├── print.hpp ├── struct_reflection_core.hpp ├── serialization.hpp └── enum_reflection_core.hpp ├── .clang-tidy ├── .clang-format └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | a.out 2 | a.out.* 3 | build* 4 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT LICENCE 2 | 3 | Copyright (c) Wu Yongwei 4 | Copyright (c) Netcan 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a 7 | copy of this software and associated documentation files (the "Software"), 8 | to deal in the Software without restriction, including without limitation 9 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | and/or sell copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(mozi_tests) 3 | 4 | if(CMAKE_CXX_COMPILER_ID MATCHES GNU AND 5 | CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 10.0) 6 | set(CMAKE_CXX_STANDARD 20) 7 | elseif(CMAKE_CXX_COMPILER_ID STREQUAL Clang AND 8 | CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 15.0) 9 | set(CMAKE_CXX_STANDARD 20) 10 | elseif(CMAKE_CXX_COMPILER_ID STREQUAL AppleClang AND 11 | CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 14.0) 12 | set(CMAKE_CXX_STANDARD 20) 13 | elseif(MSVC) 14 | set(CMAKE_CXX_STANDARD 20) 15 | add_compile_options(/Zc:__cplusplus) 16 | else() 17 | set(CMAKE_CXX_STANDARD 17) 18 | endif() 19 | 20 | if(MSVC) 21 | add_compile_options(/W3) 22 | else() 23 | add_compile_options(-Wall -Wextra -Wsign-compare -Wsign-conversion) 24 | endif() 25 | 26 | find_package(Catch2 3 REQUIRED) 27 | 28 | include_directories(..) 29 | add_executable(mozi_test 30 | enum_reflection_test.cpp 31 | struct_reflection_test.cpp 32 | equal_test.cpp 33 | print_test.cpp 34 | serialization_test.cpp 35 | bit_fields_test.cpp 36 | ) 37 | target_link_libraries(mozi_test PRIVATE Catch2::Catch2WithMain) 38 | 39 | include(CTest) 40 | include(Catch) 41 | catch_discover_tests(mozi_test) 42 | -------------------------------------------------------------------------------- /mozi/enum_reflection.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MOZI_ENUM_REFLECTION_HPP 25 | #define MOZI_ENUM_REFLECTION_HPP 26 | 27 | #include "enum_reflection_core.hpp" // IWYU pragma: export 28 | #include "enum_reflection_print.hpp" // IWYU pragma: export 29 | 30 | #endif // MOZI_ENUM_REFLECTION_HPP 31 | -------------------------------------------------------------------------------- /mozi/bit_fields.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MOZI_BIT_FIELDS_HPP 25 | #define MOZI_BIT_FIELDS_HPP 26 | 27 | #include "bit_fields_core.hpp" // IWYU pragma: export 28 | #include "struct_reflection_copy.hpp" // IWYU pragma: keep 29 | #include "struct_reflection_equal.hpp" // IWYU pragma: keep 30 | #include "struct_reflection_print.hpp" // IWYU pragma: keep 31 | 32 | #endif // MOZI_BIT_FIELDS_HPP 33 | -------------------------------------------------------------------------------- /mozi/struct_reflection.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023-2024 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MOZI_STRUCT_REFLECTION_HPP 25 | #define MOZI_STRUCT_REFLECTION_HPP 26 | 27 | #include "struct_reflection_core.hpp" // IWYU pragma: export 28 | #include "struct_reflection_compare.hpp" // IWYU pragma: export 29 | #include "struct_reflection_copy.hpp" // IWYU pragma: export 30 | #include "struct_reflection_print.hpp" // IWYU pragma: export 31 | 32 | #endif // MOZI_STRUCT_REFLECTION_HPP 33 | -------------------------------------------------------------------------------- /mozi/net_pack.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MOZI_NET_PACK_HPP 25 | #define MOZI_NET_PACK_HPP 26 | 27 | #include "net_pack_core.hpp" // IWYU pragma: export 28 | #include "net_pack_basic.hpp" // IWYU pragma: keep 29 | #include "net_pack_array.hpp" // IWYU pragma: keep 30 | #include "net_pack_struct_reflection.hpp" // IWYU pragma: keep 31 | #include "net_pack_bit_fields.hpp" // IWYU pragma: keep 32 | 33 | #endif // MOZI_NET_PACK_HPP 34 | -------------------------------------------------------------------------------- /mozi/copy.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MOZI_COPIER_HPP 25 | #define MOZI_COPIER_HPP 26 | 27 | #include // std::forward/move 28 | #include "type_traits.hpp" // mozi::remove_cvref 29 | 30 | namespace mozi { 31 | 32 | template 33 | struct copier { 34 | constexpr void operator()(const T& src, U& dest) const 35 | { 36 | dest = src; 37 | } 38 | constexpr void operator()(T&& src, U& dest) const 39 | { 40 | dest = std::move(src); 41 | } 42 | }; 43 | 44 | namespace detail { 45 | 46 | struct copy_fn { 47 | template 48 | constexpr void operator()(T&& src, U& dest) const 49 | { 50 | copier, mozi::remove_cvref_t>{}( 51 | std::forward(src), dest); 52 | } 53 | }; 54 | 55 | } // namespace detail 56 | 57 | inline constexpr detail::copy_fn copy{}; 58 | 59 | } // namespace mozi 60 | 61 | #endif // MOZI_COPIER_HPP 62 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: 3 | bugprone-*, 4 | -bugprone-chained-comparison, 5 | -bugprone-sizeof-container, 6 | clang-diagnostic-*, 7 | clang-analyzer-*, 8 | -clang-analyzer-optin.core.EnumCastOutOfRange, 9 | cppcoreguidelines-*, 10 | -cppcoreguidelines-avoid-c-arrays, 11 | -cppcoreguidelines-avoid-do-while, 12 | -cppcoreguidelines-avoid-magic-numbers, 13 | -cppcoreguidelines-avoid-non-const-global-variables, 14 | -cppcoreguidelines-macro-to-enum, 15 | -cppcoreguidelines-macro-usage, 16 | -cppcoreguidelines-owning-memory, 17 | -cppcoreguidelines-pro-bounds-array-to-pointer-decay, 18 | -cppcoreguidelines-pro-bounds-constant-array-index, 19 | -cppcoreguidelines-pro-bounds-pointer-arithmetic, 20 | -cppcoreguidelines-pro-type-static-cast-downcast, 21 | -cppcoreguidelines-pro-type-vararg, 22 | llvm-namespace-comment, 23 | modernize-*, 24 | -modernize-avoid-c-arrays, 25 | -modernize-macro-to-enum, 26 | -modernize-use-constraints, 27 | -modernize-deprecated-headers, 28 | -modernize-use-designated-initializers, 29 | -modernize-use-nodiscard, 30 | -modernize-use-trailing-return-type, 31 | readability-*, 32 | -readability-identifier-length, 33 | -readability-magic-numbers, 34 | -readability-named-parameter, 35 | -readability-qualified-auto 36 | WarningsAsErrors: '' 37 | HeaderFilterRegex: 'mozi/.*' 38 | FormatStyle: file 39 | ExtraArgs: ['-std=c++20'] 40 | CheckOptions: 41 | - key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions 42 | value: 'true' 43 | - key: llvm-namespace-comment.ShortNamespaceLines 44 | value: '1' 45 | - key: readability-function-cognitive-complexity.IgnoreMacros 46 | value: 'true' 47 | - key: readability-implicit-bool-conversion.AllowPointerConditions 48 | value: 'true' 49 | - key: readability-identifier-naming.MacroDefinitionCase 50 | value: 'UPPER_CASE' 51 | - key: readability-identifier-naming.PrivateMemberSuffix 52 | value: '_' 53 | - key: readability-identifier-naming.VariableCase 54 | value: 'lower_case' 55 | ... 56 | -------------------------------------------------------------------------------- /mozi/enum_reflection_print.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MOZI_ENUM_REFLECTION_PRINT_HPP 25 | #define MOZI_ENUM_REFLECTION_PRINT_HPP 26 | 27 | #include // std::ostream 28 | #include // std::enable_if 29 | #include "enum_reflection_core.hpp" // IWYU pragma: keep mozi::to_string 30 | #include "print.hpp" // mozi::printer 31 | #include "type_traits.hpp" // mozi::is_reflected_enum 32 | 33 | namespace mozi { 34 | 35 | template 36 | struct printer< 37 | T, std::enable_if_t && !is_scoped_enum_v>> { 38 | void operator()(T value, std::ostream& os, int /*depth*/) const 39 | { 40 | os << to_string(value); 41 | } 42 | }; 43 | 44 | template 45 | struct printer< 46 | T, std::enable_if_t && is_scoped_enum_v>> { 47 | void operator()(T value, std::ostream& os, int /*depth*/) const 48 | { 49 | os << to_string(value, mozi::enum_to_string::show_name); 50 | } 51 | }; 52 | 53 | } // namespace mozi 54 | 55 | #endif // MOZI_ENUM_REFLECTION_PRINT_HPP 56 | -------------------------------------------------------------------------------- /test/equal_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #include "mozi/equal.hpp" // mozi::equal 25 | #include // std::array 26 | #include // std::string_view 27 | #include // std::vector 28 | #include // Catch2 test macros 29 | 30 | TEST_CASE("equal") 31 | { 32 | SECTION("standard types") 33 | { 34 | long l = 42; 35 | CHECK(mozi::equal(l, 42)); 36 | 37 | std::string_view sv{"hello"}; 38 | CHECK(mozi::equal(sv, "hello")); 39 | CHECK_FALSE(mozi::equal(sv, "Hell")); 40 | 41 | std::vector v1{1, 2, 3}; 42 | std::vector v2 = v1; 43 | std::vector v3 = v1; 44 | v3[0] = 0; 45 | CHECK(mozi::equal(v1, v2)); 46 | CHECK_FALSE(mozi::equal(v1, v3)); 47 | } 48 | 49 | SECTION("arrays") 50 | { 51 | int a1[]{1, 2, 3}; 52 | int a2[]{1, 2, 3}; 53 | CHECK(mozi::equal(a1, a2)); 54 | a1[0] = 0; 55 | CHECK_FALSE(mozi::equal(a1, a2)); 56 | 57 | std::array a3{1, 2, 3}; 58 | std::array a4 = a3; 59 | CHECK(mozi::equal(a3, a4)); 60 | a3[0] = 0; 61 | CHECK_FALSE(mozi::equal(a3, a4)); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /mozi/net_pack_core.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MOZI_NET_PACK_CORE_HPP 25 | #define MOZI_NET_PACK_CORE_HPP 26 | 27 | #include // CHAR_BIT 28 | #include "serialization.hpp" // mozi::serialize/deserialize/... 29 | 30 | namespace mozi::net_pack { 31 | 32 | template 33 | struct serializer; 34 | 35 | namespace detail { 36 | 37 | struct serialize_fn { 38 | static_assert(CHAR_BIT == 8); 39 | 40 | template 41 | void operator()(const T& value, serialize_t& dest) const 42 | { 43 | mozi::serialize(value, dest, serializer_list{}); 44 | } 45 | 46 | template 47 | serialize_t operator()(const T& value) const 48 | { 49 | serialize_t result; 50 | operator()(value, result); 51 | return result; 52 | } 53 | }; 54 | 55 | struct deserialize_fn { 56 | template 57 | deserialize_result operator()(T& value, deserialize_t& src) const 58 | { 59 | return mozi::deserialize(value, src, serializer_list{}); 60 | } 61 | }; 62 | 63 | } // namespace detail 64 | 65 | inline constexpr detail::serialize_fn serialize{}; 66 | inline constexpr detail::deserialize_fn deserialize{}; 67 | 68 | } // namespace mozi::net_pack 69 | 70 | #endif // MOZI_NET_PACK_CORE_HPP 71 | -------------------------------------------------------------------------------- /mozi/struct_reflection_print.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023-2024 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MOZI_STRUCT_REFLECTION_PRINT_HPP 25 | #define MOZI_STRUCT_REFLECTION_PRINT_HPP 26 | 27 | #include // std::ostream 28 | #include // std::enable_if 29 | #include "compile_time_string.hpp" // MOZI_CTS_GET_VALUE 30 | #include "print.hpp" // mozi::printer/print 31 | #include "struct_reflection_core.hpp" // mozi::for_each 32 | #include "type_traits.hpp" // mozi::is_reflected_struct/... 33 | 34 | namespace mozi { 35 | 36 | namespace detail { 37 | 38 | // Field output function for reflected structs 39 | template 40 | void output_field(const T& obj, std::ostream& os, const char* name, 41 | int depth) 42 | { 43 | os << indent(depth) << name << ": "; 44 | print(obj, os, depth); 45 | } 46 | 47 | } // namespace detail 48 | 49 | template 50 | struct printer>> { 51 | void operator()(const T& obj, std::ostream& os, int depth) const 52 | { 53 | os << "{\n"; 54 | for_each(obj, [&os, depth](auto index, auto name, 55 | const auto& field) { 56 | detail::output_field(field, os, MOZI_CTS_GET_VALUE(name), 57 | depth + 1); 58 | if constexpr (index != T::_size - 1) { 59 | os << ",\n"; 60 | } else { 61 | os << '\n'; 62 | } 63 | }); 64 | os << detail::indent(depth) << "}"; 65 | } 66 | }; 67 | 68 | } // namespace mozi 69 | 70 | #endif // MOZI_STRUCT_REFLECTION_PRINT_HPP 71 | -------------------------------------------------------------------------------- /mozi/span.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024-2025 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MOZI_SPAN_HPP 25 | #define MOZI_SPAN_HPP 26 | 27 | #define MOZI_SPAN_GSL 1 28 | #define MOZI_SPAN_STD 2 29 | 30 | #ifndef MOZI_USE_SPAN 31 | #if __cplusplus > 201703L && __has_include() 32 | #define MOZI_USE_SPAN MOZI_SPAN_STD 33 | #elif __has_include() 34 | #define MOZI_USE_SPAN MOZI_SPAN_GSL 35 | #endif 36 | #endif 37 | 38 | #if MOZI_USE_SPAN == MOZI_SPAN_STD 39 | #include // IWYU pragma: export std::span 40 | #elif MOZI_USE_SPAN == MOZI_SPAN_GSL 41 | #include // IWYU pragma: export gsl::span 42 | #else 43 | #error "No span support is detected" 44 | #endif 45 | 46 | #include // std::is_same/remove_cv 47 | #include "equal.hpp" // mozi::equal 48 | 49 | namespace mozi { 50 | 51 | #if MOZI_USE_SPAN == MOZI_SPAN_STD 52 | using std::span; 53 | #else 54 | using gsl::span; 55 | #endif 56 | 57 | template 58 | struct equality_comparer, span, void> { 59 | template 60 | constexpr bool operator()(span lhs, span rhs) const 61 | { 62 | static_assert( 63 | std::is_same_v, std::remove_cv_t> && 64 | std::is_same_v, std::remove_cv_t>); 65 | if (lhs.size() != rhs.size()) { 66 | return false; 67 | } 68 | auto it1 = lhs.begin(); 69 | auto it2 = rhs.begin(); 70 | while (it1 != lhs.end()) { 71 | if (*it1 != *it2) { 72 | return false; 73 | } 74 | ++it1; 75 | ++it2; 76 | } 77 | return true; 78 | } 79 | }; 80 | 81 | } // namespace mozi 82 | 83 | #endif // MOZI_SPAN_HPP 84 | -------------------------------------------------------------------------------- /mozi/net_pack_struct_reflection.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MOZI_NET_PACK_STRUCT_REFLECTION_HPP 25 | #define MOZI_NET_PACK_STRUCT_REFLECTION_HPP 26 | 27 | #include // std::enable_if 28 | #include "net_pack_core.hpp" // mozi::net_pack::serializer 29 | #include "serialization.hpp" // mozi::serialize/deserialize/... 30 | #include "struct_reflection_core.hpp" // mozi::for_each 31 | #include "type_traits.hpp" // mozi::is_reflected_struct/... 32 | 33 | namespace mozi::net_pack { 34 | 35 | template 36 | struct serializer && 38 | !mozi::is_bit_fields_container_v>> { 39 | template 40 | static void serialize(T obj, serialize_t& dest, 41 | SerializerList serializers) 42 | { 43 | mozi::for_each( 44 | obj, [&](auto /*index*/, auto /*name*/, const auto& value) { 45 | mozi::serialize(value, dest, serializers); 46 | }); 47 | } 48 | 49 | template 50 | static deserialize_result deserialize(T& obj, deserialize_t& src, 51 | SerializerList serializers) 52 | { 53 | auto result = deserialize_result::success; 54 | mozi::for_each( 55 | obj, [&](auto /*index*/, auto /*name*/, auto& value) { 56 | if (result == deserialize_result::success) { 57 | result = mozi::deserialize(value, src, serializers); 58 | } 59 | }); 60 | return result; 61 | } 62 | }; 63 | 64 | } // namespace mozi::net_pack 65 | 66 | #endif // MOZI_NET_PACK_STRUCT_REFLECTION_HPP 67 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # BasedOnStyle: WebKit 3 | AccessModifierOffset: -4 4 | AlignConsecutiveAssignments: false 5 | AlignConsecutiveDeclarations: false 6 | AlignEscapedNewlinesLeft: false 7 | AlignOperands: true 8 | AlignTrailingComments: true 9 | AllowAllParametersOfDeclarationOnNextLine: true 10 | AllowShortBlocksOnASingleLine: false 11 | AllowShortCaseLabelsOnASingleLine: false 12 | AllowShortFunctionsOnASingleLine: Empty 13 | AllowShortIfStatementsOnASingleLine: false 14 | AllowShortLoopsOnASingleLine: false 15 | AlwaysBreakAfterDefinitionReturnType: None 16 | AlwaysBreakAfterReturnType: None 17 | AlwaysBreakBeforeMultilineStrings: false 18 | AlwaysBreakTemplateDeclarations: Yes 19 | BinPackArguments: true 20 | BinPackParameters: true 21 | BraceWrapping: 22 | AfterClass: false 23 | AfterControlStatement: false 24 | AfterEnum: false 25 | AfterFunction: true 26 | AfterNamespace: false 27 | AfterObjCDeclaration: false 28 | AfterStruct: false 29 | AfterUnion: false 30 | BeforeCatch: true 31 | BeforeElse: false 32 | IndentBraces: false 33 | SplitEmptyRecord: false 34 | BreakBeforeBinaryOperators: false 35 | BreakBeforeBraces: Custom 36 | BreakBeforeTernaryOperators: true 37 | BreakConstructorInitializers: BeforeColon 38 | ColumnLimit: 76 39 | CommentPragmas: '^ IWYU pragma:' 40 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 41 | ConstructorInitializerIndentWidth: 4 42 | ContinuationIndentWidth: 4 43 | Cpp11BracedListStyle: true 44 | DerivePointerAlignment: false 45 | ExperimentalAutoDetectBinPacking: false 46 | FixNamespaceComments: true 47 | IncludeCategories: 48 | - Regex: '^<.*/.*>$' 49 | Priority: 3 50 | - Regex: '^<.*\.h>$' 51 | Priority: 2 52 | - Regex: '^<' 53 | Priority: 1 54 | - Regex: '^".*\.h"$' 55 | Priority: 4 56 | - Regex: '^".*\.hpp"$' 57 | Priority: 5 58 | - Regex: '.*' 59 | Priority: 6 60 | IncludeIsMainRegex: '(_test)?$' 61 | IndentCaseLabels: false 62 | IndentWidth: 4 63 | IndentWrappedFunctionNames: false 64 | KeepEmptyLinesAtTheStartOfBlocks: false 65 | MacroBlockBegin: '' 66 | MacroBlockEnd: '' 67 | MaxEmptyLinesToKeep: 1 68 | NamespaceIndentation: None 69 | ObjCSpaceAfterProperty: true 70 | ObjCSpaceBeforeProtocolList: true 71 | PenaltyBreakBeforeFirstCallParameter: 19 72 | PenaltyBreakComment: 300 73 | PenaltyBreakFirstLessLess: 120 74 | PenaltyBreakString: 1000 75 | PenaltyExcessCharacter: 1000000 76 | PenaltyReturnTypeOnItsOwnLine: 60 77 | PointerAlignment: Left 78 | ReflowComments: true 79 | SortIncludes: true 80 | SpaceAfterCStyleCast: false 81 | SpaceBeforeAssignmentOperators: true 82 | SpaceBeforeParens: ControlStatements 83 | SpaceInEmptyParentheses: false 84 | SpacesBeforeTrailingComments: 1 85 | SpacesInAngles: false 86 | SpacesInContainerLiterals: true 87 | SpacesInCStyleCastParentheses: false 88 | SpacesInParentheses: false 89 | SpacesInSquareBrackets: false 90 | Standard: c++20 91 | TabWidth: 4 92 | UseTab: Never 93 | ... 94 | -------------------------------------------------------------------------------- /mozi/equal.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MOZI_EQUAL_HPP 25 | #define MOZI_EQUAL_HPP 26 | 27 | #include // std::size_t 28 | #include // std::equal_to 29 | #include // std::is_same 30 | #include // std::forward 31 | #include "type_traits.hpp" // mozi::remove_cvref 32 | 33 | namespace mozi { 34 | 35 | template 36 | struct equality_comparer { 37 | template 38 | constexpr bool operator()(T1&& lhs, U1&& rhs) const 39 | { 40 | static_assert(std::is_same_v, T> && 41 | std::is_same_v, U>); 42 | return std::equal_to{}(std::forward(lhs), 43 | std::forward(rhs)); 44 | } 45 | }; 46 | 47 | namespace detail { 48 | 49 | struct equal_fn { 50 | template 51 | constexpr bool operator()(T&& lhs, U&& rhs) const 52 | { 53 | return equality_comparer, 54 | mozi::remove_cvref_t>{}( 55 | std::forward(lhs), std::forward(rhs)); 56 | } 57 | }; 58 | 59 | } // namespace detail 60 | 61 | inline constexpr detail::equal_fn equal{}; 62 | 63 | template 64 | struct equality_comparer { 65 | template 66 | constexpr bool operator()(T1 (&lhs)[N], U1 (&rhs)[N]) const 67 | { 68 | static_assert( 69 | std::is_same_v, std::remove_cv_t> && 70 | std::is_same_v, std::remove_cv_t>); 71 | for (std::size_t i = 0; i < N; ++i) { 72 | if (!mozi::equal(lhs[i], rhs[i])) { 73 | return false; 74 | } 75 | } 76 | return true; 77 | } 78 | }; 79 | 80 | } // namespace mozi 81 | 82 | #endif // MOZI_EQUAL_HPP 83 | -------------------------------------------------------------------------------- /mozi/struct_reflection_equal.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MOZI_STRUCT_REFLECTION_EQUAL_HPP 25 | #define MOZI_STRUCT_REFLECTION_EQUAL_HPP 26 | 27 | #include // std::enable_if 28 | #include // std::forward 29 | #include "equal.hpp" // mozi::equality_comparer/equal 30 | #include "struct_reflection_core.hpp" // mozi::zip 31 | #include "type_traits.hpp" // mozi::remove_cvref 32 | 33 | namespace mozi { 34 | 35 | template 36 | struct equality_comparer && 38 | is_reflected_struct_v>> { 39 | template 40 | constexpr bool operator()(T1&& lhs, U1&& rhs) const 41 | { 42 | static_assert(std::is_same_v, T> && 43 | std::is_same_v, U>); 44 | if constexpr (T::_size == U::_size) { 45 | bool result = true; 46 | zip(std::forward(lhs), std::forward(rhs), 47 | [&result](auto /*name1*/, auto /*name2*/, 48 | auto&& value1, auto&& value2) { 49 | if (result) { 50 | result = 51 | equal(std::forward(value1), 52 | std::forward(value2)); 53 | } 54 | }); 55 | return result; 56 | } else { 57 | return false; 58 | } 59 | } 60 | }; 61 | 62 | } // namespace mozi 63 | 64 | #define MOZI_DECLARE_EQUAL_COMPARISON(st) \ 65 | constexpr bool operator==(const st& lhs, const st& rhs) \ 66 | { \ 67 | return mozi::equal(lhs, rhs); \ 68 | } 69 | 70 | #if !defined(DECLARE_EQUAL_COMPARISON) && \ 71 | !defined(MOZI_NO_DECLARE_EQUAL_COMPARISON) 72 | #define DECLARE_EQUAL_COMPARISON MOZI_DECLARE_EQUAL_COMPARISON 73 | #endif 74 | 75 | #endif // MOZI_STRUCT_REFLECTION_EQUAL_HPP 76 | -------------------------------------------------------------------------------- /mozi/net_pack_array.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MOZI_NET_PACK_ARRAY_HPP 25 | #define MOZI_NET_PACK_ARRAY_HPP 26 | 27 | #include // std::array 28 | #include // std::size_t 29 | #include "net_pack_core.hpp" // mozi::net_pack::serializer 30 | #include "serialization.hpp" // mozi::serialize/deserialize/... 31 | 32 | namespace mozi::net_pack { 33 | 34 | template 35 | struct serializer { 36 | template 37 | static void serialize(const T (&arr)[N], serialize_t& dest, 38 | SerializerList serializers) 39 | { 40 | for (const auto& value : arr) { 41 | mozi::serialize(value, dest, serializers); 42 | } 43 | } 44 | 45 | template 46 | static deserialize_result deserialize(T (&arr)[N], deserialize_t& src, 47 | SerializerList serializers) 48 | { 49 | for (auto& value : arr) { 50 | auto result = mozi::deserialize(value, src, serializers); 51 | if (result != deserialize_result::success) { 52 | return result; 53 | } 54 | } 55 | return deserialize_result::success; 56 | } 57 | }; 58 | 59 | template 60 | struct serializer> { 61 | template 62 | static void serialize(const std::array& arr, serialize_t& dest, 63 | SerializerList serializers) 64 | { 65 | for (const auto& value : arr) { 66 | mozi::serialize(value, dest, serializers); 67 | } 68 | } 69 | 70 | template 71 | static deserialize_result deserialize(std::array& arr, 72 | deserialize_t& src, 73 | SerializerList serializers) 74 | { 75 | for (auto& value : arr) { 76 | auto result = mozi::deserialize(value, src, serializers); 77 | if (result != deserialize_result::success) { 78 | return result; 79 | } 80 | } 81 | return deserialize_result::success; 82 | } 83 | }; 84 | 85 | } // namespace mozi::net_pack 86 | 87 | #endif // MOZI_NET_PACK_BASIC_HPP 88 | -------------------------------------------------------------------------------- /test/print_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023-2024 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #include "mozi/print.hpp" // mozi::print/println 25 | #include // std::array 26 | #include // std::byte 27 | #include // std::uint8_t 28 | #include // std::map 29 | #include // std::ostringstream 30 | #include // std::string 31 | #include // std::string_view 32 | #include // std::vector 33 | #include // Catch2 test macros 34 | 35 | #if __cplusplus > 201703L && __has_include() 36 | #include 37 | #endif 38 | 39 | TEST_CASE("print") 40 | { 41 | std::ostringstream oss; 42 | 43 | SECTION("basic types") 44 | { 45 | int n = 42; 46 | mozi::println(n, oss); 47 | char ch = 'H'; 48 | mozi::println(ch, oss); 49 | CHECK(oss.str() == "42\n'H'\n"); 50 | } 51 | 52 | SECTION("string types") 53 | { 54 | const char *msg1 = "Hello"; 55 | char msg2[] = "world"; 56 | std::string msg3{"from"}; 57 | std::string_view msg4{"Mozi"}; 58 | mozi::print(msg1, oss); 59 | mozi::print(msg2, oss); 60 | mozi::print(msg3, oss); 61 | mozi::print(msg4, oss); 62 | CHECK(oss.str() == "\"Hello\"\"world\"\"from\"\"Mozi\""); 63 | } 64 | 65 | SECTION("container types") 66 | { 67 | std::array a{1, 2, 3}; 68 | std::byte b[]{std::byte{4}, std::byte{5}, std::byte{6}}; 69 | std::vector v{7, 8, 9}; 70 | std::map mp{{1, 'a'}, {2, 'b'}, {3, 'c'}}; 71 | mozi::println(a, oss); 72 | mozi::println(b, oss); 73 | mozi::println(v, oss); 74 | mozi::println(mp, oss); 75 | CHECK(oss.str() == "{ 1, 2, 3 }\n" 76 | "{ 4, 5, 6 }\n" 77 | "{ 7, 8, 9 }\n" 78 | "{ 1 => 'a', 2 => 'b', 3 => 'c' }\n"); 79 | } 80 | } 81 | 82 | #ifdef __cpp_lib_ranges 83 | TEST_CASE("print c++20 ranges") 84 | { 85 | std::vector v{1, 2, 3, 4, 5, 6}; 86 | std::ostringstream oss; 87 | mozi::print(v | std::views::filter([](int n) { return n % 2 == 0; }) | 88 | std::views::reverse, 89 | oss); 90 | CHECK(oss.str() == "{ 6, 4, 2 }"); 91 | } 92 | #endif 93 | -------------------------------------------------------------------------------- /mozi/compile_time_string.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022-2023 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MOZI_COMPILE_TIME_STRING_HPP 25 | #define MOZI_COMPILE_TIME_STRING_HPP 26 | 27 | #include // IWYU pragma: keep std::size_t 28 | #include // IWYU pragma: keep std::decay/remove_cvref 29 | #include "metamacro.h" // MOZI_PASTE/MOZI_STR 30 | 31 | #if __cplusplus >= 202002L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) 32 | 33 | namespace mozi { 34 | 35 | template 36 | struct compile_time_string { 37 | // Including could be quite heavyweight, if we only need 38 | // to use copy_n 39 | static constexpr void copy_n(const char* src, std::size_t count, 40 | char* dest) 41 | { 42 | for (std::size_t i = 0; i < count; ++i) { 43 | dest[i] = src[i]; 44 | } 45 | } 46 | consteval compile_time_string(const char (&str)[N]) 47 | { 48 | copy_n(str, N, value); 49 | } 50 | char value[N]{}; 51 | }; 52 | 53 | template 54 | constexpr auto operator""_cts() 55 | { 56 | return cts; 57 | } 58 | 59 | template 60 | struct cts_wrapper { 61 | static constexpr compile_time_string str{cts}; 62 | }; 63 | 64 | } // namespace mozi 65 | 66 | #define MOZI_CTS_STRING(x) mozi::cts_wrapper{} 67 | #define MOZI_CTS_GET_VALUE(x) (std::remove_cvref_t::str.value) 68 | 69 | #elif defined(__GNUC__) 70 | 71 | namespace mozi { 72 | 73 | template 74 | struct compile_time_string { 75 | static constexpr char value[sizeof...(Cs) + 1]{Cs...}; 76 | }; 77 | 78 | #pragma GCC diagnostic push 79 | 80 | #ifdef __clang__ 81 | #pragma GCC diagnostic ignored "-Wgnu-string-literal-operator-template" 82 | #else 83 | #pragma GCC diagnostic ignored "-Wpedantic" 84 | #endif 85 | 86 | // We here use the non-standard GCC string literal operator template that 87 | // takes an argument pack of characters 88 | template 89 | constexpr compile_time_string operator""_cts() 90 | { 91 | return {}; 92 | } 93 | 94 | } // namespace mozi 95 | 96 | #pragma GCC diagnostic pop 97 | 98 | #define MOZI_CTS_STRING(x) MOZI_PASTE(MOZI_STR(x), _cts) 99 | #define MOZI_CTS_GET_VALUE(x) (std::decay_t::value) 100 | 101 | #else 102 | 103 | #error "A C++20- or GCC-compatible compiler is required" 104 | 105 | #endif 106 | 107 | #endif // MOZI_COMPILE_TIME_STRING_HPP 108 | -------------------------------------------------------------------------------- /mozi/net_pack_bit_fields.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MOZI_NET_PACK_BIT_FIELDS_HPP 25 | #define MOZI_NET_PACK_BIT_FIELDS_HPP 26 | 27 | #include // CHAR_BIT 28 | #include // std::size_t 29 | #include // std::enable_if 30 | #include "bit_fields_core.hpp" // mozi::count_bit_fields/... 31 | #include "net_pack_core.hpp" // mozi::net_pack::serializer 32 | #include "serialization.hpp" // mozi::serialize/deserialize/... 33 | #include "struct_reflection_core.hpp" // mozi::for_each 34 | #include "type_traits.hpp" // mozi::is_bit_fields_container/... 35 | 36 | namespace mozi::net_pack { 37 | 38 | template 39 | struct serializer>> { 40 | static constexpr std::size_t size_bits = count_bit_fields(); 41 | static_assert(size_bits == 8 || size_bits == 16 || size_bits == 32, 42 | "A bit-fields container must have 8, 16, or 32 bits"); 43 | 44 | template 45 | static void serialize(T obj, serialize_t& dest, 46 | SerializerList serializers) 47 | { 48 | using value_type = 49 | typename mozi::detail::bits_storage::type; 50 | value_type value{}; 51 | mozi::for_each( 52 | obj, [&](auto /*index*/, auto /*name*/, const auto& field) { 53 | value <<= remove_cvref_t::length; 54 | value |= field.underlying_value(); 55 | }); 56 | mozi::serialize(value, dest, serializers); 57 | } 58 | 59 | template 60 | static deserialize_result deserialize(T& obj, deserialize_t& src, 61 | SerializerList serializers) 62 | { 63 | using value_type = 64 | typename mozi::detail::bits_storage::type; 65 | constexpr unsigned total_len = sizeof(value_type) * CHAR_BIT; 66 | value_type value{}; 67 | auto ec = mozi::deserialize(value, src, serializers); 68 | if (ec == deserialize_result::success) { 69 | mozi::for_each( 70 | obj, [&](auto /*index*/, auto /*name*/, auto& field) { 71 | constexpr unsigned len = 72 | remove_cvref_t::length; 73 | field = (unsigned{value} >> (total_len - len)); 74 | value <<= len; 75 | }); 76 | } 77 | return ec; 78 | } 79 | }; 80 | 81 | } // namespace mozi::net_pack 82 | 83 | #endif // MOZI_NET_PACK_BIT_FIELDS_HPP 84 | -------------------------------------------------------------------------------- /mozi/compare.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MOZI_COMPARE_HPP 25 | #define MOZI_COMPARE_HPP 26 | 27 | #ifndef MOZI_USE_THREE_WAY_COMPARISON 28 | #if __cplusplus >= 202002L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) 29 | #include // IWYU pragma: keep 30 | #endif 31 | 32 | // If the standard library does not fully support three-way comparison, 33 | // using components like array or string in reflected structs can be a 34 | // problem. So by default Mozi enables three-way comparison only when the 35 | // standard library claims to support three-way comparison. 36 | #if __cpp_lib_three_way_comparison >= 201907L 37 | #define MOZI_USE_THREE_WAY_COMPARISON 1 38 | #else 39 | #define MOZI_USE_THREE_WAY_COMPARISON 0 40 | #endif 41 | #endif 42 | 43 | #if MOZI_USE_THREE_WAY_COMPARISON 44 | 45 | #include // std::strong_ordering 46 | #include // std::size_t 47 | #include // std::is_same/remove_cv/remove_cvref 48 | #include // std::forward 49 | 50 | namespace mozi { 51 | 52 | template 53 | struct comparer { 54 | template 55 | constexpr auto operator()(T1&& lhs, U1&& rhs) const 56 | { 57 | static_assert(std::is_same_v, T> && 58 | std::is_same_v, U>); 59 | return (std::forward(lhs) <=> std::forward(rhs)); 60 | } 61 | }; 62 | 63 | namespace detail { 64 | 65 | struct compare_fn { 66 | template 67 | constexpr auto operator()(T&& lhs, U&& rhs) const 68 | { 69 | return comparer, std::remove_cvref_t>{}( 70 | std::forward(lhs), std::forward(rhs)); 71 | } 72 | }; 73 | 74 | } // namespace detail 75 | 76 | inline constexpr detail::compare_fn compare{}; 77 | 78 | template 79 | struct comparer { 80 | template 81 | constexpr auto operator()(T1 (&lhs)[N], U1 (&rhs)[N]) const 82 | { 83 | static_assert( 84 | std::is_same_v, std::remove_cv_t> && 85 | std::is_same_v, std::remove_cv_t>); 86 | for (std::size_t i = 0; i < N; ++i) { 87 | auto result = mozi::compare(lhs[i], rhs[i]); 88 | if (result != std::strong_ordering::equivalent) { 89 | return result; 90 | } 91 | } 92 | return std::strong_ordering::equivalent; 93 | } 94 | }; 95 | 96 | } // namespace mozi 97 | 98 | #endif // MOZI_USE_THREE_WAY_COMPARISON 99 | 100 | #endif // MOZI_COMPARE_HPP 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Mozi 2 | ==== 3 | 4 | The Name 5 | -------- 6 | 7 | [Mozi][lnk_mozi] was a renowned Chinese philosopher and craftsman, and 8 | his name sounds very good for a programming project. 9 | 10 | While it is OK to pronounce “Mozi” in any way you like (the proper 11 | Chinese pronunciation is probably too difficult for most of the 12 | English-speaking community), my preferred English pronunciation is 13 | “MO-tsee” (/ˈməʊtsiː/). 14 | 15 | 16 | The Project 17 | ----------- 18 | 19 | This project serves as a continuation and successor to my previous C++ 20 | utility project, [Nvwa][lnk_nvwa], which is showing its age. However, I 21 | do not want to make dramatic changes to its code and break backward 22 | compatibility, so a new project is more appropriate for new stuff. 23 | 24 | In creating Mozi, I am also taking this opportunity to leave behind some 25 | bad habits, such as the use of prefix-less macros and reserved 26 | identifiers (e.g. `_NOTHREADS` and `_M_line`). Overall, Mozi will have 27 | a more modern style. 28 | 29 | 30 | Base C++ Standard 31 | ----------------- 32 | 33 | This project primarily uses the C++17 standard. I need `string_view`s, 34 | fold expressions, inline variables, and constexpr if statements. They 35 | are necessary for code readability and performance. 36 | 37 | Where appropriate, support for newer C++ standards is added. 38 | 39 | 40 | Contents 41 | -------- 42 | 43 | Mozi is supposed to evolve over time. Initially, manual static 44 | reflection for `enum`s and `struct`s was included, as well as macros 45 | that were necessary in such code generation usage. Support facilities 46 | were then added, like equality comparison and serialization. 47 | 48 | A basic example of static reflection follows: 49 | 50 | ```cpp 51 | DEFINE_STRUCT( 52 | S, 53 | (int)v1, 54 | (std::vector)v2, 55 | (std::string)msg 56 | ); 57 | 58 | S data{42, {0x01, 0x02}, "Hello"}; 59 | mozi::println(data); 60 | ``` 61 | 62 | The resulting output would be: 63 | 64 | ``` 65 | { 66 | v1: 42, 67 | v2: { 1, 2 }, 68 | msg: "Hello" 69 | } 70 | ``` 71 | 72 | More may come later. 73 | 74 | 75 | Test Code 76 | --------- 77 | 78 | The test code serves the purpose of both *verifying* and *demonstrating* 79 | the functionalities of Mozi. It is the only thing that requires 80 | building. 81 | 82 | In order to build the test, you need to have [CMake][lnk_cmake] and 83 | [Catch2][lnk_catch2] v3. 84 | 85 | ### Prerequisite: CMake 86 | 87 | You need to have CMake 3.10 or later. Standard CMake installations 88 | should usually do. 89 | 90 | ### Prerequisite: Catch2 v3 91 | 92 | You need to have Catch2 v3, while many Linux distros only come with v2. 93 | You probably need to follow the standard build and installation 94 | procedure on such distros: 95 | 96 | ``` 97 | cd /path/to/Catch2/source 98 | mkdir build && cd build 99 | cmake .. 100 | cmake --build . -j 101 | sudo make install 102 | ``` 103 | 104 | I use [Homebrew][lnk_brew] on macOS (`brew install catch2`) and 105 | [vcpkg][lnk_vcpkg] on Windows (`vcpkg install catch2`). They have the 106 | up-to-date Catch2 version. 107 | 108 | ### Tested compilers 109 | 110 | I mainly test with the following compilers: 111 | 112 | - GCC 10 and later on Linux 113 | - Clang 17 and Apple Clang 14 on macOS 114 | - MSVC as in Visual Studio 2022 on Windows 115 | 116 | If you do not use any of these compilers and encounter a problem, you 117 | had better report the problem with a patch. I may not be able to verify 118 | and fix your problem. 119 | 120 | ### Build and test 121 | 122 | The following procedure should normally work: 123 | 124 | ``` 125 | cd /path/to/mozi/test 126 | mkdir build && cd build 127 | cmake .. 128 | cmake --build . -j && ctest 129 | ``` 130 | 131 | You should then see the message “100% tests passed”. 132 | 133 | #### Note to Windows users 134 | 135 | Keep in mind that when using vcpkg you generally need to specify the 136 | path to it in the third step above, like: 137 | 138 | ``` 139 | cmake "-DCMAKE_TOOLCHAIN_FILE=/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake" .. 140 | ``` 141 | 142 | 143 | Related Speeches and Articles 144 | ----------------------------- 145 | 146 | - [C++ 及系统软件技术大会 2023:C++ 之静态反射(演讲 PDF)](https://github.com/adah1972/cpp_summit_2023/) 147 | - [PureCPP 大会 2024:使用 Mozi 库的静态反射:调试输出和序列化(演讲 PDF 和视频)](http://purecpp.cn/detail?id=2457) 148 | - [Static reflection in C++ (blog article)](https://yongweiwu.wordpress.com/2024/12/28/static-reflection-in-cxx/) 149 | - [Bit fields, byte order, and serialization (blog article)](https://yongweiwu.wordpress.com/2025/02/21/bit-fields-byte-order-and-serialization/) 150 | 151 | 152 | [lnk_brew]: https://brew.sh/ 153 | [lnk_catch2]: https://github.com/catchorg/Catch2 154 | [lnk_cmake]: https://cmake.org/ 155 | [lnk_mozi]: https://en.wikipedia.org/wiki/Mozi 156 | [lnk_nvwa]: https://github.com/adah1972/nvwa 157 | [lnk_vcpkg]: https://github.com/microsoft/vcpkg 158 | 159 | 160 | 163 | -------------------------------------------------------------------------------- /test/enum_reflection_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #include "mozi/enum_reflection.hpp" // DEFINE_ENUM/DEFINE_ENUM_CLASS 25 | #include // std::ostringstream 26 | #include // std::tuple 27 | #include // std::is_enum 28 | #include // std::vector 29 | #include // uint8_t 30 | #include // Catch2 test macros 31 | #include "mozi/print.hpp" // mozi::print 32 | #include "mozi/type_traits.hpp" // mozi::is_reflected_enum 33 | 34 | namespace { 35 | 36 | DEFINE_ENUM( // 37 | Channel, unsigned, // 38 | CHANNEL_RED, CHANNEL_GREEN, CHANNEL_BLUE, CHANNEL_ALPHA); 39 | 40 | DEFINE_ENUM_CLASS( // 41 | Color, uint8_t, // 42 | red = 1, highlight = Color::red, green, blue); 43 | 44 | enum class Number { zero, one, two, three }; 45 | 46 | } // unnamed namespace 47 | 48 | using mozi::enum_to_string; 49 | 50 | TEST_CASE("enum_reflection: enum") 51 | { 52 | SECTION("is_enum", "reflected enum is enum") 53 | { 54 | CHECK(std::is_enum_v); 55 | } 56 | 57 | SECTION("is_defined", "check defined enumerators") 58 | { 59 | CHECK(is_defined(CHANNEL_RED)); 60 | CHECK(is_defined(CHANNEL_GREEN)); 61 | CHECK(is_defined(CHANNEL_BLUE)); 62 | CHECK(is_defined(CHANNEL_ALPHA)); 63 | CHECK_FALSE(is_defined(Channel{9})); 64 | } 65 | 66 | SECTION("to_string", "converts enumerator to string") 67 | { 68 | CHECK(to_string(CHANNEL_RED) == "CHANNEL_RED"); 69 | CHECK(to_string(CHANNEL_GREEN) == "CHANNEL_GREEN"); 70 | CHECK(to_string(CHANNEL_BLUE) == "CHANNEL_BLUE"); 71 | CHECK(to_string(Channel{9}) == "(Channel)9"); 72 | } 73 | 74 | Channel channel{}; 75 | SECTION("from_string", "convert string to enumerator") 76 | { 77 | REQUIRE(from_string("CHANNEL_RED", channel)); 78 | CHECK(channel == CHANNEL_RED); 79 | } 80 | } 81 | 82 | TEST_CASE("enum_reflection: enum class") 83 | { 84 | SECTION("is_enum", "reflected enum is enum") 85 | { 86 | CHECK(std::is_enum_v); 87 | } 88 | 89 | SECTION("is_defined", "check defined enumerators") 90 | { 91 | CHECK(is_defined(Color::red)); 92 | CHECK(is_defined(Color::highlight)); 93 | CHECK(is_defined(Color::green)); 94 | CHECK(is_defined(Color::blue)); 95 | CHECK_FALSE(is_defined(Color{0})); 96 | CHECK_FALSE(is_defined(Color{9})); 97 | } 98 | 99 | Color color{1}; 100 | SECTION("to_string", "converts enumerator to string") 101 | { 102 | CHECK(to_string(color, enum_to_string::show_name) == "Color::red"); 103 | CHECK(to_string(Color::highlight) == "red"); 104 | CHECK(to_string(Color::green) == "green"); 105 | CHECK(to_string(Color::blue) == "blue"); 106 | CHECK(to_string(Color{9}) == "(Color)9"); 107 | } 108 | 109 | SECTION("from_string", "convert string to enumerator") 110 | { 111 | REQUIRE(from_string("highlight", color)); 112 | CHECK(color == Color::highlight); 113 | } 114 | } 115 | 116 | TEST_CASE("enum_reflection: reflected?") 117 | { 118 | CHECK(mozi::is_reflected_enum_v); 119 | CHECK(mozi::is_reflected_enum_v); 120 | CHECK_FALSE(mozi::is_reflected_enum_v); 121 | } 122 | 123 | TEST_CASE("enum_reflection: print") 124 | { 125 | std::ostringstream oss; 126 | 127 | SECTION("enum") 128 | { 129 | mozi::print(Channel::CHANNEL_BLUE, oss); 130 | CHECK(oss.str() == "CHANNEL_BLUE"); 131 | } 132 | 133 | SECTION("enum class") 134 | { 135 | mozi::print(Color::blue, oss); 136 | CHECK(oss.str() == "Color::blue"); 137 | } 138 | 139 | SECTION("enum in other data structures") 140 | { 141 | std::tuple tup{Color::green, std::vector{CHANNEL_RED, CHANNEL_BLUE}, 142 | 3}; 143 | mozi::print(tup, oss); 144 | CHECK(oss.str() == 145 | "(Color::green, { CHANNEL_RED, CHANNEL_BLUE }, 3)"); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /test/bit_fields_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #include "mozi/bit_fields.hpp" // mozi::bit_field/... 25 | #include // std::hex 26 | #include // std::ostringstream 27 | #include // Catch2 test macros 28 | #include "mozi/copy.hpp" // mozi::copy 29 | #include "mozi/equal.hpp" // mozi::equal 30 | #include "mozi/print.hpp" // mozi::println 31 | #include "mozi/struct_reflection.hpp" // DEFINE_STRUCT 32 | 33 | namespace { 34 | 35 | DEFINE_BIT_FIELDS_CONTAINER( // 36 | Date, // 37 | (mozi::bit_field<23, mozi::bit_field_signed>)year, // 38 | (mozi::bit_field<4>)month, // 39 | (mozi::bit_field<5>)day // 40 | ); 41 | 42 | DECLARE_COMPARISON(Date); 43 | 44 | DEFINE_BIT_FIELDS_CONTAINER( // 45 | Flags, // 46 | (mozi::bit_field<3>)f1, // 47 | (mozi::bit_field<17>)f2, // 48 | (mozi::bit_field<12>)f3 // 49 | ); 50 | 51 | DEFINE_STRUCT( // 52 | S1, // 53 | (int)v1, // 54 | (short)v2, // 55 | (Flags)v3 // 56 | ); 57 | 58 | DEFINE_STRUCT( // 59 | S2, // 60 | (int)v1, // 61 | (int)v2, // 62 | (Flags)v3 // 63 | ); 64 | 65 | } // unnamed namespace 66 | 67 | TEST_CASE("bit_fields: basic") 68 | { 69 | Date d1{2023, 12, 31}; 70 | Date d2{2024, 1, 13}; 71 | CHECK(d1 == d1); 72 | CHECK(d1 < d2); 73 | CHECK(d1 <= d2); 74 | CHECK(d2 > d1); 75 | CHECK(!(d2 < d1)); 76 | d2.year = -1; 77 | CHECK(d2.year == -1); 78 | CHECK(d2.year != 0); 79 | CHECK(d2.month == 1); 80 | CHECK(d2.day == 13); 81 | CHECK(d2 < d1); 82 | 83 | d2.day = 33; 84 | CHECK(d2.day != 33); 85 | mozi::bit_field<1, mozi::bit_field_signed> f1{}; 86 | CHECK(f1 == 0); 87 | f1 = 1; // overflown 88 | CHECK(f1 == -1); 89 | f1 = -1; 90 | CHECK(f1 == -1); 91 | mozi::bit_field<3, mozi::bit_field_signed> f2{3}; 92 | CHECK(f2 == 3); 93 | f2 = -4; 94 | CHECK(f2 == -4); 95 | f2 = 4; // overflown 96 | CHECK(f2 != 4); 97 | f2 = -5; // overflown 98 | CHECK(f2 != -5); 99 | mozi::bit_field<9, mozi::bit_field_signed> f3{-2}; 100 | CHECK(f3 == -2); 101 | mozi::bit_field<19, mozi::bit_field_signed> f4{-3}; 102 | CHECK(f4 == -3); 103 | mozi::bit_field<32, mozi::bit_field_signed> f5{-4}; 104 | CHECK(f5 == -4); 105 | } 106 | 107 | TEST_CASE("bit_fields: print") 108 | { 109 | std::ostringstream oss; 110 | mozi::bit_field<4> f1{1}; 111 | mozi::println(f1, oss); 112 | #ifdef __GNUC__ 113 | #pragma GCC diagnostic push 114 | #pragma GCC diagnostic ignored "-Wsign-conversion" 115 | #endif 116 | f1 = -2; // warning expected here 117 | #ifdef __GNUC__ 118 | #pragma GCC diagnostic pop 119 | #endif 120 | mozi::println(f1, oss); 121 | mozi::bit_field<4, mozi::bit_field_signed> f2{-3}; 122 | mozi::println(f2, oss); 123 | CHECK(oss.str() == "1\n14\n-3\n"); 124 | 125 | oss.str(""); 126 | Date d{2024, 8, 17}; 127 | mozi::println(d, oss); 128 | CHECK(oss.str() == "{\n" 129 | " year: 2024,\n" 130 | " month: 8,\n" 131 | " day: 17\n" 132 | "}\n"); 133 | 134 | oss.str(""); 135 | S1 data{42, 0x1234, {{1}, {0x1FFFF}, {0b101010101010}}}; 136 | oss << std::hex; 137 | mozi::println(data, oss); 138 | CHECK(oss.str() == "{\n" 139 | " v1: 2a,\n" 140 | " v2: 1234,\n" 141 | " v3: {\n" 142 | " f1: 1,\n" 143 | " f2: 1ffff,\n" 144 | " f3: aaa\n" 145 | " }\n" 146 | "}\n"); 147 | } 148 | 149 | TEST_CASE("bit_fields: copy") 150 | { 151 | S1 data{42, 0x1234, {{1}, {0x1FFFF}, {0b101010101010}}}; 152 | S2 data2{}; 153 | mozi::copy(data, data2); 154 | CHECK(mozi::equal(data, data2)); 155 | CHECK(mozi::equal(data2, data)); 156 | data2.v3.f1 = 0b010; 157 | CHECK(!mozi::equal(data, data2)); 158 | CHECK(!mozi::equal(data2, data)); 159 | } 160 | -------------------------------------------------------------------------------- /mozi/struct_reflection_copy.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MOZI_STRUCT_REFLECTION_COPY_HPP 25 | #define MOZI_STRUCT_REFLECTION_COPY_HPP 26 | 27 | #include // std::size_t 28 | #include // SIZE_MAX 29 | #include // std::tuple 30 | #include // std::enable_if 31 | #include // std::forward/move 32 | #include "copy.hpp" // mozi::copier/copy 33 | #include "struct_reflection_core.hpp" // mozi::zip/for_each/for_each_meta 34 | #include "type_traits.hpp" // mozi::is_reflected_struct 35 | 36 | namespace mozi { 37 | 38 | namespace detail { 39 | 40 | template 41 | constexpr void copy_tuple_like_impl(T&& src, U& dest, 42 | std::index_sequence) 43 | { 44 | using mozi::get; 45 | using std::get; 46 | (copy(get(std::forward(src)), get(dest)), ...); 47 | } 48 | 49 | } // namespace detail 50 | 51 | template 52 | struct copier && 54 | is_reflected_struct_v>> { 55 | constexpr void operator()(const T& src, U& dest) const 56 | { 57 | zip(src, dest, 58 | [](auto /*name1*/, auto /*name2*/, 59 | const auto& value1, auto& value2) { 60 | copy(value1, value2); 61 | }); 62 | } 63 | constexpr void operator()(T&& src, U& dest) const 64 | { 65 | zip(std::move(src), dest, 66 | [](auto /*name1*/, auto /*name2*/, 67 | auto&& value1, auto& value2) { 68 | copy(std::forward(value1), value2); 69 | }); 70 | } 71 | }; 72 | 73 | template 74 | struct copier, 75 | std::enable_if_t>> { 76 | constexpr void operator()(const T& src, std::tuple& dest) const 77 | { 78 | using DT = std::decay_t; 79 | static_assert(sizeof...(Args) == DT::_size); 80 | detail::copy_tuple_like_impl(src, dest, 81 | std::make_index_sequence{}); 82 | } 83 | constexpr void operator()(T&& src, std::tuple& dest) const 84 | { 85 | using DT = std::decay_t; 86 | static_assert(sizeof...(Args) == DT::_size); 87 | detail::copy_tuple_like_impl(std::move(src), dest, 88 | std::make_index_sequence{}); 89 | } 90 | }; 91 | 92 | template 93 | struct copier, T, 94 | std::enable_if_t>> { 95 | constexpr void operator()(const std::tuple& src, T& dest) const 96 | { 97 | using DT = std::decay_t; 98 | static_assert(sizeof...(Args) == DT::_size); 99 | detail::copy_tuple_like_impl(src, dest, 100 | std::make_index_sequence{}); 101 | } 102 | constexpr void operator()(std::tuple&& src, T& dest) const 103 | { 104 | using DT = std::decay_t; 105 | static_assert(sizeof...(Args) == DT::_size); 106 | detail::copy_tuple_like_impl(std::move(src), dest, 107 | std::make_index_sequence{}); 108 | } 109 | }; 110 | 111 | template > && 113 | is_reflected_struct_v>), 114 | int> = 0> 115 | constexpr std::size_t count_missing_fields() 116 | { 117 | std::size_t result = 0; 118 | for_each_meta([&result](auto /*index*/, auto name, auto /*type*/) { 119 | if constexpr (get_index(name) == SIZE_MAX) { 120 | ++result; 121 | } 122 | }); 123 | return result; 124 | } 125 | 126 | enum class missing_fields : std::size_t {}; 127 | 128 | template 130 | constexpr void copy_same_name_fields(T&& src, U& dest) // NOLINT 131 | { 132 | constexpr size_t actual_missing_fields = 133 | count_missing_fields, std::decay_t>(); 134 | static_assert(size_t(MissingFields) == actual_missing_fields); 135 | for_each(dest, [&src](auto /*index*/, auto name, auto& value) { 136 | using DT = std::decay_t; 137 | constexpr auto index = get_index
(name); 138 | if constexpr (index != SIZE_MAX) { 139 | copy(get(std::forward(src)), value); 140 | } 141 | }); 142 | } 143 | 144 | } // namespace mozi 145 | 146 | #endif // MOZI_STRUCT_REFLECTION_COPY_HPP 147 | -------------------------------------------------------------------------------- /mozi/struct_reflection_compare.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023-2024 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MOZI_STRUCT_REFLECTION_COMPARE_HPP 25 | #define MOZI_STRUCT_REFLECTION_COMPARE_HPP 26 | 27 | #include "metamacro.h" // MOZI_DO_PRAGMA 28 | #include "compare.hpp" // MOZI_USE_THREE_WAY_COMPARISON/... 29 | #include "struct_reflection_core.hpp" // MOZI_DECLARE_LESS_COMPARISON/... 30 | #include "struct_reflection_equal.hpp" // IWYU pragma: export 31 | 32 | #if MOZI_USE_THREE_WAY_COMPARISON 33 | 34 | #include // std::min 35 | #include // std::strong_ordering 36 | #include // std::size_t 37 | #include // std::remove_cvref 38 | #include // std::forward/index_sequence/... 39 | #include "type_traits.hpp" // mozi::is_reflected_struct 40 | 41 | namespace mozi { 42 | 43 | namespace detail { 44 | 45 | template && 47 | mozi::is_reflected_struct_v), 48 | int> = 0> 49 | constexpr auto get_common_comparison_type_impl(std::index_sequence) 50 | { 51 | return mozi::type_t::type>() <=> 54 | std::declval< 55 | typename U::template _field::type>())...>>{}; 56 | } 57 | 58 | } // namespace detail 59 | 60 | template && 62 | mozi::is_reflected_struct_v), 63 | int> = 0> 64 | constexpr auto get_common_comparison_type() 65 | { 66 | return detail::get_common_comparison_type_impl( 67 | std::make_index_sequence{}); 68 | } 69 | 70 | template 71 | struct comparer && 73 | is_reflected_struct_v>> { 74 | template 75 | constexpr auto operator()(T1&& lhs, U1&& rhs) const 76 | { 77 | static_assert(std::is_same_v, T> && 78 | std::is_same_v, U>); 79 | typename decltype(get_common_comparison_type())::type result = 80 | std::strong_ordering::equivalent; 81 | zip(std::forward(lhs), std::forward(rhs), 82 | [&result](auto /*name1*/, auto /*name2*/, 83 | auto&& value1, auto&& value2) { 84 | if (result == std::strong_ordering::equivalent) { 85 | result = 86 | compare(std::forward(value1), 87 | std::forward(value2)); 88 | } 89 | }); 90 | if (result == std::strong_ordering::equivalent) { 91 | result = T::_size <=> U::_size; 92 | } 93 | return result; 94 | } 95 | }; 96 | 97 | } // namespace mozi 98 | 99 | #define MOZI_DECLARE_COMPARISON_IMP(st) \ 100 | MOZI_DECLARE_EQUAL_COMPARISON(st) \ 101 | constexpr auto operator<=>(const st& lhs, const st& rhs) \ 102 | { \ 103 | static_assert(mozi::is_reflected_struct_v); \ 104 | return mozi::compare(lhs, rhs); \ 105 | } 106 | 107 | #else 108 | 109 | #define MOZI_DECLARE_COMPARISON_IMP(st) \ 110 | MOZI_DECLARE_EQUAL_COMPARISON(st) \ 111 | MOZI_DECLARE_LESS_COMPARISON(st) \ 112 | constexpr bool operator!=(const st& lhs, const st& rhs) \ 113 | { \ 114 | return !(rhs == lhs); \ 115 | } \ 116 | constexpr bool operator>(const st& lhs, const st& rhs) \ 117 | { \ 118 | return (rhs < lhs); \ 119 | } \ 120 | constexpr bool operator<=(const st& lhs, const st& rhs) \ 121 | { \ 122 | return !(rhs < lhs); \ 123 | } \ 124 | constexpr bool operator>=(const st& lhs, const st& rhs) \ 125 | { \ 126 | return !(lhs < rhs); \ 127 | } 128 | 129 | #endif 130 | 131 | #if defined(__clang__) 132 | #define MOZI_DECLARE_COMPARISON(st) \ 133 | MOZI_DO_PRAGMA(GCC diagnostic push) \ 134 | MOZI_DO_PRAGMA(GCC diagnostic ignored "-Wunused-function") \ 135 | MOZI_DECLARE_COMPARISON_IMP(st) \ 136 | MOZI_DO_PRAGMA(GCC diagnostic pop) 137 | #else 138 | #define MOZI_DECLARE_COMPARISON MOZI_DECLARE_COMPARISON_IMP 139 | #endif 140 | 141 | #if !defined(DECLARE_COMPARISON) && !defined(MOZI_NO_DECLARE_COMPARISON) 142 | #define DECLARE_COMPARISON MOZI_DECLARE_COMPARISON 143 | #endif 144 | 145 | #endif // MOZI_STRUCT_REFLECTION_COMPARE_HPP 146 | -------------------------------------------------------------------------------- /mozi/net_pack_basic.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MOZI_NET_PACK_BASIC_HPP 25 | #define MOZI_NET_PACK_BASIC_HPP 26 | 27 | #include // std::array 28 | #include // CHAR_BIT/UCHAR_MAX 29 | #include // std::byte/size_t 30 | #include // std::enable_if/is_integral/is_enum 31 | #include "net_pack_core.hpp" // mozi::net_pack::serializer 32 | #include "serialization.hpp" // mozi::deserialize_result/... 33 | #include "type_traits.hpp" // mozi::is_char/underlying_type 34 | 35 | namespace mozi::net_pack { 36 | 37 | template <> 38 | struct serializer { 39 | static void serialize(bool value, serialize_t& dest) 40 | { 41 | dest.push_back(std::byte{value}); 42 | } 43 | 44 | static deserialize_result deserialize(bool& value, deserialize_t& src) 45 | { 46 | if (src.empty()) { 47 | return deserialize_result::input_truncated; 48 | } 49 | if (src.front() == std::byte{0}) { 50 | value = false; 51 | src = src.subspan(1); 52 | return deserialize_result::success; 53 | } 54 | if (src.front() == std::byte{1}) { 55 | value = true; 56 | src = src.subspan(1); 57 | return deserialize_result::success; 58 | } 59 | return deserialize_result::invalid_value; 60 | } 61 | 62 | template 63 | static void serialize(bool value, serialize_t& dest, 64 | SerializerList /*unused*/) 65 | { 66 | serialize(value, dest); 67 | } 68 | template 69 | static deserialize_result deserialize(bool& value, deserialize_t& src, 70 | SerializerList /*unused*/) 71 | { 72 | return deserialize(value, src); 73 | } 74 | }; 75 | 76 | template 77 | struct serializer>> { 78 | static void serialize(T value, serialize_t& dest) 79 | { 80 | dest.push_back(static_cast(value)); 81 | } 82 | 83 | static deserialize_result deserialize(T& value, deserialize_t& src) 84 | { 85 | if (src.empty()) { 86 | return deserialize_result::input_truncated; 87 | } 88 | value = static_cast(src.front()); 89 | src = src.subspan(1); 90 | return deserialize_result::success; 91 | } 92 | 93 | template 94 | static void serialize(T value, serialize_t& dest, 95 | SerializerList /*unused*/) 96 | { 97 | serialize(value, dest); 98 | } 99 | template 100 | static deserialize_result deserialize(T& value, deserialize_t& src, 101 | SerializerList /*unused*/) 102 | { 103 | return deserialize(value, src); 104 | } 105 | }; 106 | 107 | namespace detail { 108 | 109 | // A nice optimizing compiler can reduce this function to a single 110 | // instruction, if it is available on the platform (like bswap on x86). 111 | template 112 | constexpr std::array net_convert(T value) 113 | { 114 | std::array result{}; 115 | constexpr auto mask = static_cast>(UCHAR_MAX); 116 | constexpr auto len = sizeof(T); 117 | auto unsigned_value = static_cast>(value); 118 | for (std::size_t i = 0; i < len; ++i) { 119 | result[len - i - 1] = static_cast(unsigned_value & mask); 120 | unsigned_value >>= CHAR_BIT; 121 | } 122 | return result; 123 | } 124 | 125 | } // namespace detail 126 | 127 | template 128 | struct serializer< 129 | T, std::enable_if_t && (sizeof(T) > 1)>> { 130 | static void serialize(T value, serialize_t& dest) 131 | { 132 | auto net_value = detail::net_convert(value); 133 | dest.insert(dest.end(), net_value.begin(), net_value.end()); 134 | } 135 | 136 | static deserialize_result deserialize(T& value, deserialize_t& src) 137 | { 138 | if (src.size() < sizeof(T)) { 139 | return deserialize_result::input_truncated; 140 | } 141 | std::make_unsigned_t unsigned_value{}; 142 | for (std::size_t i = 0; i < sizeof(T); ++i) { 143 | unsigned_value <<= CHAR_BIT; 144 | unsigned_value |= static_cast(src[i]); 145 | } 146 | value = static_cast(unsigned_value); 147 | src = src.subspan(sizeof(T)); 148 | return deserialize_result::success; 149 | } 150 | 151 | template 152 | static void serialize(T value, serialize_t& dest, 153 | SerializerList /*unused*/) 154 | { 155 | serialize(value, dest); 156 | } 157 | template 158 | static deserialize_result deserialize(T& value, deserialize_t& src, 159 | SerializerList /*unused*/) 160 | { 161 | return deserialize(value, src); 162 | } 163 | }; 164 | 165 | template 166 | struct serializer>> { 167 | template 168 | static void serialize(T value, serialize_t& dest, SerializerList serializers) 169 | { 170 | mozi::serialize(static_cast>(value), 171 | dest, serializers); 172 | } 173 | 174 | template 175 | static deserialize_result deserialize(T& value, deserialize_t& src, 176 | SerializerList serializers) 177 | { 178 | mozi::underlying_type_t temp; 179 | auto result = mozi::deserialize(temp, src, serializers); 180 | if (result == deserialize_result::success) { 181 | value = static_cast(temp); 182 | } 183 | return result; 184 | } 185 | }; 186 | 187 | } // namespace mozi::net_pack 188 | 189 | #endif // MOZI_NET_PACK_BASIC_HPP 190 | -------------------------------------------------------------------------------- /mozi/type_traits.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023-2024 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MOZI_TYPE_TRAITS_HPP 25 | #define MOZI_TYPE_TRAITS_HPP 26 | 27 | #include // std::begin/end 28 | #include // std::tuple_size 29 | #include // std::false_type/true_type/void_t/remove_cv/... 30 | #include // std::pair 31 | 32 | namespace mozi { 33 | 34 | namespace adl { 35 | 36 | using std::begin; 37 | using std::end; 38 | 39 | template 40 | auto adl_begin(Rng&& rng) -> decltype(begin(rng)); 41 | 42 | template 43 | auto adl_end(Rng&& rng) -> decltype(end(rng)); 44 | 45 | } // namespace adl 46 | 47 | // Template to work around CWG 2518 48 | template 49 | struct always_false { 50 | static constexpr bool value = false; 51 | }; 52 | template 53 | inline constexpr bool always_false_v = always_false::value; 54 | 55 | // Type traits for ranges 56 | template 57 | struct is_range : std::false_type {}; 58 | template 59 | struct is_range()), 60 | adl::adl_end(std::declval()))>> 61 | : std::true_type {}; 62 | template 63 | inline constexpr bool is_range_v = is_range::value; 64 | 65 | // Type trait to detect std::pair 66 | template 67 | struct is_pair : std::false_type {}; 68 | template 69 | struct is_pair> : std::true_type {}; 70 | template 71 | inline constexpr bool is_pair_v = is_pair::value; 72 | 73 | // Type trait for tuple-like objects 74 | template 75 | struct is_tuple_like : std::false_type {}; 76 | template 77 | struct is_tuple_like::value)>> 78 | : std::true_type {}; 79 | template 80 | inline constexpr bool is_tuple_like_v = is_tuple_like::value; 81 | 82 | // Type trait for char types 83 | template 84 | struct is_char : std::false_type {}; 85 | template <> 86 | struct is_char : std::true_type {}; 87 | template <> 88 | struct is_char : std::true_type {}; 89 | template <> 90 | struct is_char : std::true_type {}; 91 | template 92 | inline constexpr bool is_char_v = is_char::value; 93 | 94 | // Type trait for char pointer types 95 | template 96 | struct is_char_pointer : std::false_type {}; 97 | template 98 | struct is_char_pointer< 99 | T, 100 | std::enable_if_t && 101 | is_char_v>>>> 102 | : std::true_type {}; 103 | template 104 | inline constexpr bool is_char_pointer_v = is_char_pointer::value; 105 | 106 | // Type trait to promote char types to "normal" int types 107 | template 108 | struct promote_char { 109 | using type = T; 110 | }; 111 | template 112 | struct promote_char< 113 | T, std::enable_if_t && std::is_signed_v>> { 114 | using type = int; 115 | }; 116 | template 117 | struct promote_char< 118 | T, std::enable_if_t && std::is_unsigned_v>> { 119 | using type = unsigned; 120 | }; 121 | template 122 | using promote_char_t = typename promote_char::type; 123 | 124 | // C++20 remove_cvref 125 | template 126 | struct remove_cvref { 127 | using type = std::remove_cv_t>; 128 | }; 129 | template 130 | using remove_cvref_t = typename remove_cvref::type; 131 | 132 | // C++20-compatible underlying_type for better SFINAE than some old 133 | // implementations 134 | template 135 | struct underlying_type {}; 136 | template 137 | struct underlying_type>> { 138 | using type = std::underlying_type_t; 139 | }; 140 | template 141 | using underlying_type_t = typename underlying_type::type; 142 | 143 | // C++23 is_scoped_enum 144 | template 145 | struct is_scoped_enum : std::false_type {}; 146 | template 147 | struct is_scoped_enum< 148 | T, std::enable_if_t && 149 | !std::is_convertible_v>>> 150 | : std::true_type {}; 151 | template 152 | inline constexpr bool is_scoped_enum_v = is_scoped_enum::value; 153 | 154 | // Type trait for whether a type is complete 155 | template 156 | struct is_type_complete : std::false_type {}; 157 | template 158 | struct is_type_complete> 159 | : std::true_type {}; 160 | template 161 | inline constexpr bool is_type_complete_v = is_type_complete::value; 162 | 163 | // Type trait for containers 164 | template 165 | struct is_container : std::false_type {}; 166 | template 167 | struct is_container> 168 | : std::true_type {}; 169 | template 170 | inline constexpr bool is_container_v = is_container::value; 171 | 172 | // Type trait for map-like containers 173 | template 174 | struct is_map : std::false_type {}; 175 | template 176 | struct is_map< 177 | T, std::void_t(), 178 | std::declval(), 179 | std::declval())>> 180 | : std::true_type {}; 181 | template 182 | inline constexpr bool is_map_v = is_map::value; 183 | 184 | // Type trait for reflected enums 185 | template 186 | struct is_reflected_enum : std::false_type {}; 187 | template 188 | struct is_reflected_enum> 190 | : std::true_type {}; 191 | template 192 | inline constexpr static bool is_reflected_enum_v = 193 | is_reflected_enum::value; 194 | 195 | // Type trait for reflected structs 196 | template 197 | struct is_reflected_struct : std::false_type {}; 198 | template 199 | struct is_reflected_struct> 200 | : std::true_type {}; 201 | template 202 | inline constexpr static bool is_reflected_struct_v = 203 | is_reflected_struct::value; 204 | 205 | // Type trait for reflected bit fields 206 | template 207 | struct is_bit_fields_container : std::false_type {}; 208 | template 209 | struct is_bit_fields_container< 210 | T, std::void_t> 212 | : std::true_type {}; 213 | template 214 | inline constexpr static bool is_bit_fields_container_v = 215 | is_bit_fields_container::value; 216 | 217 | } // namespace mozi 218 | 219 | #endif // MOZI_TYPE_TRAITS_HPP 220 | -------------------------------------------------------------------------------- /mozi/bit_fields_core.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MOZI_BIT_FIELDS_CORE_HPP 25 | #define MOZI_BIT_FIELDS_CORE_HPP 26 | 27 | #include // std::size_t 28 | #include // std::uint8_t/uint16_t/uint32_t/... 29 | #include // std::enable_if 30 | #include "metamacro.h" // MOZI_GET_ARG_COUNT/MOZI_REPEAT_ON 31 | #include "struct_reflection_core.hpp" // mozi::for_each_meta/MOZI_FIELD 32 | #include "type_traits.hpp" // mozi::is_bit_fields_container/... 33 | 34 | namespace mozi { 35 | 36 | namespace detail { 37 | 38 | template 39 | struct bits_storage; 40 | 41 | template 42 | struct bits_storage 0 && N <= 8)>> { 43 | using type = std::uint8_t; 44 | }; 45 | 46 | template 47 | struct bits_storage 8 && N <= 16)>> { 48 | using type = std::uint16_t; 49 | }; 50 | 51 | template 52 | struct bits_storage 16 && N <= 32)>> { 53 | using type = std::uint32_t; 54 | }; 55 | 56 | constexpr std::uint32_t get_bit_field_mask(unsigned len) 57 | { 58 | // Shift count overflow is undefined behavior 59 | if (len >= 32) { 60 | return UINT32_MAX; 61 | } 62 | return ~(UINT32_MAX << len); 63 | } 64 | 65 | } // namespace detail 66 | 67 | enum class bit_field_signedness { is_unsigned, is_signed }; 68 | 69 | inline constexpr auto bit_field_unsigned = bit_field_signedness::is_unsigned; 70 | inline constexpr auto bit_field_signed = bit_field_signedness::is_signed; 71 | 72 | template 74 | class bit_field { 75 | public: 76 | static_assert(sizeof(unsigned) >= sizeof(std::uint32_t)); 77 | 78 | using value_type = typename detail::bits_storage::type; 79 | static constexpr std::size_t length = N; 80 | static constexpr auto signedness = Signedness; 81 | 82 | constexpr bit_field() = default; 83 | 84 | template = 0> 86 | constexpr bit_field(unsigned value) 87 | { 88 | *this = value; 89 | } 90 | template = 0> 92 | constexpr bit_field(int value) 93 | { 94 | *this = value; 95 | } 96 | 97 | template = 0> 99 | constexpr bit_field& operator=(unsigned value) 100 | { 101 | // Truncated 102 | value_ = value & detail::get_bit_field_mask(N); 103 | return *this; 104 | } 105 | template = 0> 107 | constexpr bit_field& operator=(int value) 108 | { 109 | // Truncated and sign-extended, assuming two's-complement 110 | // representation 111 | constexpr unsigned sign_bit = 1U << (N - 1); 112 | if (value >= 0 && (static_cast(value) & sign_bit) == 0) { 113 | value_ = static_cast(value) & 114 | detail::get_bit_field_mask(N); 115 | } else { 116 | value_ = static_cast(value) | 117 | ~detail::get_bit_field_mask(N - 1); 118 | } 119 | return *this; 120 | } 121 | 122 | constexpr value_type underlying_value() const 123 | { 124 | if constexpr (Signedness == bit_field_unsigned) { 125 | return value_; 126 | } else { 127 | return value_ & detail::get_bit_field_mask(N); 128 | } 129 | } 130 | 131 | template = 0> 133 | constexpr operator unsigned() const 134 | { 135 | return value_; 136 | } 137 | template = 0> 139 | constexpr operator int() const 140 | { 141 | return static_cast>(value_); 142 | } 143 | 144 | private: 145 | value_type value_; 146 | }; 147 | 148 | template 149 | constexpr bool operator==(const bit_field& lhs, 150 | const bit_field& rhs) 151 | { 152 | return lhs.underlying_value() == rhs.underlying_value(); 153 | } 154 | 155 | #if __cplusplus >= 202002L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) 156 | 157 | template 158 | constexpr auto operator<=>(const bit_field& lhs, 159 | const bit_field& rhs) 160 | { 161 | if constexpr (S == bit_field_unsigned) { 162 | return static_cast(lhs) <=> static_cast(rhs); 163 | } else { 164 | return static_cast(lhs) <=> static_cast(rhs); 165 | } 166 | } 167 | 168 | #else 169 | 170 | template 171 | constexpr bool operator!=(const bit_field& lhs, 172 | const bit_field& rhs) 173 | { 174 | return !(lhs == rhs); 175 | } 176 | 177 | template 178 | constexpr bool operator<(const bit_field& lhs, 179 | const bit_field& rhs) 180 | { 181 | if constexpr (S == bit_field_unsigned) { 182 | return static_cast(lhs) < static_cast(rhs); 183 | } else { 184 | return static_cast(lhs) < static_cast(rhs); 185 | } 186 | } 187 | 188 | template 189 | constexpr bool operator>(const bit_field& lhs, 190 | const bit_field& rhs) 191 | { 192 | return rhs < lhs; 193 | } 194 | 195 | template 196 | constexpr bool operator<=(const bit_field& lhs, 197 | const bit_field& rhs) 198 | { 199 | return !(rhs < lhs); 200 | } 201 | 202 | template 203 | constexpr bool operator>=(const bit_field& lhs, 204 | const bit_field& rhs) 205 | { 206 | return !(lhs < rhs); 207 | } 208 | 209 | #endif 210 | 211 | namespace detail { 212 | 213 | template 214 | struct is_bit_field : std::false_type {}; 215 | template 216 | struct is_bit_field> : std::true_type {}; 217 | template 218 | inline constexpr bool is_bit_field_v = is_bit_field::value; 219 | 220 | } // namespace detail 221 | 222 | template , int> = 0> 224 | constexpr std::size_t count_bit_fields() 225 | { 226 | std::size_t result{}; 227 | mozi::for_each_meta([&](auto /*index*/, auto /*name*/, auto type) { 228 | using field_type = typename decltype(type)::type; 229 | static_assert(detail::is_bit_field_v); 230 | result += field_type::length; 231 | }); 232 | return result; 233 | } 234 | 235 | } // namespace mozi 236 | 237 | #define MOZI_DEFINE_BIT_FIELDS_CONTAINER(st, ...) \ 238 | struct st { \ 239 | using is_mozi_reflected = void; \ 240 | using is_mozi_bit_fields_container = void; \ 241 | template \ 242 | struct _field; \ 243 | static constexpr std::size_t _size = \ 244 | MOZI_GET_ARG_COUNT(__VA_ARGS__); \ 245 | MOZI_REPEAT_ON(MOZI_FIELD, __VA_ARGS__) \ 246 | } 247 | 248 | #if !defined(DEFINE_BIT_FIELDS_CONTAINER) && \ 249 | !defined(MOZI_NO_DEFINE_BIT_FIELDS_CONTAINER) 250 | #define DEFINE_BIT_FIELDS_CONTAINER MOZI_DEFINE_BIT_FIELDS_CONTAINER 251 | #endif 252 | 253 | #endif // MOZI_BIT_FIELDS_CORE_HPP 254 | -------------------------------------------------------------------------------- /mozi/print.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MOZI_PRINT_HPP 25 | #define MOZI_PRINT_HPP 26 | 27 | #include // std::size_t 28 | #include // std::cout 29 | #include // std::ostream 30 | #include // std::string 31 | #include // std::string_view 32 | #include // std::tuple_element 33 | #include // std::enable_if/decay/is_same 34 | #include // std::forward/pair/index_sequence/... 35 | #include "type_traits.hpp" // miscellaneous type traits 36 | 37 | #if MOZI_PRINT_USE_FMTLIB 38 | #include 39 | #endif 40 | 41 | namespace mozi { 42 | 43 | template 44 | struct printer { 45 | void operator()(const T& obj, std::ostream& os, int /*depth*/) const 46 | { 47 | os << obj; 48 | } 49 | }; 50 | 51 | namespace detail { 52 | 53 | struct print_fn { 54 | template 55 | void operator()(T&& obj, std::ostream& os = std::cout, 56 | int depth = 0) const 57 | { 58 | printer>{}(std::forward(obj), os, depth); 59 | } 60 | }; 61 | 62 | } // namespace detail 63 | 64 | inline constexpr detail::print_fn print{}; 65 | 66 | template 67 | void println(T&& obj, std::ostream& os = std::cout, int depth = 0) 68 | { 69 | print(std::forward(obj), os, depth); 70 | os << '\n'; 71 | } 72 | 73 | namespace detail { 74 | 75 | template 76 | void output_normal(T&& obj, std::ostream& os) 77 | { 78 | // The main different between output_normal and print is how they 79 | // treat characters and strings at the outermost level 80 | using DT = std::decay_t; 81 | if constexpr (is_char_v
|| is_char_pointer_v
|| 82 | std::is_same_v || 83 | std::is_same_v) { 84 | os << obj; 85 | } else { 86 | print(std::forward(obj), os); 87 | } 88 | } 89 | 90 | } // namespace detail 91 | 92 | template 93 | void output(std::ostream& os, Args&&... args) 94 | { 95 | (detail::output_normal(std::forward(args), os), ...); 96 | } 97 | 98 | template 99 | void outputln(std::ostream& os, Args&&... args) 100 | { 101 | (detail::output_normal(std::forward(args), os), ...); 102 | os << '\n'; 103 | } 104 | 105 | namespace detail { 106 | 107 | // Stream manipulator for indentation 108 | class indent { 109 | public: 110 | explicit indent(int n) : n_(n) {} 111 | template 112 | friend std::basic_ostream& 113 | operator<<(std::basic_ostream& os, const indent& x) 114 | { 115 | for (int i = 0; i < x.n_; ++i) { 116 | os << " "; 117 | } 118 | return os; 119 | } 120 | 121 | private: 122 | int n_; 123 | }; 124 | 125 | // Helper functions to check whether indentation and newline need to be 126 | // inserted between objects of specified type 127 | template 128 | constexpr bool needs_indent(); 129 | template 130 | constexpr bool needs_indent_tuple(std::index_sequence) 131 | { 132 | return (needs_indent>() || ...); 133 | } 134 | template 135 | constexpr bool needs_indent() 136 | { 137 | if constexpr (is_reflected_struct_v) { 138 | return true; 139 | } else if constexpr (is_container_v) { 140 | return needs_indent(); 141 | } else if constexpr (is_tuple_like_v) { 142 | return needs_indent_tuple( 143 | std::make_index_sequence>{}); 144 | } 145 | return false; 146 | } 147 | 148 | // Element output function for containers that define a key_type and 149 | // have its value type as std::pair 150 | template 151 | void output_element(std::ostream& os, const T& element, int depth, 152 | std::true_type) 153 | { 154 | print(element.first, os, depth); 155 | os << " => "; 156 | print(element.second, os, depth); 157 | } 158 | 159 | // Element output function for other ranges 160 | template 161 | void output_element(std::ostream& os, const T& element, int depth, 162 | std::false_type) 163 | { 164 | print(element, os, depth); 165 | } 166 | 167 | // Member output function for tuple-like objects 168 | template 169 | void output_tuple_members(std::ostream& os, const Tup& tup, int depth, 170 | std::index_sequence) 171 | { 172 | using std::get; 173 | ((os << (Is != 0 ? ", " : ""), print(get(tup), os, depth)), ...); 174 | } 175 | 176 | } // namespace detail 177 | 178 | template 179 | struct printer || 180 | std::is_same_v>> { 181 | void operator()(T ch, std::ostream& os, int /*depth*/) const 182 | { 183 | // chars and signed chars are output as characters 184 | #if MOZI_PRINT_USE_FMTLIB 185 | os << fmt::format("{:?}", ch); 186 | #else 187 | os << '\'' << ch << '\''; 188 | #endif 189 | } 190 | }; 191 | 192 | template 193 | struct printer>> { 194 | void operator()(T value, std::ostream& os, int /*depth*/) const 195 | { 196 | // unsigned chars are output as unsigned integers 197 | os << static_cast(value); 198 | } 199 | }; 200 | 201 | template 202 | struct printer< 203 | T, std::enable_if_t && !is_reflected_enum_v>> { 204 | void operator()(T value, std::ostream& os, int /*depth*/) const 205 | { 206 | // enumerators are output as underlying integers 207 | os << static_cast>>(value); 208 | } 209 | }; 210 | 211 | template 212 | struct printer>> { 213 | void operator()(T ptr, std::ostream& os, int /*depth*/) const 214 | { 215 | // char* etc. are output as strings 216 | #if MOZI_PRINT_USE_FMTLIB 217 | os << fmt::format("{:?}", ptr); 218 | #else 219 | os << '"' << ptr << '"'; 220 | #endif 221 | } 222 | }; 223 | 224 | template 225 | struct printer>> { 226 | template 227 | // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) 228 | void operator()(U&& rng, std::ostream& os, int depth) const 229 | { 230 | static_assert(std::is_same_v>); 231 | 232 | if constexpr (std::is_same_v || 233 | std::is_same_v || 234 | is_char_pointer_v>) { 235 | // string, string_view, and string literals 236 | #if MOZI_PRINT_USE_FMTLIB 237 | os << fmt::format("{:?}", rng); 238 | #else 239 | os << '"' << rng << '"'; 240 | #endif 241 | } else { 242 | using std::begin; 243 | using std::end; 244 | using element_type = remove_cvref_t; 245 | constexpr bool needs_indent = detail::needs_indent(); 246 | constexpr char sep = needs_indent ? '\n' : ' '; 247 | 248 | os << '{'; 249 | auto last = end(rng); 250 | bool on_first_element = true; 251 | for (auto it = begin(rng); it != last; ++it) { 252 | if (on_first_element) { 253 | on_first_element = false; 254 | } else { 255 | os << ','; 256 | } 257 | os << sep; 258 | if constexpr (needs_indent) { 259 | os << detail::indent(depth + 1); 260 | } 261 | detail::output_element( 262 | os, *it, depth + (needs_indent ? 1 : 0), is_map{}); 263 | } 264 | if (!on_first_element) { // range is not empty 265 | os << sep; 266 | if constexpr (needs_indent) { 267 | os << detail::indent(depth); 268 | } 269 | } 270 | os << '}'; 271 | } 272 | } 273 | }; 274 | 275 | template 276 | struct printer && !is_range_v && 277 | !is_reflected_struct_v>> { 278 | void operator()(const T& tup, std::ostream& os, int depth) const 279 | { 280 | os << '('; 281 | detail::output_tuple_members( 282 | os, tup, depth, 283 | std::make_index_sequence>{}); 284 | os << ')'; 285 | } 286 | }; 287 | 288 | } // namespace mozi 289 | 290 | #endif // MOZI_PRINT_HPP 291 | -------------------------------------------------------------------------------- /mozi/struct_reflection_core.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Netcan 3 | * Copyright (c) 2022-2023 Wu Yongwei 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a 6 | * copy of this software and associated documentation files (the "Software"), 7 | * to deal in the Software without restriction, including without limitation 8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | * and/or sell copies of the Software, and to permit persons to whom the 10 | * Software is furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included 13 | * in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 18 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | * 23 | */ 24 | 25 | #ifndef MOZI_STRUCT_REFLECTION_CORE_HPP 26 | #define MOZI_STRUCT_REFLECTION_CORE_HPP 27 | 28 | #include // std::size_t 29 | #include // SIZE_MAX 30 | #include // std::decay/enable_if/is_same/... 31 | #include // std::forward/index_sequence/... 32 | #include "metamacro.h" // MOZI_GET_ARG_COUNT/MOZI_REPEAT_ON/... 33 | #include "compile_time_string.hpp" // MOZI_CTS_STRING 34 | #include "type_traits.hpp" // mozi::is_reflected_struct 35 | 36 | namespace mozi { 37 | 38 | template 39 | using index_t = std::integral_constant; 40 | 41 | template 42 | struct type_t { 43 | using type = T; 44 | }; 45 | 46 | template >, int> = 0> 48 | constexpr decltype(auto) get(T&& obj) 49 | { 50 | using DT = std::decay_t; 51 | static_assert(I < DT::_size, "Index to get is out of range"); 52 | return typename DT::template _field(std::forward(obj)).value(); 53 | } 54 | 55 | namespace detail { 56 | 57 | template 58 | constexpr void for_each_meta_impl(F&& f, std::index_sequence) 59 | { 60 | using DT = std::decay_t; 61 | (void(std::forward(f)( 62 | index_t{}, DT::template _field::name, 63 | type_t::type>{})), 64 | ...); 65 | } 66 | 67 | template 68 | constexpr void for_each_impl(T&& obj, F&& f, std::index_sequence) 69 | { 70 | using DT = std::decay_t; 71 | (void(std::forward(f)(index_t{}, 72 | DT::template _field::name, 73 | get(std::forward(obj)))), 74 | ...); 75 | } 76 | 77 | template 78 | constexpr void zip_impl(T&& obj1, U&& obj2, F&& f, 79 | std::index_sequence) 80 | { 81 | using DT = std::decay_t; 82 | using DU = std::decay_t; 83 | static_assert(DT::_size == DU::_size); 84 | (void(std::forward(f)( 85 | DT::template _field::name, DU::template _field::name, 86 | get(std::forward(obj1)), get(std::forward(obj2)))), 87 | ...); 88 | } 89 | 90 | } // namespace detail 91 | 92 | template , int> = 0> 94 | constexpr void for_each_meta(F&& f) 95 | { 96 | detail::for_each_meta_impl(std::forward(f), 97 | std::make_index_sequence{}); 98 | } 99 | 100 | template >, int> = 0> 102 | constexpr void for_each(T&& obj, F&& f) 103 | { 104 | using DT = std::decay_t; 105 | detail::for_each_impl(std::forward(obj), std::forward(f), 106 | std::make_index_sequence{}); 107 | } 108 | 109 | template > && 111 | is_reflected_struct_v>), 112 | int> = 0> 113 | constexpr void zip(T&& obj1, U&& obj2, F&& f) 114 | { 115 | using DT = std::decay_t; 116 | using DU = std::decay_t; 117 | static_assert(DT::_size == DU::_size); 118 | detail::zip_impl(std::forward(obj1), std::forward(obj2), 119 | std::forward(f), 120 | std::make_index_sequence{}); 121 | } 122 | 123 | template , int> = 0> 125 | constexpr std::size_t get_index(Name /*name*/) 126 | { 127 | auto result = SIZE_MAX; 128 | for_each_meta([&result](auto index, auto name, auto /*type*/) { 129 | if constexpr (std::is_same_v) { 130 | result = index; 131 | } 132 | (void)index; 133 | }); 134 | return result; 135 | } 136 | 137 | } // namespace mozi 138 | 139 | #if __cplusplus < 202002L && defined(__GNUC__) 140 | // The literal operator needs to be exposed in this configuration 141 | using mozi::operator""_cts; 142 | #endif 143 | 144 | #define MOZI_FIELD(i, arg) \ 145 | MOZI_PAIR(arg); \ 146 | template \ 147 | struct _field { \ 148 | using type = decltype(std::decay_t::MOZI_STRIP(arg)); \ 149 | static constexpr auto name = MOZI_CTS_STRING(MOZI_STRIP(arg)); \ 150 | constexpr explicit _field(T&& obj) /* NOLINT */ \ 151 | : obj_(std::forward(obj)) \ 152 | { \ 153 | } \ 154 | constexpr decltype(auto) value() \ 155 | { \ 156 | return (std::forward(obj_).MOZI_STRIP(arg)); \ 157 | } \ 158 | \ 159 | private: \ 160 | T&& obj_; /* NOLINT */ \ 161 | }; 162 | 163 | #define MOZI_DEFINE_STRUCT(st, ...) \ 164 | struct st { \ 165 | using is_mozi_reflected = void; \ 166 | template \ 167 | struct _field; \ 168 | static constexpr std::size_t _size = \ 169 | MOZI_GET_ARG_COUNT(__VA_ARGS__); \ 170 | MOZI_REPEAT_ON(MOZI_FIELD, __VA_ARGS__) \ 171 | } 172 | 173 | // This macro can only be used at the global namespace, so it cannot be 174 | // integrated into the MOZI_DEFINE_STRUCT macro, even if the functionality 175 | // is always wanted. 176 | #define MOZI_DECLARE_TUPLE_LIKE(st) \ 177 | template <> \ 178 | struct std::tuple_size { \ 179 | static constexpr size_t value = st::_size; \ 180 | }; \ 181 | template \ 182 | struct std::tuple_element { \ 183 | using type = typename st::_field::type; \ 184 | } 185 | 186 | // While it is possible to provide this definition as a friend inside the 187 | // reflected struct, it may not be necessary and can cause some compiler 188 | // warnings. 189 | #define MOZI_DECLARE_LESS_COMPARISON(st) \ 190 | constexpr bool operator<(const st& lhs, const st& rhs) \ 191 | { \ 192 | static_assert(mozi::is_reflected_struct_v); \ 193 | int result = 0; \ 194 | mozi::zip(lhs, rhs, \ 195 | [&result](auto /*name1*/, auto /*name2*/, \ 196 | const auto& value1, const auto& value2) { \ 197 | if (result == 0) { \ 198 | if (value1 < value2) { \ 199 | result = -1; \ 200 | } else if (value2 < value1) { \ 201 | result = 1; \ 202 | } \ 203 | } \ 204 | }); \ 205 | return result < 0; \ 206 | } 207 | 208 | #if !defined(DEFINE_STRUCT) && !defined(MOZI_NO_DEFINE_STRUCT) 209 | #define DEFINE_STRUCT MOZI_DEFINE_STRUCT 210 | #endif 211 | 212 | #if !defined(DECLARE_TUPLE_LIKE) && !defined(MOZI_NO_DECLARE_TUPLE_LIKE) 213 | #define DECLARE_TUPLE_LIKE MOZI_DECLARE_TUPLE_LIKE 214 | #endif 215 | 216 | #if !defined(DECLARE_LESS_COMPARISON) && \ 217 | !defined(MOZI_NO_DECLARE_LESS_COMPARISON) 218 | #define DECLARE_LESS_COMPARISON MOZI_DECLARE_LESS_COMPARISON 219 | #endif 220 | 221 | #endif // MOZI_STRUCT_REFLECTION_CORE_HPP 222 | -------------------------------------------------------------------------------- /mozi/serialization.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Wu Yongwei 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included 12 | * in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #ifndef MOZI_SERIALIZE_HPP 25 | #define MOZI_SERIALIZE_HPP 26 | 27 | #include // std::byte 28 | #include // std::tuple 29 | #include // std::is_same 30 | #include // std::vector 31 | #include "span.hpp" // mozi::span 32 | #include "type_traits.hpp" // mozi::always_false/is_type_complete/... 33 | 34 | #if __has_include() 35 | #include // IWYU pragma: keep 36 | #if __has_include() 37 | #include // __cpp_lib_memory_resource 38 | #endif 39 | #endif 40 | 41 | #ifndef MOZI_SERIALIZATION_USES_PMR 42 | #if __cpp_lib_memory_resource >= 201603L 43 | #define MOZI_SERIALIZATION_USES_PMR 1 44 | #else 45 | #define MOZI_SERIALIZATION_USES_PMR 0 46 | #endif 47 | #endif 48 | 49 | namespace mozi { 50 | 51 | #if MOZI_SERIALIZATION_USES_PMR == 1 52 | using serialize_t = std::pmr::vector; 53 | #else 54 | using serialize_t = std::vector; 55 | #endif 56 | using deserialize_t = span; 57 | 58 | enum class deserialize_result { 59 | success, 60 | input_truncated, 61 | invalid_value, 62 | unexpected_input_data, 63 | }; 64 | 65 | // Type for storing a list of serializers. There are no data members and 66 | // only the type counts. 67 | // 68 | // A serializer shall have two template parameters: the first is the type to 69 | // be serialized, and the second is used for SFINAE. A serializer should 70 | // have two static member functions, specialized for supported types. See 71 | // net_pack_basic.hpp for examples. 72 | template