Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for std::variant #1075

Merged
merged 8 commits into from
Jul 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ jobs:
-D MSGPACK_BUILD_TESTS=ON \
-D CMAKE_BUILD_TYPE=Debug \
-D MSGPACK_GEN_COVERAGE=ON \
-D MSGPACK_USE_STD_VARIANT_ADAPTOR=ON \
-D CMAKE_PREFIX_PATH="$HOME/zlib-prefix/64;$HOME/boost-prefix/64" \
-B build \
-S . || exit 1
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/gha.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ jobs:
3)
export CXX="g++-10"
export MSGPACK_CXX_VERSION="MSGPACK_CXX17=ON"
export MSGPACK_USE_STD_VARIANT_ADAPTOR="MSGPACK_USE_STD_VARIANT_ADAPTOR=ON"
;;
4)
export CXX="clang++-10"
Expand Down
25 changes: 15 additions & 10 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,17 @@ OPTION (MSGPACK_CXX14 "Using c++14 compiler" OFF)
OPTION (MSGPACK_CXX17 "Using c++17 compiler" OFF)
OPTION (MSGPACK_CXX20 "Using c++20 compiler" OFF)

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

SET (CMAKE_CXX_STANDARD_REQUIRED ON)

Expand Down Expand Up @@ -92,6 +93,10 @@ ELSE ()
TARGET_COMPILE_DEFINITIONS(msgpack-cxx INTERFACE MSGPACK_DEFAULT_API_VERSION=3)
ENDIF ()

IF (MSGPACK_USE_STD_VARIANT_ADAPTOR)
TARGET_COMPILE_DEFINITIONS(msgpack-cxx INTERFACE MSGPACK_USE_STD_VARIANT_ADAPTOR)
ENDIF ()

