Skip to content

Commit 2d65f66

Browse files
authored
Merge pull request #1075 from uyha/variant
feat: add support for `std::variant`
2 parents ac062e2 + 35638ea commit 2d65f66

File tree

9 files changed

+217
-10
lines changed

9 files changed

+217
-10
lines changed

.github/workflows/coverage.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ jobs:
5656
-D MSGPACK_BUILD_TESTS=ON \
5757
-D CMAKE_BUILD_TYPE=Debug \
5858
-D MSGPACK_GEN_COVERAGE=ON \
59+
-D MSGPACK_USE_STD_VARIANT_ADAPTOR=ON \
5960
-D CMAKE_PREFIX_PATH="$HOME/zlib-prefix/64;$HOME/boost-prefix/64" \
6061
-B build \
6162
-S . || exit 1

.github/workflows/gha.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ jobs:
145145
3)
146146
export CXX="g++-10"
147147
export MSGPACK_CXX_VERSION="MSGPACK_CXX17=ON"
148+
export MSGPACK_USE_STD_VARIANT_ADAPTOR="MSGPACK_USE_STD_VARIANT_ADAPTOR=ON"
148149
;;
149150
4)
150151
export CXX="clang++-10"

CMakeLists.txt

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,17 @@ OPTION (MSGPACK_CXX14 "Using c++14 compiler" OFF)
2323
OPTION (MSGPACK_CXX17 "Using c++17 compiler" OFF)
2424
OPTION (MSGPACK_CXX20 "Using c++20 compiler" OFF)
2525

26-
OPTION (MSGPACK_32BIT "32bit compile" OFF)
27-
OPTION (MSGPACK_USE_BOOST "Use Boost libraried" ON)
28-
OPTION (MSGPACK_USE_X3_PARSE "Use Boost X3 parse" OFF)
29-
OPTION (MSGPACK_BUILD_TESTS "Build tests" OFF)
30-
OPTION (MSGPACK_BUILD_DOCS "Build Doxygen documentation" ON)
31-
OPTION (MSGPACK_FUZZ_REGRESSION "Enable regression testing" OFF)
32-
OPTION (MSGPACK_BUILD_EXAMPLES "Build msgpack examples" OFF)
33-
OPTION (MSGPACK_GEN_COVERAGE "Generate coverage report" OFF)
34-
OPTION (MSGPACK_USE_STATIC_BOOST "Statically link with boost libraries" OFF)
35-
OPTION (MSGPACK_CHAR_SIGN "Char sign to use (signed or unsigned)")
26+
OPTION (MSGPACK_32BIT "32bit compile" OFF)
27+
OPTION (MSGPACK_USE_BOOST "Use Boost libraried" ON)
28+
OPTION (MSGPACK_USE_X3_PARSE "Use Boost X3 parse" OFF)
29+
OPTION (MSGPACK_BUILD_TESTS "Build tests" OFF)
30+
OPTION (MSGPACK_BUILD_DOCS "Build Doxygen documentation" ON)
31+
OPTION (MSGPACK_FUZZ_REGRESSION "Enable regression testing" OFF)
32+
OPTION (MSGPACK_BUILD_EXAMPLES "Build msgpack examples" OFF)
33+
OPTION (MSGPACK_GEN_COVERAGE "Generate coverage report" OFF)
34+
OPTION (MSGPACK_USE_STATIC_BOOST "Statically link with boost libraries" OFF)
35+
OPTION (MSGPACK_CHAR_SIGN "Char sign to use (signed or unsigned)")
36+
OPTION (MSGPACK_USE_STD_VARIANT_ADAPTOR "Enable the adaptor for std::variant" OFF)
3637

3738
SET (CMAKE_CXX_STANDARD_REQUIRED ON)
3839

@@ -92,6 +93,10 @@ ELSE ()
9293
TARGET_COMPILE_DEFINITIONS(msgpack-cxx INTERFACE MSGPACK_DEFAULT_API_VERSION=3)
9394
ENDIF ()
9495

96+
IF (MSGPACK_USE_STD_VARIANT_ADAPTOR)
97+
TARGET_COMPILE_DEFINITIONS(msgpack-cxx INTERFACE MSGPACK_USE_STD_VARIANT_ADAPTOR)
98+
ENDIF ()
99+
95100
IF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
96101
IF (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.1)
97102
INCLUDE (CheckCXXSourceCompiles)

