From e820d4235b559333b243d9023dcca7807a8e45d7 Mon Sep 17 00:00:00 2001 From: nekosu <liao.hy@outlook.com> Date: Sun, 16 Feb 2025 16:44:55 +0800 Subject: [PATCH] feat: variant --- .gitignore | 3 ++ include/common/utils.hpp | 60 +++++++++++++++++++++++++++++++++++++++ include/common/value.hpp | 39 ++++++++++++++++++++----- test/serializing_test.cpp | 7 ++++- 4 files changed, 101 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index b6e98b0..87b77ae 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,6 @@ package.json package-lock.json yarn.lock pnpm-lock.yaml + +# Clangd +.cache diff --git a/include/common/utils.hpp b/include/common/utils.hpp index e7219b1..8c656e5 100644 --- a/include/common/utils.hpp +++ b/include/common/utils.hpp @@ -7,6 +7,8 @@ #include <sstream> #include <string> #include <type_traits> +#include <utility> +#include <variant> namespace json { @@ -84,6 +86,11 @@ constexpr bool is_collection = false; template <typename T> constexpr bool is_collection<T> = is_container<T> && !is_map<T> && !is_fixed_array<T>; +template <typename T> +constexpr bool is_variant = false; +template <typename... args_t> +constexpr bool is_variant<std::variant<args_t...>> = true; + template <typename T> class has_to_json_in_member { @@ -261,4 +268,57 @@ inline string_t to_basic_string(any_t&& arg) static_assert(!sizeof(any_t), "Unsupported type"); } } + +template <std::size_t id, typename string_t, typename variant_t> +bool serialize_variant_impl(basic_value<string_t>& val, variant_t&& var) +{ + if (var.index() == id) { + val = basic_value<string_t>(std::get<id>(std::forward<variant_t>(var))); + return false; + } + return true; +} + +template <typename string_t, typename variant_t, std::size_t... ids> +basic_value<string_t> serialize_variant(variant_t&& var, std::index_sequence<ids...>) +{ + basic_value<string_t> val; + (serialize_variant_impl<ids>(val, std::forward<variant_t>(var)) && ...); + return val; +} + +template <std::size_t id, typename string_t, typename variant_t> +bool deserialize_variant_impl(const basic_value<string_t>& val, variant_t& var) +{ + using alt_t = std::variant_alternative_t<id, variant_t>; + if (val.template is<alt_t>()) { + var = val.template as<alt_t>(); + return false; + } + return true; +} + +template <typename string_t, typename variant_t, std::size_t... ids> +variant_t deserialize_variant(const basic_value<string_t>& val, std::index_sequence<ids...>) +{ + variant_t var; + (deserialize_variant_impl<ids>(val, var) && ...); + return var; +} + +template <typename string_t, typename alt_t> +bool detect_variant_impl(const basic_value<string_t>& val) +{ + if (val.template is<alt_t>()) { + return true; + } + return false; +} + +template <typename string_t, typename variant_t, std::size_t... ids> +bool detect_variant(const basic_value<string_t>& val, std::index_sequence<ids...>) +{ + return (detect_variant_impl<string_t, std::variant_alternative_t<ids, variant_t>>(val) && ...); +} + } // namespace json::_utils diff --git a/include/common/value.hpp b/include/common/value.hpp index 439c5a7..99892ef 100644 --- a/include/common/value.hpp +++ b/include/common/value.hpp @@ -9,6 +9,8 @@ #include <ostream> #include <string> #include <tuple> +#include <type_traits> +#include <utility> #include <variant> #include "exception.hpp" @@ -134,6 +136,16 @@ class basic_value { } + template < + typename variant_t, + std::enable_if_t<_utils::is_variant<std::remove_reference_t<variant_t>>, bool> = true> + basic_value(variant_t&& var) + : basic_value(_utils::serialize_variant<string_t>( + std::forward<variant_t>(var), + std::make_index_sequence<std::variant_size_v<std::remove_reference_t<variant_t>>>())) + { + } + template < typename value_t, std::enable_if_t<!std::is_convertible_v<value_t, basic_value<string_t>>, bool> = true> @@ -366,13 +378,21 @@ class basic_value { return as_array().template as_tuple<elem_ts...>(); } - + template <typename elem1_t, typename elem2_t> explicit operator std::pair<elem1_t, elem2_t>() const { return as_array().template as_pair<elem1_t, elem2_t>(); } + template <typename... args_t> + explicit operator std::variant<args_t...>() const + { + return _utils::deserialize_variant<string_t, std::variant<args_t...>>( + *this, + std::make_index_sequence<std::variant_size_v<std::variant<args_t...>>>()); + } + private: friend class basic_array<string_t>; friend class basic_object<string_t>; @@ -566,6 +586,11 @@ inline bool basic_value<string_t>::is() const noexcept return is_object() && std::is_constructible_v<string_t, typename value_t::key_type> && all<typename value_t::mapped_type>(); } + else if constexpr (_utils::is_variant<value_t>) { + return _utils::detect_variant<string_t, value_t>( + *this, + std::make_index_sequence<std::variant_size_v<value_t>>()); + } else { static_assert(!sizeof(value_t), "Unsupported type"); } @@ -637,16 +662,16 @@ inline auto basic_value<string_t>::get_helper( { if constexpr (std::is_constructible_v<string_t, first_key_t>) { return is_object() ? as_object().get_helper( - default_value, - std::forward<first_key_t>(first), - std::forward<rest_keys_t>(rest)...) + default_value, + std::forward<first_key_t>(first), + std::forward<rest_keys_t>(rest)...) : default_value; } else if constexpr (std::is_integral_v<std::decay_t<first_key_t>>) { return is_array() ? as_array().get_helper( - default_value, - std::forward<first_key_t>(first), - std::forward<rest_keys_t>(rest)...) + default_value, + std::forward<first_key_t>(first), + std::forward<rest_keys_t>(rest)...) : default_value; } else { diff --git a/test/serializing_test.cpp b/test/serializing_test.cpp index 72492d4..53429c0 100644 --- a/test/serializing_test.cpp +++ b/test/serializing_test.cpp @@ -13,6 +13,11 @@ bool serializing() { + std::variant<int, std::vector<int>> var = 1; + json::value k = var; + auto var2 = k.as<std::variant<int, std::vector<int>>>(); + auto is_var = k.is<std::variant<int, std::vector<int>>>(); + json::value root; root["hello"] = "meojson"; @@ -286,7 +291,7 @@ bool jsonizing() mine.w = MyStruct::W::C; json::value j_mine = mine; - std::cout << j_mine<< std::endl; + std::cout << j_mine << std::endl; MyStruct new_mine = (MyStruct)j_mine;