IF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
IF (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.1)
INCLUDE (CheckCXXSourceCompiles)
Expand Down
2 changes: 2 additions & 0 deletions Files.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ SET (msgpack-cxx_HEADERS
include/msgpack/adaptor/cpp17/carray_byte.hpp
include/msgpack/adaptor/cpp17/optional.hpp
include/msgpack/adaptor/cpp17/string_view.hpp
include/msgpack/adaptor/cpp17/variant.hpp
include/msgpack/adaptor/cpp17/vector_byte.hpp
include/msgpack/adaptor/cpp20/span.hpp
include/msgpack/adaptor/define.hpp
Expand Down Expand Up @@ -542,6 +543,7 @@ SET (msgpack-cxx_HEADERS
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/variant.hpp
include/msgpack/v1/adaptor/cpp17/vector_byte.hpp
include/msgpack/v1/adaptor/cpp20/span.hpp
include/msgpack/v1/adaptor/define.hpp
Expand Down
1 change: 1 addition & 0 deletions ci/build_cmake.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ cmake \
-D MSGPACK_CHAR_SIGN=${CHAR_SIGN} \
-D MSGPACK_DEFAULT_API_VERSION=${API_VERSION} \
-D MSGPACK_USE_X3_PARSE=${X3_PARSE} \
-D MSGPACK_USE_STD_VARIANT_ADAPTOR=${STD_VARIANT_ADAPTOR} \
-D CMAKE_CXX_FLAGS="${CXXFLAGS} ${ARCH_FLAG}" \
-D CMAKE_INSTALL_PREFIX=$prefix_dir \
-B $build_dir \
Expand Down
16 changes: 16 additions & 0 deletions include/msgpack/adaptor/cpp17/variant.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// MessagePack for C++ static resolution routine
//
// Copyright (C) 2023 Uy Ha
//
// 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_VARIANT_HPP
#define MSGPACK_TYPE_CPP17_VARIANT_HPP

#include "msgpack/v1/adaptor/cpp17/variant.hpp"

#endif // MSGPACK_TYPE_CPP17_VARIANT_HPP
4 changes: 4 additions & 0 deletions include/msgpack/type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@
#include "adaptor/cpp17/carray_byte.hpp"
#include "adaptor/cpp17/vector_byte.hpp"

#if MSGPACK_HAS_INCLUDE(<variant>)
#include "adaptor/cpp17/variant.hpp"
#endif // MSGPACK_HAS_INCLUDE(<variant>)

#if MSGPACK_HAS_INCLUDE(<span>)
#include "adaptor/cpp20/span.hpp"
#endif // MSGPACK_HAS_INCLUDE(<span>)
Expand Down
151 changes: 151 additions & 0 deletions include/msgpack/v1/adaptor/cpp17/variant.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
//
// MessagePack for C++ static resolution routine
//
// Copyright (C) 2023 Uy Ha
//
// 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_VARIANT_HPP
#define MSGPACK_V1_TYPE_VARIANT_HPP

#if defined(MSGPACK_USE_STD_VARIANT_ADAPTOR)

#include "msgpack/cpp_version.hpp"

#if MSGPACK_CPP_VERSION >= 201703

#include "msgpack/adaptor/adaptor_base.hpp"
#include "msgpack/object.hpp"
#include "msgpack/versioning.hpp"

#include <variant>

namespace msgpack {
MSGPACK_API_VERSION_NAMESPACE(v1) {
namespace adaptor {
namespace detail {
template <
typename Variant,
typename T,
typename... Ts,
std::size_t current_index,
std::size_t... indices
>
Variant construct_variant(
std::size_t index,
msgpack::object& object,
std::index_sequence<current_index, indices...>
) {
if constexpr(sizeof...(Ts) == 0) {
return object.as<T>();
}
else {
if (index == current_index) {
return object.as<T>();
}
return construct_variant<Variant, Ts...>(
index,
object,
std::index_sequence<indices...>()
);
}
}

struct object_variant_overload {
object_variant_overload(msgpack::object& obj, msgpack::zone& zone)
: obj{obj}
, zone{zone} {}

template<typename T>
void operator()(T const& value) {
obj = msgpack::object(value, zone);
}

msgpack::object& obj;
msgpack::zone& zone;
};
} // namespace detail

template <typename... Ts>
struct as<std::variant<Ts...>, typename std::enable_if<(msgpack::has_as<Ts>::value && ...)>::type> {
uyha marked this conversation as resolved.
Show resolved Hide resolved
std::variant<Ts...> operator()(msgpack::object const& o) const {
if ( o.type != msgpack::type::ARRAY
uyha marked this conversation as resolved.
Show resolved Hide resolved
|| o.via.array.size != 2
|| o.via.array.ptr[0].type != msgpack::type::POSITIVE_INTEGER
|| o.via.array.ptr[0].via.u64 >= sizeof...(Ts)) {
throw msgpack::type_error{};
}

return detail::construct_variant<std::variant<Ts...>, Ts...>(
o.via.array.ptr[0].as<std::size_t>(),
o.via.array.ptr[1],
std::make_index_sequence<sizeof...(Ts)>()
);
}
};

template<typename... Ts>
struct convert<std::variant<Ts...>> {
msgpack::object const& operator()(msgpack::object const& o, std::variant<Ts...>& v) const {
if ( o.type != msgpack::type::ARRAY
uyha marked this conversation as resolved.
Show resolved Hide resolved
|| o.via.array.size != 2
|| o.via.array.ptr[0].type != msgpack::type::POSITIVE_INTEGER
|| o.via.array.ptr[0].via.u64 >= sizeof...(Ts)) {
throw msgpack::type_error{};
}

v = detail::construct_variant<std::variant<Ts...>, Ts...>(
o.via.array.ptr[0].as<std::size_t>(),
o.via.array.ptr[1],
std::make_index_sequence<sizeof...(Ts)>()
);
return o;
}
};

template <typename... Ts>
struct pack<std::variant<Ts...>>{
template<typename Stream>
msgpack::packer<Stream>& operator()(
msgpack::packer<Stream>& o,
std::variant<Ts...> const& v
) const {
o.pack_array(2);
o.pack_uint64(v.index());
std::visit([&o](auto const& value){o.pack(value);}, v);
return o;
}
};


template<typename... Ts>
struct object_with_zone<std::variant<Ts...>> {
void operator()(
msgpack::object::with_zone& o,
std::variant<Ts...> const& v
) const {
msgpack::object *p =
static_cast<msgpack::object *>(
o.zone.allocate_align(
sizeof(msgpack::object) * 2,
MSGPACK_ZONE_ALIGNOF(msgpack::object)
)
);

o.type = msgpack::type::ARRAY;
o.via.array.size = 2;
o.via.array.ptr = p;
o.via.array.ptr[0]= msgpack::object(v.index(), o.zone);
std::visit(detail::object_variant_overload(o.via.array.ptr[1], o.zone), v);
}
};
} // namespace adaptor
}
} // namespace msgpack

#endif // MSGPACK_CPP_VERSION >= 201703
#endif // defined(MSGPACK_USE_STD_VARIANT_ADAPTOR)
#endif // MSGPACK_V1_TYPE_VARIANT_HPP
26 changes: 26 additions & 0 deletions test/msgpack_cpp17.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -461,4 +461,30 @@ BOOST_AUTO_TEST_CASE(carray_byte_object_with_zone)
}
}

#if defined(MSGPACK_USE_STD_VARIANT_ADAPTOR)

BOOST_AUTO_TEST_CASE(variant_pack_unpack_as) {
std::stringstream ss;
std::variant<bool, int, float, double> val1{1.0};
msgpack::pack(ss, val1);
std::string const& str = ss.str();
msgpack::object_handle oh =
msgpack::unpack(str.data(), str.size());
std::variant<bool, int, float, double> val2 =
oh.get().as<std::variant<bool, int, float, double> >();
BOOST_CHECK(val1 == val2);
BOOST_CHECK_THROW((oh.get().as<std::variant<bool>>()), msgpack::type_error);
}

BOOST_AUTO_TEST_CASE(variant_with_zone) {
msgpack::zone z;
std::variant<bool, int, float, double> val1{1.0};
msgpack::object obj(val1, z);
std::variant<bool, int, float, double> val2 = obj.as<std::variant<bool, int, float, double>>();
BOOST_CHECK(val1 == val2);
BOOST_CHECK_THROW((obj.as<std::variant<bool>>()), msgpack::type_error);
}

#endif // defined(MSGPACK_USE_STD_VARIANT_ADAPTOR)

#endif // MSGPACK_CPP_VERSION >= 201703