Files.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ SET (msgpack-cxx_HEADERS
3333
include/msgpack/adaptor/cpp17/carray_byte.hpp
3434
include/msgpack/adaptor/cpp17/optional.hpp
3535
include/msgpack/adaptor/cpp17/string_view.hpp
36+
include/msgpack/adaptor/cpp17/variant.hpp
3637
include/msgpack/adaptor/cpp17/vector_byte.hpp
3738
include/msgpack/adaptor/cpp20/span.hpp
3839
include/msgpack/adaptor/define.hpp
@@ -542,6 +543,7 @@ SET (msgpack-cxx_HEADERS
542543
include/msgpack/v1/adaptor/cpp17/carray_byte.hpp
543544
include/msgpack/v1/adaptor/cpp17/optional.hpp
544545
include/msgpack/v1/adaptor/cpp17/string_view.hpp
546+
include/msgpack/v1/adaptor/cpp17/variant.hpp
545547
include/msgpack/v1/adaptor/cpp17/vector_byte.hpp
546548
include/msgpack/v1/adaptor/cpp20/span.hpp
547549
include/msgpack/v1/adaptor/define.hpp

ci/build_cmake.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ cmake \
2121
-D MSGPACK_CHAR_SIGN=${CHAR_SIGN} \
2222
-D MSGPACK_DEFAULT_API_VERSION=${API_VERSION} \
2323
-D MSGPACK_USE_X3_PARSE=${X3_PARSE} \
24+
-D MSGPACK_USE_STD_VARIANT_ADAPTOR=${STD_VARIANT_ADAPTOR} \
2425
-D CMAKE_CXX_FLAGS="${CXXFLAGS} ${ARCH_FLAG}" \
2526
-D CMAKE_INSTALL_PREFIX=$prefix_dir \
2627
-B $build_dir \
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//
2+
// MessagePack for C++ static resolution routine
3+
//
4+
// Copyright (C) 2023 Uy Ha
5+
//
6+
// Distributed under the Boost Software License, Version 1.0.
7+
// (See accompanying file LICENSE_1_0.txt or copy at
8+
// http://www.boost.org/LICENSE_1_0.txt)
9+
//
10+
11+
#ifndef MSGPACK_TYPE_CPP17_VARIANT_HPP
12+
#define MSGPACK_TYPE_CPP17_VARIANT_HPP
13+
14+
#include "msgpack/v1/adaptor/cpp17/variant.hpp"
15+
16+
#endif // MSGPACK_TYPE_CPP17_VARIANT_HPP

include/msgpack/type.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@
6363
#include "adaptor/cpp17/carray_byte.hpp"
6464
#include "adaptor/cpp17/vector_byte.hpp"
6565

66+
#if MSGPACK_HAS_INCLUDE(<variant>)
67+
#include "adaptor/cpp17/variant.hpp"
68+
#endif // MSGPACK_HAS_INCLUDE(<variant>)
69+
6670
#if MSGPACK_HAS_INCLUDE(<span>)
6771
#include "adaptor/cpp20/span.hpp"
6872
#endif // MSGPACK_HAS_INCLUDE(<span>)
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
//
2+
// MessagePack for C++ static resolution routine
3+
//
4+
// Copyright (C) 2023 Uy Ha
5+
//
6+
// Distributed under the Boost Software License, Version 1.0.
7+
// (See accompanying file LICENSE_1_0.txt or copy at
8+
// http://www.boost.org/LICENSE_1_0.txt)
9+
//
10+
11+
#ifndef MSGPACK_V1_TYPE_VARIANT_HPP
12+
#define MSGPACK_V1_TYPE_VARIANT_HPP
13+
14+
#if defined(MSGPACK_USE_STD_VARIANT_ADAPTOR)
15+
16+
#include "msgpack/cpp_version.hpp"
17+
18+
#if MSGPACK_CPP_VERSION >= 201703
19+
20+
#include "msgpack/adaptor/adaptor_base.hpp"
21+
#include "msgpack/object.hpp"
22+
#include "msgpack/versioning.hpp"
23+
24+
#include <variant>
25+
26+
namespace msgpack {
27+
MSGPACK_API_VERSION_NAMESPACE(v1) {
28+
namespace adaptor {
29+
namespace detail {
30+
template <
31+
typename Variant,
32+
typename T,
33+
typename... Ts,
34+
std::size_t current_index,
35+
std::size_t... indices
36+
>
37+
Variant construct_variant(
38+
std::size_t index,
39+
msgpack::object& object,
40+
std::index_sequence<current_index, indices...>
41+
) {
42+
if constexpr(sizeof...(Ts) == 0) {
43+
return object.as<T>();
44+
}
45+
else {
46+
if (index == current_index) {
47+
return object.as<T>();
48+
}
49+
return construct_variant<Variant, Ts...>(
50+
index,
51+
object,
52+
std::index_sequence<indices...>()
53+
);
54+
}
55+
}
56+
57+
struct object_variant_overload {
58+
object_variant_overload(msgpack::object& obj, msgpack::zone& zone)
59+
: obj{obj}
60+
, zone{zone} {}
61+
62+
template<typename T>
63+
void operator()(T const& value) {
64+
obj = msgpack::object(value, zone);
65+
}
66+
67+
msgpack::object& obj;
68+
msgpack::zone& zone;
69+
};
70+
} // namespace detail
71+
72+
template <typename... Ts>
73+
struct as<std::variant<Ts...>, typename std::enable_if<(msgpack::has_as<Ts>::value && ...)>::type> {
74+
std::variant<Ts...> operator()(msgpack::object const& o) const {
75+
if ( o.type != msgpack::type::ARRAY
76+
|| o.via.array.size != 2
77+
|| o.via.array.ptr[0].type != msgpack::type::POSITIVE_INTEGER
78+
|| o.via.array.ptr[0].via.u64 >= sizeof...(Ts)) {
79+
throw msgpack::type_error{};
80+
}
81+
82+
return detail::construct_variant<std::variant<Ts...>, Ts...>(
83+
o.via.array.ptr[0].as<std::size_t>(),
84+
o.via.array.ptr[1],
85+
std::make_index_sequence<sizeof...(Ts)>()
86+
);
87+
}
88+
};
89+
90+
template<typename... Ts>
91+
struct convert<std::variant<Ts...>> {
92+
msgpack::object const& operator()(msgpack::object const& o, std::variant<Ts...>& v) const {
93+
if ( o.type != msgpack::type::ARRAY
94+
|| o.via.array.size != 2
95+
|| o.via.array.ptr[0].type != msgpack::type::POSITIVE_INTEGER
96+
|| o.via.array.ptr[0].via.u64 >= sizeof...(Ts)) {
97+
throw msgpack::type_error{};
98+
}
99+
100+
v = detail::construct_variant<std::variant<Ts...>, Ts...>(
101+
o.via.array.ptr[0].as<std::size_t>(),
102+
o.via.array.ptr[1],
103+
std::make_index_sequence<sizeof...(Ts)>()
104+
);
105+
return o;
106+
}
107+
};
108+
109+
template <typename... Ts>
110+
struct pack<std::variant<Ts...>>{
111+
template<typename Stream>
112+
msgpack::packer<Stream>& operator()(
113+
msgpack::packer<Stream>& o,
114+
std::variant<Ts...> const& v
115+
) const {
116+
o.pack_array(2);
117+
o.pack_uint64(v.index());
118+
std::visit([&o](auto const& value){o.pack(value);}, v);
119+
return o;
120+
}
121+
};
122+
123+
124+
template<typename... Ts>
125+
struct object_with_zone<std::variant<Ts...>> {
126+
void operator()(
127+
msgpack::object::with_zone& o,
128+
std::variant<Ts...> const& v
129+
) const {
130+
msgpack::object *p =
131+
static_cast<msgpack::object *>(
132+
o.zone.allocate_align(
133+
sizeof(msgpack::object) * 2,
134+
MSGPACK_ZONE_ALIGNOF(msgpack::object)
135+
)
136+
);
137+
138+
o.type = msgpack::type::ARRAY;
139+
o.via.array.size = 2;
140+
o.via.array.ptr = p;
141+
o.via.array.ptr[0]= msgpack::object(v.index(), o.zone);
142+
std::visit(detail::object_variant_overload(o.via.array.ptr[1], o.zone), v);
143+
}
144+
};
145+
} // namespace adaptor
146+
}
147+
} // namespace msgpack
148+
149+
#endif // MSGPACK_CPP_VERSION >= 201703
150+
#endif // defined(MSGPACK_USE_STD_VARIANT_ADAPTOR)
151+
#endif // MSGPACK_V1_TYPE_VARIANT_HPP

test/msgpack_cpp17.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,4 +461,30 @@ BOOST_AUTO_TEST_CASE(carray_byte_object_with_zone)
461461
}
462462
}
463463

