Skip to content

Commit 54d279f

Browse files
committed
Add support for std::variant
1 parent c59bc0b commit 54d279f

File tree

2 files changed

+124
-5
lines changed

2 files changed

+124
-5
lines changed

include/msgpack23/msgpack23.h

+114-5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
#include <cstring>
1212
#include <span>
1313
#include <string>
14+
#include <type_traits>
15+
#include <variant>
1416
#include <vector>
1517

1618
namespace msgpack23 {
@@ -97,6 +99,20 @@ namespace msgpack23 {
9799
template<typename T>
98100
concept EnumLike = std::is_enum_v<T>;
99101

102+
template<typename T>
103+
struct is_variant : std::false_type {
104+
};
105+
106+
template<typename... Types>
107+
struct is_variant<std::variant<Types...> > : std::true_type {
108+
};
109+
110+
template<typename T>
111+
inline constexpr bool is_variant_v = is_variant<T>::value;
112+
113+
template<typename T>
114+
concept VariantLike = is_variant_v<T>;
115+
100116
template<typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
101117
[[nodiscard]] constexpr T to_big_endian(T const value) noexcept {
102118
if constexpr (std::endian::native == std::endian::little) {
@@ -183,7 +199,7 @@ namespace msgpack23 {
183199
}
184200

185201
template<CollectionLike T>
186-
requires (!MapLike<T> && !EnumLike<T>)
202+
requires (!MapLike<T>) && (!EnumLike<T>)
187203
void pack_type(T const &value) {
188204
if (!pack_array_header(value.size())) {
189205
throw std::length_error("Collection is too long to be serialized.");
@@ -199,7 +215,43 @@ namespace msgpack23 {
199215
}
200216

201217
template<typename T>
202-
requires (!CollectionLike<T> && !MapLike<T> && !EnumLike<T>)
218+
requires VariantLike<T>
219+
void pack_type(T const &value) {
220+
std::vector<std::byte> data{};
221+
std::visit([this, &data](auto const &arg) {
222+
Packer packer{};
223+
data = packer(arg);
224+
}, value);
225+
auto const index = static_cast<std::int8_t>(value.index());
226+
if (index > 127) {
227+
throw std::overflow_error("Variant index is to large to be serialized.");
228+
}
229+
if (data.size() == 1) {
230+
emplace_constant(FormatConstants::fixext1);
231+
} else if (data.size() == 2) {
232+
emplace_constant(FormatConstants::fixext2);
233+
} else if (data.size() == 4) {
234+
emplace_constant(FormatConstants::fixext4);
235+
} else if (data.size() == 8) {
236+
emplace_constant(FormatConstants::fixext8);
237+
} else if (data.size() < std::numeric_limits<std::uint8_t>::max()) {
238+
emplace_constant(FormatConstants::ext8);
239+
emplace_integral(static_cast<std::uint8_t>(data.size()));
240+
} else if (data.size() < std::numeric_limits<std::uint16_t>::max()) {
241+
emplace_constant(FormatConstants::ext16);
242+
emplace_integral(static_cast<std::uint16_t>(data.size()));
243+
} else if (data.size() < std::numeric_limits<std::uint32_t>::max()) {
244+
emplace_constant(FormatConstants::ext32);
245+
emplace_integral(static_cast<std::uint32_t>(data.size()));
246+
} else {
247+
throw std::length_error("Variant is too long to be serialized.");
248+
}
249+
emplace_integral(index);
250+
data_.insert(data_.end(), data.begin(), data.end());
251+
}
252+
253+
template<typename T>
254+
requires (!CollectionLike<T>) && (!MapLike<T>) && (!EnumLike<T>) && (!VariantLike<T>)
203255
void pack_type(T const &value) {
204256
value.pack(*this);
205257
}
@@ -476,6 +528,18 @@ namespace msgpack23 {
476528
return false;
477529
}
478530

531+
template<typename Variant, std::size_t Index = 0>
532+
Variant create_variant_by_index(std::size_t const i) {
533+
if constexpr (Index < std::variant_size_v<Variant>) {
534+
if (i == Index) {
535+
return Variant{std::in_place_index<Index>};
536+
}
537+
return create_variant_by_index<Variant, Index + 1>(i);
538+
} else {
539+
throw std::logic_error("Invalid variant index");
540+
}
541+
}
542+
479543
[[nodiscard]] std::size_t unpack_map_header() {
480544
std::size_t map_size = 0;
481545
if (read_conditional<FormatConstants::map32, std::uint32_t>(map_size)) {
@@ -514,7 +578,7 @@ namespace msgpack23 {
514578
}
515579

516580
template<CollectionLike T>
517-
requires (!MapLike<T> && EmplaceAvailable<T> && (!EnumLike<T>))
581+
requires (!MapLike<T>) && EmplaceAvailable<T> && (!EnumLike<T>)
518582
void unpack_type(T &value) {
519583
using ValueType = typename T::value_type;
520584
auto const array_size = unpack_array_header();
@@ -526,7 +590,7 @@ namespace msgpack23 {
526590
}
527591

528592
template<CollectionLike T>
529-
requires (!MapLike<T> && (!EmplaceAvailable<T>) && (!EnumLike<T>))
593+
requires (!MapLike<T>) && (!EmplaceAvailable<T>) && (!EnumLike<T>)
530594
void unpack_type(T &value) {
531595
using ValueType = typename T::value_type;
532596
std::vector<ValueType> vec;
@@ -540,7 +604,52 @@ namespace msgpack23 {
540604
}
541605

542606
template<typename T>
543-
requires (!CollectionLike<T> && !MapLike<T> && !EnumLike<T>)
607+
requires VariantLike<T>
608+
void unpack_type(T &value) {
609+
std::size_t size = 0;
610+
if (check_constant(FormatConstants::fixext1)) {
611+
increment();
612+
size = 1;
613+
} else if (check_constant(FormatConstants::fixext2)) {
614+
increment();
615+
size = 2;
616+
} else if (check_constant(FormatConstants::fixext4)) {
617+
increment();
618+
size = 4;
619+
} else if (check_constant(FormatConstants::fixext8)) {
620+
increment();
621+
size = 8;
622+
} else if (check_constant(FormatConstants::ext8)) {
623+
increment();
624+
size = read_integral<std::uint8_t>();
625+
} else if (check_constant(FormatConstants::ext16)) {
626+
increment();
627+
size = read_integral<std::uint16_t>();
628+
} else if (check_constant(FormatConstants::ext32)) {
629+
increment();
630+
size = read_integral<std::uint32_t>();
631+
} else {
632+
throw std::logic_error("Unexpected format for std::variant");
633+
}
634+
auto const index = static_cast<std::int8_t>(read_integral<std::uint8_t>());
635+
636+
if (index < 0 || index > static_cast<std::int8_t>(std::variant_size_v<T> - 1)) {
637+
throw std::out_of_range("Invalid variant index");
638+
}
639+
640+
auto const data_start = data_.subspan(position_, size);
641+
increment(size);
642+
643+
value = create_variant_by_index<T>(index);
644+
645+
std::visit([this, &data_start](auto &arg) {
646+
Unpacker unpacker(data_start);
647+
unpacker(arg);
648+
}, value);
649+
}
650+
651+
template<typename T>
652+
requires (!CollectionLike<T>) && (!MapLike<T>) && (!EnumLike<T>) && (!VariantLike<T>)
544653
void unpack_type(T &value) {
545654
value.unpack(*this);
546655
}

tests/type_packing_tests.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -249,4 +249,14 @@ namespace {
249249
unpacker(actual);
250250
EXPECT_EQ(expected, actual);
251251
}
252+
253+
TEST(msgpack23, VariantTypePacking) {
254+
msgpack23::Packer packer{};
255+
std::variant<std::uint8_t, std::string> const expected{"Hello, Variant!"};
256+
auto const data = packer(expected);
257+
msgpack23::Unpacker unpacker{data};
258+
std::variant<std::uint8_t, std::string> actual{};
259+
unpacker(actual);
260+
EXPECT_EQ(expected, actual);
261+
}
252262
}

0 commit comments

Comments
 (0)