diff --git a/src/json.hpp b/src/json.hpp index 3c338eb8..e3dd33c8 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -54,6 +54,7 @@ SOFTWARE. #include <string> // getline, stoi, string, to_string #include <type_traits> // add_pointer, conditional, decay, enable_if, false_type, integral_constant, is_arithmetic, is_base_of, is_const, is_constructible, is_convertible, is_default_constructible, is_enum, is_floating_point, is_integral, is_nothrow_move_assignable, is_nothrow_move_constructible, is_pointer, is_reference, is_same, is_scalar, is_signed, remove_const, remove_cv, remove_pointer, remove_reference, true_type, underlying_type #include <utility> // declval, forward, make_pair, move, pair, swap +#include <valarray> // valarray #include <vector> // vector // exclude unsupported compilers @@ -661,6 +662,18 @@ struct external_constructor<value_t::array> } j.assert_invariant(); } + + template<typename BasicJsonType, typename T, + enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0> + static void construct(BasicJsonType& j, const std::valarray<T>& arr) + { + using std::begin; + using std::end; + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr)); + j.assert_invariant(); + } }; template<> @@ -921,11 +934,18 @@ template < is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value or std::is_same<typename BasicJsonType::array_t, CompatibleArrayType>::value, int > = 0 > -void to_json(BasicJsonType& j, const CompatibleArrayType& arr) +void to_json(BasicJsonType& j, const CompatibleArrayType& arr) { external_constructor<value_t::array>::construct(j, arr); } +template <typename BasicJsonType, typename T, + enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0> +void to_json(BasicJsonType& j, std::valarray<T> arr) +{ + external_constructor<value_t::array>::construct(j, std::move(arr)); +} + template <typename BasicJsonType> void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) { @@ -936,7 +956,7 @@ template < typename BasicJsonType, typename CompatibleObjectType, enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value, int> = 0 > -void to_json(BasicJsonType& j, const CompatibleObjectType& obj) +void to_json(BasicJsonType& j, const CompatibleObjectType& obj) { external_constructor<value_t::object>::construct(j, obj); } @@ -1082,6 +1102,22 @@ void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l) }); } +// valarray doesn't have an insert method +template<typename BasicJsonType, typename T, + enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0> +void from_json(const BasicJsonType& j, std::valarray<T>& l) +{ + if (JSON_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + l.resize(j.size()); + for (size_t i = 0; i < j.size(); ++i) + { + l[i] = j[i]; + } +} + template<typename BasicJsonType, typename CompatibleArrayType> void from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<0> /*unused*/) { @@ -4441,7 +4477,7 @@ class binary_reader */ BasicJsonType parse_msgpack(const bool strict) { - const auto res = parse_msgpack_internal(); + const auto res = parse_msgpack_internal(); if (strict) { get(); @@ -13187,7 +13223,7 @@ class basic_json */ template<typename A1, typename A2, detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0> - static basic_json from_cbor(A1&& a1, A2&& a2, const bool strict = true) + static basic_json from_cbor(A1 && a1, A2 && a2, const bool strict = true) { return binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).parse_cbor(strict); } @@ -13274,7 +13310,7 @@ class basic_json */ template<typename A1, typename A2, detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0> - static basic_json from_msgpack(A1&& a1, A2&& a2, const bool strict = true) + static basic_json from_msgpack(A1 && a1, A2 && a2, const bool strict = true) { return binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).parse_msgpack(strict); } diff --git a/test/src/unit-constructor1.cpp b/test/src/unit-constructor1.cpp index 5130e505..da10ed2e 100644 --- a/test/src/unit-constructor1.cpp +++ b/test/src/unit-constructor1.cpp @@ -38,6 +38,7 @@ using nlohmann::json; #include <list> #include <unordered_map> #include <unordered_set> +#include <valarray> TEST_CASE("constructors") { @@ -311,6 +312,36 @@ TEST_CASE("constructors") CHECK(a2 == a); } + SECTION("std::valarray<int>") + { + std::valarray<int> va = {1, 2, 3, 4, 5}; + json j(va); + CHECK(j.type() == json::value_t::array); + CHECK(j == json({1, 2, 3, 4, 5})); + + std::valarray<int> jva = j; + CHECK(jva.size() == va.size()); + for (size_t i = 0; i < jva.size(); ++i) + { + CHECK(va[i] == jva[i]); + } + } + + SECTION("std::valarray<double>") + { + std::valarray<double> va = {1.2, 2.3, 3.4, 4.5, 5.6}; + json j(va); + CHECK(j.type() == json::value_t::array); + CHECK(j == json({1.2, 2.3, 3.4, 4.5, 5.6})); + + std::valarray<double> jva = j; + CHECK(jva.size() == va.size()); + for (size_t i = 0; i < jva.size(); ++i) + { + CHECK(va[i] == jva[i]); + } + } + SECTION("std::vector<json>") { std::vector<json> a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp index 59c8e53f..6a42bfb8 100644 --- a/test/src/unit-conversions.cpp +++ b/test/src/unit-conversions.cpp @@ -37,6 +37,7 @@ using nlohmann::json; #include <list> #include <unordered_map> #include <unordered_set> +#include <valarray> TEST_CASE("value conversion") { @@ -1008,6 +1009,15 @@ TEST_CASE("value conversion") auto m5 = j5.get<std::forward_list<std::string>>(); } + SECTION("std::valarray") + { + auto m1 = j1.get<std::valarray<int>>(); + auto m2 = j2.get<std::valarray<unsigned int>>(); + auto m3 = j3.get<std::valarray<double>>(); + auto m4 = j4.get<std::valarray<bool>>(); + auto m5 = j5.get<std::valarray<std::string>>(); + } + SECTION("std::vector") { auto m1 = j1.get<std::vector<int>>(); @@ -1050,6 +1060,7 @@ TEST_CASE("value conversion") CHECK_THROWS_AS((json().get<std::vector<int>>()), json::type_error&); CHECK_THROWS_AS((json().get<std::vector<json>>()), json::type_error&); CHECK_THROWS_AS((json().get<std::list<json>>()), json::type_error&); + CHECK_THROWS_AS((json().get<std::valarray<int>>()), json::type_error&); // does type really must be an array? or it rather must not be null? // that's what I thought when other test like this one broke @@ -1057,6 +1068,7 @@ TEST_CASE("value conversion") CHECK_THROWS_WITH((json().get<std::vector<int>>()), "[json.exception.type_error.302] type must be array, but is null"); CHECK_THROWS_WITH((json().get<std::vector<json>>()), "[json.exception.type_error.302] type must be array, but is null"); CHECK_THROWS_WITH((json().get<std::list<json>>()), "[json.exception.type_error.302] type must be array, but is null"); + CHECK_THROWS_WITH((json().get<std::valarray<int>>()), "[json.exception.type_error.302] type must be array, but is null"); } } } diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index 58deb331..3aeee814 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -1204,4 +1204,33 @@ TEST_CASE("regression tests") std::string i = "\xef\xbb\xbf{\n \"foo\": true\n}"; CHECK_NOTHROW(json::parse(i.begin(), i.end())); } + + SECTION("issue #702 - conversion from valarray<double> to json fails to build") + { + SECTION("original example") + { + std::valarray<double> v; + nlohmann::json j; + j["test"] = v; + } + + SECTION("full example") + { + std::valarray<double> v = {1.2, 2.3, 3.4, 4.5}; + json j = v; + std::valarray<double> vj = j; + + CHECK(j == json(vj)); + CHECK(v.size() == vj.size()); + for (size_t i = 0; i < v.size(); ++i) + { + CHECK(v[i] == vj[i]); + CHECK(v[i] == j[i]); + } + + CHECK_THROWS_AS(json().get<std::valarray<double>>(), json::type_error&); + CHECK_THROWS_WITH(json().get<std::valarray<double>>(), + "[json.exception.type_error.302] type must be array, but is null"); + } + } }