diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e6051fe..0c3642f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,15 @@ IF (MSGPACK_USE_X3_PARSE) ENDIF () ENDIF () ELSE () - IF (MSGPACK_CXX17) + IF (MSGPACK_CXX20) + IF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + SET (CMAKE_CXX_FLAGS "-std=c++20 ${CMAKE_CXX_FLAGS}") + ELSEIF ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + SET (CMAKE_CXX_FLAGS "-std=c++20 ${CMAKE_CXX_FLAGS}") + ELSEIF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + # MESSAGE ( FATAL_ERROR "MSVC doesn't support C++20.") + ENDIF () + ELSEIF (MSGPACK_CXX17) IF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") SET (CMAKE_CXX_FLAGS "-std=c++17 ${CMAKE_CXX_FLAGS}") ELSEIF ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") diff --git a/Files.cmake b/Files.cmake index ad92fb94..f91c79d8 100644 --- a/Files.cmake +++ b/Files.cmake @@ -28,11 +28,13 @@ SET (msgpackc_HEADERS include/msgpack/adaptor/cpp11/unique_ptr.hpp include/msgpack/adaptor/cpp11/unordered_map.hpp include/msgpack/adaptor/cpp11/unordered_set.hpp + include/msgpack/adaptor/cpp17/array_byte.hpp include/msgpack/adaptor/cpp17/byte.hpp include/msgpack/adaptor/cpp17/carray_byte.hpp include/msgpack/adaptor/cpp17/optional.hpp include/msgpack/adaptor/cpp17/string_view.hpp include/msgpack/adaptor/cpp17/vector_byte.hpp + include/msgpack/adaptor/cpp20/span.hpp include/msgpack/adaptor/define.hpp include/msgpack/adaptor/define_decl.hpp include/msgpack/adaptor/deque.hpp @@ -124,11 +126,13 @@ SET (msgpackc_HEADERS include/msgpack/v1/adaptor/cpp11/unique_ptr.hpp include/msgpack/v1/adaptor/cpp11/unordered_map.hpp include/msgpack/v1/adaptor/cpp11/unordered_set.hpp + include/msgpack/v1/adaptor/cpp17/array_byte.hpp include/msgpack/v1/adaptor/cpp17/byte.hpp include/msgpack/v1/adaptor/cpp17/carray_byte.hpp include/msgpack/v1/adaptor/cpp17/optional.hpp include/msgpack/v1/adaptor/cpp17/string_view.hpp include/msgpack/v1/adaptor/cpp17/vector_byte.hpp + include/msgpack/v1/adaptor/cpp20/span.hpp include/msgpack/v1/adaptor/define.hpp include/msgpack/v1/adaptor/define_decl.hpp include/msgpack/v1/adaptor/deque.hpp diff --git a/include/msgpack/adaptor/cpp17/array_byte.hpp b/include/msgpack/adaptor/cpp17/array_byte.hpp new file mode 100644 index 00000000..069f36ee --- /dev/null +++ b/include/msgpack/adaptor/cpp17/array_byte.hpp @@ -0,0 +1,16 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2021 KONDO Takatoshi +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef MSGPACK_TYPE_CPP17_ARRAY_BYTE_HPP +#define MSGPACK_TYPE_CPP17_ARRAY_BYTE_HPP + +#include "msgpack/v1/adaptor/cpp17/array_byte.hpp" + +#endif // MSGPACK_TYPE_CPP17_ARRAY_BYTE_HPP diff --git a/include/msgpack/adaptor/cpp20/span.hpp b/include/msgpack/adaptor/cpp20/span.hpp new file mode 100644 index 00000000..b779b47e --- /dev/null +++ b/include/msgpack/adaptor/cpp20/span.hpp @@ -0,0 +1,16 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2021 KONDO Takatoshi +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef MSGPACK_TYPE_CPP20_SPAN_HPP +#define MSGPACK_TYPE_CPP20_SPAN_HPP + +#include "msgpack/v1/adaptor/cpp20/span.hpp" + +#endif // MSGPACK_TYPE_CPP20_SPAN_HPP diff --git a/include/msgpack/type.hpp b/include/msgpack/type.hpp index d2f3ee93..109cd85a 100644 --- a/include/msgpack/type.hpp +++ b/include/msgpack/type.hpp @@ -54,10 +54,15 @@ #include "adaptor/cpp17/string_view.hpp" #endif // MSGPACK_HAS_INCLUDE() +#include "adaptor/cpp17/array_byte.hpp" #include "adaptor/cpp17/byte.hpp" #include "adaptor/cpp17/carray_byte.hpp" #include "adaptor/cpp17/vector_byte.hpp" +#if MSGPACK_HAS_INCLUDE() +#include "adaptor/cpp20/span.hpp" +#endif // MSGPACK_HAS_INCLUDE() + #endif // defined(MSGPACK_USE_CPP03) #include "adaptor/boost/fusion.hpp" diff --git a/include/msgpack/v1/adaptor/cpp17/array_byte.hpp b/include/msgpack/v1/adaptor/cpp17/array_byte.hpp new file mode 100644 index 00000000..6f8fa7f8 --- /dev/null +++ b/include/msgpack/v1/adaptor/cpp17/array_byte.hpp @@ -0,0 +1,121 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2021 KONDO Takatoshi +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#ifndef MSGPACK_V1_TYPE_ARRAY_BYTE_HPP +#define MSGPACK_V1_TYPE_ARRAY_BYTE_HPP + +#if __cplusplus >= 201703 + +#include "msgpack/versioning.hpp" +#include "msgpack/adaptor/adaptor_base.hpp" +#include "msgpack/adaptor/check_container_size.hpp" + +#include +#include +#include + +namespace msgpack { + +/// @cond +MSGPACK_API_VERSION_NAMESPACE(v1) { +/// @endcond + +namespace adaptor { + +template +struct convert > { + msgpack::object const& operator()(msgpack::object const& o, std::array& v) const { + switch (o.type) { + case msgpack::type::BIN: + if (o.via.bin.size != N) + throw msgpack::type_error(); + if (N != 0) { +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif // defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)) && !defined(__clang__) + std::memcpy(&v.front(), o.via.bin.ptr, o.via.bin.size); +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif // defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)) && !defined(__clang__) + } + break; + case msgpack::type::STR: + if (o.via.bin.size != N) + throw msgpack::type_error(); + if (N != 0) { +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif // defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)) && !defined(__clang__) + std::memcpy(&v.front(), o.via.str.ptr, o.via.str.size); +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif // defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)) && !defined(__clang__) + } + break; + default: + throw msgpack::type_error(); + break; + } + return o; + } +}; + +template +struct pack > { + template + msgpack::packer& operator()(msgpack::packer& o, const std::array& v) const { + uint32_t size = checked_get_container_size(v.size()); + o.pack_bin(size); + if (size != 0) { + o.pack_bin_body(reinterpret_cast(&v.front()), size); + } + + return o; + } +}; + +template +struct object > { + void operator()(msgpack::object& o, const std::array& v) const { + uint32_t size = checked_get_container_size(v.size()); + o.type = msgpack::type::BIN; + if (size != 0) { + o.via.bin.ptr = reinterpret_cast(&v.front()); + } + o.via.bin.size = size; + } +}; + +template +struct object_with_zone > { + void operator()(msgpack::object::with_zone& o, const std::array& v) const { + uint32_t size = checked_get_container_size(v.size()); + o.type = msgpack::type::BIN; + o.via.bin.size = size; + if (size != 0) { + char* ptr = static_cast(o.zone.allocate_align(size, MSGPACK_ZONE_ALIGNOF(char))); + o.via.bin.ptr = ptr; + std::memcpy(ptr, &v.front(), size); + } + } +}; + +} // namespace adaptor + +/// @cond +} // MSGPACK_API_VERSION_NAMESPACE(v1) +/// @endcond + +} // namespace msgpack + +#endif // __cplusplus >= 201703 + +#endif // MSGPACK_V1_TYPE_ARRAY_BYTE_HPP diff --git a/include/msgpack/v1/adaptor/cpp20/span.hpp b/include/msgpack/v1/adaptor/cpp20/span.hpp new file mode 100644 index 00000000..161fd305 --- /dev/null +++ b/include/msgpack/v1/adaptor/cpp20/span.hpp @@ -0,0 +1,121 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2021 KONDO Takatoshi and Daniil Kovalev +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef MSGPACK_V1_TYPE_CPP20_SPAN_HPP +#define MSGPACK_V1_TYPE_CPP20_SPAN_HPP + +// Some compilers still do not set __cplusplus to 202002 +#if __cplusplus > 201703 + +#include "msgpack/versioning.hpp" +#include "msgpack/adaptor/adaptor_base.hpp" +#include "msgpack/adaptor/check_container_size.hpp" +#include "msgpack/meta.hpp" + +#include +#include +#include + +namespace msgpack { + +/// @cond +MSGPACK_API_VERSION_NAMESPACE(v1) { +/// @endcond + +namespace adaptor { + +#define MSGPACK_ADAPTOR_CONVERT_SPAN_BINARY(byte_t) \ + template <> \ + struct convert > { \ + msgpack::object const& operator()(msgpack::object const& o, std::span& v) const { \ + switch (o.type) { \ + case msgpack::type::BIN: \ + v = std::span(reinterpret_cast(o.via.bin.ptr), o.via.bin.size); \ + break; \ + default: \ + throw msgpack::type_error(); \ + break; \ + } \ + return o; \ + } \ + }; + +#define MSGPACK_ADAPTOR_PACK_SPAN_BINARY(byte_t) \ + template <> \ + struct pack > { \ + template \ + msgpack::packer& operator()(msgpack::packer& o, const std::span& v) const { \ + uint32_t size = checked_get_container_size(v.size()); \ + o.pack_bin(size); \ + if (size != 0) { \ + o.pack_bin_body(reinterpret_cast(v.data()), size); \ + } \ + return o; \ + } \ + }; + +#define MSGPACK_ADAPTOR_OBJECT_SPAN_BINARY(byte_t) \ + template <> \ + struct object > { \ + void operator()(msgpack::object& o, const std::span& v) const { \ + uint32_t size = checked_get_container_size(v.size()); \ + o.type = msgpack::type::BIN; \ + if (size != 0) { \ + o.via.bin.ptr = reinterpret_cast(v.data()); \ + } \ + o.via.bin.size = size; \ + } \ + }; + +#define MSGPACK_ADAPTOR_OBJECT_WITH_ZONE_SPAN_BINARY(byte_t) \ + template <> \ + struct object_with_zone > { \ + void operator()(msgpack::object::with_zone& o, const std::span& v) const { \ + uint32_t size = checked_get_container_size(v.size()); \ + o.type = msgpack::type::BIN; \ + o.via.bin.size = size; \ + if (size != 0) { \ + char* ptr = static_cast(o.zone.allocate_align(size, MSGPACK_ZONE_ALIGNOF(char))); \ + o.via.bin.ptr = ptr; \ + std::memcpy(ptr, v.data(), size); \ + } \ + } \ + }; + +#define MSGPACK_ADAPTOR_SPAN_BINARY(byte_t) \ + MSGPACK_ADAPTOR_CONVERT_SPAN_BINARY(byte_t) \ + MSGPACK_ADAPTOR_PACK_SPAN_BINARY(byte_t) \ + MSGPACK_ADAPTOR_PACK_SPAN_BINARY(const byte_t) \ + MSGPACK_ADAPTOR_OBJECT_SPAN_BINARY(byte_t) \ + MSGPACK_ADAPTOR_OBJECT_SPAN_BINARY(const byte_t) \ + MSGPACK_ADAPTOR_OBJECT_WITH_ZONE_SPAN_BINARY(byte_t) \ + MSGPACK_ADAPTOR_OBJECT_WITH_ZONE_SPAN_BINARY(const byte_t) + +MSGPACK_ADAPTOR_SPAN_BINARY(char) +MSGPACK_ADAPTOR_SPAN_BINARY(unsigned char) +MSGPACK_ADAPTOR_SPAN_BINARY(std::byte) + +#undef MSGPACK_ADAPTOR_SPAN_BINARY +#undef MSGPACK_ADAPTOR_OBJECT_WITH_ZONE_SPAN_BINARY +#undef MSGPACK_ADAPTOR_OBJECT_SPAN_BINARY +#undef MSGPACK_ADAPTOR_PACK_SPAN_BINARY +#undef MSGPACK_ADAPTOR_CONVERT_SPAN_BINARY + +} // namespace adaptor + +/// @cond +} // MSGPACK_API_VERSION_NAMESPACE(v1) +/// @endcond + +} // namespace msgpack + +#endif // __cplusplus > 201703 + +#endif // MSGPACK_V1_TYPE_CPP11_ARRAY_HPP diff --git a/test/msgpack_cpp17.cpp b/test/msgpack_cpp17.cpp index b0c29e5e..fcb2e853 100644 --- a/test/msgpack_cpp17.cpp +++ b/test/msgpack_cpp17.cpp @@ -271,15 +271,15 @@ TEST(MSGPACK_CPP17, vector_byte_pack_convert) }; msgpack::pack(ss, val1); + std::string const& str = ss.str(); char packed[] = { char(0xc4), char(0x05), char(0x01), char(0x02), char(0x7f), char(0x80), char(0xff) }; + EXPECT_EQ(str.size(), sizeof(packed)); for (size_t i = 0; i != sizeof(packed); ++i) { - std::string const& str = ss.str(); EXPECT_EQ(str[i], packed[i]); } msgpack::object_handle oh; - std::string const& str = ss.str(); msgpack::unpack(oh, str.data(), str.size()); std::vector val2 = oh.get().as>(); EXPECT_EQ(val1, val2); @@ -310,6 +310,125 @@ TEST(MSGPACK_CPP17, vector_byte_object_with_zone) EXPECT_EQ(val1, val2); } +TEST(MSGPACK_CPP17, array_byte_pack_convert) +{ + std::stringstream ss; + std::array val1{ + std::byte{0x01}, std::byte{0x02}, std::byte{0x7f}, std::byte{0x80}, std::byte{0xff} + }; + + msgpack::pack(ss, val1); + std::string const& str = ss.str(); + + char packed[] = { char(0xc4), char(0x05), char(0x01), char(0x02), char(0x7f), char(0x80), char(0xff) }; + EXPECT_EQ(str.size(), sizeof(packed)); + for (size_t i = 0; i != sizeof(packed); ++i) { + EXPECT_EQ(str[i], packed[i]); + } + + { + msgpack::object_handle oh; + msgpack::unpack(oh, str.data(), str.size()); + auto val2 = oh.get().as>(); + EXPECT_EQ(val1, val2); + } + { + msgpack::object_handle oh; + msgpack::unpack(oh, str.data(), str.size()); + EXPECT_THROW((oh.get().as>()), msgpack::type_error); + EXPECT_THROW((oh.get().as>()), msgpack::type_error); + EXPECT_THROW((oh.get().as>()), msgpack::type_error); + } +} + +TEST(MSGPACK_CPP17, array_byte_object) +{ + std::array val1{ + std::byte{0x01}, std::byte{0x02}, std::byte{0x7f}, std::byte{0x80}, std::byte{0xff} + }; + + // Caller need to manage val1's lifetime. The Data is not copied. + msgpack::object obj(val1); + + auto val2 = obj.as>(); + EXPECT_EQ(val1, val2); + + EXPECT_THROW((obj.as>()), msgpack::type_error); + EXPECT_THROW((obj.as>()), msgpack::type_error); + EXPECT_THROW((obj.as>()), msgpack::type_error); +} + +TEST(MSGPACK_CPP17, array_byte_object_with_zone) +{ + msgpack::zone z; + std::array val1{ + std::byte{0x01}, std::byte{0x02}, std::byte{0x7f}, std::byte{0x80}, std::byte{0xff} + }; + msgpack::object obj(val1, z); + + auto val2 = obj.as>(); + EXPECT_EQ(val1, val2); + + EXPECT_THROW((obj.as>()), msgpack::type_error); + EXPECT_THROW((obj.as>()), msgpack::type_error); + EXPECT_THROW((obj.as>()), msgpack::type_error); +} + +TEST(MSGPACK_CPP17, array_byte_empty_pack_convert) +{ + std::stringstream ss; + std::array val1{}; + + msgpack::pack(ss, val1); + std::string const& str = ss.str(); + + char packed[] = { char(0xc4), char(0x00) }; + EXPECT_EQ(str.size(), sizeof(packed)); + for (size_t i = 0; i != sizeof(packed); ++i) { + EXPECT_EQ(str[i], packed[i]); + } + + { + msgpack::object_handle oh; + msgpack::unpack(oh, str.data(), str.size()); + auto val2 = oh.get().as>(); + EXPECT_EQ(val1, val2); + } + { + msgpack::object_handle oh; + msgpack::unpack(oh, str.data(), str.size()); + EXPECT_THROW((oh.get().as>()), msgpack::type_error); + EXPECT_THROW((oh.get().as>()), msgpack::type_error); + } +} + +TEST(MSGPACK_CPP17, array_byte_empty_object) +{ + std::array val1{}; + + // Caller need to manage val1's lifetime. The Data is not copied. + msgpack::object obj(val1); + + auto val2 = obj.as>(); + EXPECT_EQ(val1, val2); + + EXPECT_THROW((obj.as>()), msgpack::type_error); + EXPECT_THROW((obj.as>()), msgpack::type_error); +} + +TEST(MSGPACK_CPP17, array_byte_empty_object_with_zone) +{ + msgpack::zone z; + std::array val1{}; + msgpack::object obj(val1, z); + + auto val2 = obj.as>(); + EXPECT_EQ(val1, val2); + + EXPECT_THROW((obj.as>()), msgpack::type_error); + EXPECT_THROW((obj.as>()), msgpack::type_error); +} + TEST(MSGPACK_CPP17, carray_byte_pack_convert) { std::stringstream ss; @@ -318,15 +437,15 @@ TEST(MSGPACK_CPP17, carray_byte_pack_convert) }; msgpack::pack(ss, val1); + std::string const& str = ss.str(); char packed[] = { char(0xc4), char(0x05), char(0x01), char(0x02), char(0x7f), char(0x80), char(0xff) }; + EXPECT_EQ(str.size(), sizeof(packed)); for (size_t i = 0; i != sizeof(packed); ++i) { - std::string const& str = ss.str(); EXPECT_EQ(str[i], packed[i]); } msgpack::object_handle oh; - std::string const& str = ss.str(); msgpack::unpack(oh, str.data(), str.size()); std::byte val2[sizeof(val1)]; oh.get().convert(val2);