464+
#if defined(MSGPACK_USE_STD_VARIANT_ADAPTOR)
465+
466+
BOOST_AUTO_TEST_CASE(variant_pack_unpack_as) {
467+
std::stringstream ss;
468+
std::variant<bool, int, float, double> val1{1.0};
469+
msgpack::pack(ss, val1);
470+
std::string const& str = ss.str();
471+
msgpack::object_handle oh =
472+
msgpack::unpack(str.data(), str.size());
473+
std::variant<bool, int, float, double> val2 =
474+
oh.get().as<std::variant<bool, int, float, double> >();
475+
BOOST_CHECK(val1 == val2);
476+
BOOST_CHECK_THROW((oh.get().as<std::variant<bool>>()), msgpack::type_error);
477+
}
478+
479+
BOOST_AUTO_TEST_CASE(variant_with_zone) {
480+
msgpack::zone z;
481+
std::variant<bool, int, float, double> val1{1.0};
482+
msgpack::object obj(val1, z);
483+
std::variant<bool, int, float, double> val2 = obj.as<std::variant<bool, int, float, double>>();
484+
BOOST_CHECK(val1 == val2);
485+
BOOST_CHECK_THROW((obj.as<std::variant<bool>>()), msgpack::type_error);
486+
}
487+
488+
#endif // defined(MSGPACK_USE_STD_VARIANT_ADAPTOR)
489+
464490
#endif // MSGPACK_CPP_VERSION >= 201703

0 commit comments

Comments
 (0)